parent
39fab67523
commit
9ce62c735a
|
@ -87,6 +87,14 @@ use super::*;
|
||||||
}
|
}
|
||||||
let override_ident: syn::Ident = format_ident!("{}Override", s.ident);
|
let override_ident: syn::Ident = format_ident!("{}Override", s.ident);
|
||||||
let mut field_tokentrees = vec![];
|
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![];
|
let mut field_idents = vec![];
|
||||||
for f in &s.fields {
|
for f in &s.fields {
|
||||||
let ident = &f.ident;
|
let ident = &f.ident;
|
||||||
|
@ -146,6 +154,7 @@ use super::*;
|
||||||
//let fields = &s.fields;
|
//let fields = &s.fields;
|
||||||
|
|
||||||
let literal_struct = quote! {
|
let literal_struct = quote! {
|
||||||
|
#(#attrs_tokens)*
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct #override_ident {
|
pub struct #override_ident {
|
||||||
|
@ -153,6 +162,7 @@ use super::*;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#(#attrs_tokens)*
|
||||||
impl Default for #override_ident {
|
impl Default for #override_ident {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
#override_ident {
|
#override_ident {
|
||||||
|
|
|
@ -892,11 +892,6 @@ Always sign sent messages
|
||||||
Key to be used when signing/encrypting (not functional yet)
|
Key to be used when signing/encrypting (not functional yet)
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em none
|
.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
|
.El
|
||||||
.Sh TERMINAL
|
.Sh TERMINAL
|
||||||
.Bl -tag -width 36n
|
.Bl -tag -width 36n
|
||||||
|
|
|
@ -128,6 +128,7 @@ pub enum ToggleFlag {
|
||||||
InternalVal(bool),
|
InternalVal(bool),
|
||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
|
Ask,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<bool> for ToggleFlag {
|
impl From<bool> for ToggleFlag {
|
||||||
|
@ -157,9 +158,15 @@ impl ToggleFlag {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_ask(&self) -> bool {
|
||||||
|
*self == ToggleFlag::Ask
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_false(&self) -> bool {
|
pub fn is_false(&self) -> bool {
|
||||||
ToggleFlag::False == *self || ToggleFlag::InternalVal(false) == *self
|
ToggleFlag::False == *self || ToggleFlag::InternalVal(false) == *self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_true(&self) -> bool {
|
pub fn is_true(&self) -> bool {
|
||||||
ToggleFlag::True == *self || ToggleFlag::InternalVal(true) == *self
|
ToggleFlag::True == *self || ToggleFlag::InternalVal(true) == *self
|
||||||
}
|
}
|
||||||
|
@ -174,6 +181,7 @@ impl Serialize for ToggleFlag {
|
||||||
ToggleFlag::Unset | ToggleFlag::InternalVal(_) => serializer.serialize_none(),
|
ToggleFlag::Unset | ToggleFlag::InternalVal(_) => serializer.serialize_none(),
|
||||||
ToggleFlag::False => serializer.serialize_bool(false),
|
ToggleFlag::False => serializer.serialize_bool(false),
|
||||||
ToggleFlag::True => serializer.serialize_bool(true),
|
ToggleFlag::True => serializer.serialize_bool(true),
|
||||||
|
ToggleFlag::Ask => serializer.serialize_str("ask"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,10 +191,17 @@ impl<'de> Deserialize<'de> for ToggleFlag {
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let s = <bool>::deserialize(deserializer);
|
let s = <String>::deserialize(deserializer);
|
||||||
Ok(match s? {
|
Ok(match s? {
|
||||||
true => ToggleFlag::True,
|
s if s.eq_ignore_ascii_case("true") => ToggleFlag::True,
|
||||||
false => ToggleFlag::False,
|
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
|
||||||
|
)))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7220,12 +7220,8 @@ extern "C" {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn gpgme_get_protocol_name(proto: gpgme_protocol_t) -> *const ::std::os::raw::c_char;
|
pub fn gpgme_get_protocol_name(proto: gpgme_protocol_t) -> *const ::std::os::raw::c_char;
|
||||||
}
|
}
|
||||||
extern "C" {
|
pub type gpgme_set_armor = unsafe extern "C" fn(ctx: gpgme_ctx_t, yes: ::std::os::raw::c_int);
|
||||||
pub fn gpgme_set_armor(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_get_armor(ctx: gpgme_ctx_t) -> ::std::os::raw::c_int;
|
|
||||||
}
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn gpgme_set_textmode(ctx: gpgme_ctx_t, yes: ::std::os::raw::c_int);
|
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,
|
home_dir: *const ::std::os::raw::c_char,
|
||||||
) -> gpgme_error_t;
|
) -> gpgme_error_t;
|
||||||
}
|
}
|
||||||
extern "C" {
|
pub type gpgme_signers_clear = unsafe extern "C" fn(ctx: gpgme_ctx_t);
|
||||||
pub fn gpgme_signers_clear(ctx: gpgme_ctx_t);
|
|
||||||
}
|
pub type gpgme_signers_add =
|
||||||
extern "C" {
|
unsafe extern "C" fn(ctx: gpgme_ctx_t, key: gpgme_key_t) -> gpgme_error_t;
|
||||||
pub fn gpgme_signers_add(ctx: gpgme_ctx_t, key: gpgme_key_t) -> gpgme_error_t;
|
|
||||||
}
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn gpgme_signers_count(ctx: gpgme_ctx_t) -> ::std::os::raw::c_uint;
|
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;
|
pub type gpgme_encrypt_result_t = *mut _gpgme_op_encrypt_result;
|
||||||
extern "C" {
|
pub type gpgme_op_encrypt_result = unsafe extern "C" fn(ctx: gpgme_ctx_t) -> gpgme_encrypt_result_t;
|
||||||
pub fn gpgme_op_encrypt_result(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_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_NO_ENCRYPT_TO: gpgme_encrypt_flags_t = 2;
|
||||||
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_PREPARE: gpgme_encrypt_flags_t = 4;
|
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_WRAP: gpgme_encrypt_flags_t = 128;
|
||||||
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_WANT_ADDRESS: gpgme_encrypt_flags_t = 256;
|
pub const gpgme_encrypt_flags_t_GPGME_ENCRYPT_WANT_ADDRESS: gpgme_encrypt_flags_t = 256;
|
||||||
pub type gpgme_encrypt_flags_t = u32;
|
pub type gpgme_encrypt_flags_t = u32;
|
||||||
extern "C" {
|
pub type gpgme_op_encrypt_start = unsafe extern "C" fn(
|
||||||
pub fn gpgme_op_encrypt_start(
|
ctx: gpgme_ctx_t,
|
||||||
ctx: gpgme_ctx_t,
|
recp: *mut gpgme_key_t,
|
||||||
recp: *mut gpgme_key_t,
|
flags: gpgme_encrypt_flags_t,
|
||||||
flags: gpgme_encrypt_flags_t,
|
plain: gpgme_data_t,
|
||||||
plain: gpgme_data_t,
|
cipher: gpgme_data_t,
|
||||||
cipher: gpgme_data_t,
|
) -> gpgme_error_t;
|
||||||
) -> gpgme_error_t;
|
|
||||||
}
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn gpgme_op_encrypt(
|
pub fn gpgme_op_encrypt(
|
||||||
ctx: gpgme_ctx_t,
|
ctx: gpgme_ctx_t,
|
||||||
|
@ -7814,15 +7804,13 @@ extern "C" {
|
||||||
cipher: gpgme_data_t,
|
cipher: gpgme_data_t,
|
||||||
) -> gpgme_error_t;
|
) -> gpgme_error_t;
|
||||||
}
|
}
|
||||||
extern "C" {
|
pub type gpgme_op_encrypt_sign_start = unsafe extern "C" fn(
|
||||||
pub fn gpgme_op_encrypt_sign_start(
|
ctx: gpgme_ctx_t,
|
||||||
ctx: gpgme_ctx_t,
|
recp: *mut gpgme_key_t,
|
||||||
recp: *mut gpgme_key_t,
|
flags: gpgme_encrypt_flags_t,
|
||||||
flags: gpgme_encrypt_flags_t,
|
plain: gpgme_data_t,
|
||||||
plain: gpgme_data_t,
|
cipher: gpgme_data_t,
|
||||||
cipher: gpgme_data_t,
|
) -> gpgme_error_t;
|
||||||
) -> gpgme_error_t;
|
|
||||||
}
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
pub fn gpgme_op_encrypt_sign(
|
pub fn gpgme_op_encrypt_sign(
|
||||||
ctx: gpgme_ctx_t,
|
ctx: gpgme_ctx_t,
|
||||||
|
|
|
@ -25,11 +25,16 @@ use crate::email::{
|
||||||
};
|
};
|
||||||
use crate::error::{ErrorKind, IntoMeliError, MeliError, Result, ResultIntoMeliError};
|
use crate::error::{ErrorKind, IntoMeliError, MeliError, Result, ResultIntoMeliError};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
|
use serde::{
|
||||||
|
de::{self, Deserialize},
|
||||||
|
Deserializer, Serialize, Serializer,
|
||||||
|
};
|
||||||
use smol::Async;
|
use smol::Async;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ffi::{CStr, CString, OsStr};
|
use std::ffi::{CStr, CString, OsStr};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::io::Seek;
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -66,6 +71,7 @@ pub enum GpgmeFlag {
|
||||||
///"auto-key-retrieve"
|
///"auto-key-retrieve"
|
||||||
AutoKeyRetrieve,
|
AutoKeyRetrieve,
|
||||||
OfflineMode,
|
OfflineMode,
|
||||||
|
AsciiArmor,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
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 {
|
struct IoState {
|
||||||
max_idx: usize,
|
max_idx: usize,
|
||||||
ops: HashMap<usize, GpgmeFd>,
|
ops: HashMap<usize, GpgmeFd>,
|
||||||
|
@ -187,6 +272,7 @@ impl Context {
|
||||||
};
|
};
|
||||||
ret.set_flag(GpgmeFlag::AutoKeyRetrieve, false)?;
|
ret.set_flag(GpgmeFlag::AutoKeyRetrieve, false)?;
|
||||||
ret.set_flag(GpgmeFlag::OfflineMode, true)?;
|
ret.set_flag(GpgmeFlag::OfflineMode, true)?;
|
||||||
|
ret.set_flag(GpgmeFlag::AsciiArmor, true)?;
|
||||||
ret.set_auto_key_locate(LocateKey::LOCAL)?;
|
ret.set_auto_key_locate(LocateKey::LOCAL)?;
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
@ -221,12 +307,21 @@ impl Context {
|
||||||
};
|
};
|
||||||
return Ok(());
|
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_ON: &[u8; 2] = b"1\0";
|
||||||
const VALUE_OFF: &[u8; 2] = b"0\0";
|
const VALUE_OFF: &[u8; 2] = b"0\0";
|
||||||
let raw_flag = match flag {
|
let raw_flag = match flag {
|
||||||
GpgmeFlag::AutoKeyRetrieve => c_string_literal!("auto-key-retrieve"),
|
GpgmeFlag::AutoKeyRetrieve => c_string_literal!("auto-key-retrieve"),
|
||||||
GpgmeFlag::OfflineMode => unreachable!(),
|
GpgmeFlag::AsciiArmor | GpgmeFlag::OfflineMode => unreachable!(),
|
||||||
};
|
};
|
||||||
self.set_flag_inner(
|
self.set_flag_inner(
|
||||||
raw_flag,
|
raw_flag,
|
||||||
|
@ -249,6 +344,11 @@ impl Context {
|
||||||
call!(&self.inner.lib, gpgme_get_offline)(self.inner.inner.as_ptr()) > 0
|
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);
|
let val = self.get_flag_inner(raw_flag);
|
||||||
Ok(!val.is_null())
|
Ok(!val.is_null())
|
||||||
|
@ -259,23 +359,7 @@ impl Context {
|
||||||
if val == LocateKey::NODEFAULT {
|
if val == LocateKey::NODEFAULT {
|
||||||
self.set_flag_inner(auto_key_locate, c_string_literal!("clear,nodefault"))
|
self.set_flag_inner(auto_key_locate, c_string_literal!("clear,nodefault"))
|
||||||
} else {
|
} else {
|
||||||
let mut accum = String::new();
|
let mut accum = val.to_string();
|
||||||
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();
|
|
||||||
accum.push('\0');
|
accum.push('\0');
|
||||||
self.set_flag_inner(
|
self.set_flag_inner(
|
||||||
auto_key_locate,
|
auto_key_locate,
|
||||||
|
@ -592,11 +676,34 @@ impl Context {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign<'d>(
|
pub fn sign(
|
||||||
&mut self,
|
&mut self,
|
||||||
text: &'d mut Data,
|
sign_keys: Vec<Key>,
|
||||||
) -> Result<impl Future<Output = Result<()>> + 'd> {
|
mut text: Data,
|
||||||
let sig = std::ptr::null_mut();
|
) -> 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 {
|
unsafe {
|
||||||
gpgme_error_try(
|
gpgme_error_try(
|
||||||
&self.inner.lib,
|
&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 = self.io_state.clone();
|
||||||
let io_state_lck = self.io_state.lock().unwrap();
|
let io_state_lck = self.io_state.lock().unwrap();
|
||||||
|
@ -672,13 +786,17 @@ impl Context {
|
||||||
};
|
};
|
||||||
let _ = rcv.recv().await;
|
let _ = rcv.recv().await;
|
||||||
let io_state_lck = io_state.lock().unwrap();
|
let io_state_lck = io_state.lock().unwrap();
|
||||||
let ret = io_state_lck
|
io_state_lck
|
||||||
.done
|
.done
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.take()
|
.take()
|
||||||
.unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")));
|
.unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")))?;
|
||||||
ret
|
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;
|
recipient_iter = (*recipient_iter).next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
use std::io::Seek;
|
|
||||||
/* Rewind cursor */
|
/* Rewind cursor */
|
||||||
plain
|
plain
|
||||||
.seek(std::io::SeekFrom::Start(0))
|
.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<()> {
|
fn gpgme_error_try(lib: &libloading::Library, error_code: GpgmeError) -> Result<()> {
|
||||||
|
|
|
@ -112,7 +112,6 @@
|
||||||
#[pgp]
|
#[pgp]
|
||||||
#auto_sign = false # always sign sent messages
|
#auto_sign = false # always sign sent messages
|
||||||
#auto_verify_signatures = true # always verify signatures when reading signed e-mails
|
#auto_verify_signatures = true # always verify signatures when reading signed e-mails
|
||||||
#gpg_binary = "/usr/bin/gpg2" #optional
|
|
||||||
#
|
#
|
||||||
#[terminal]
|
#[terminal]
|
||||||
#theme = "dark" # or "light"
|
#theme = "dark" # or "light"
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub use crate::view::*;
|
||||||
mod compose;
|
mod compose;
|
||||||
pub use self::compose::*;
|
pub use self::compose::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
pub mod pgp;
|
pub mod pgp;
|
||||||
|
|
||||||
mod status;
|
mod status;
|
||||||
|
|
|
@ -84,8 +84,8 @@ pub struct Composer {
|
||||||
|
|
||||||
embed_area: Area,
|
embed_area: Area,
|
||||||
embed: Option<EmbedStatus>,
|
embed: Option<EmbedStatus>,
|
||||||
sign_mail: ToggleFlag,
|
#[cfg(feature = "gpgme")]
|
||||||
encrypt_mail: ToggleFlag,
|
gpg_state: gpg::GpgComposeState,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
has_changes: bool,
|
has_changes: bool,
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
|
@ -107,8 +107,8 @@ impl Default for Composer {
|
||||||
form: FormWidget::default(),
|
form: FormWidget::default(),
|
||||||
|
|
||||||
mode: ViewMode::Edit,
|
mode: ViewMode::Edit,
|
||||||
sign_mail: ToggleFlag::Unset,
|
#[cfg(feature = "gpgme")]
|
||||||
encrypt_mail: ToggleFlag::Unset,
|
gpg_state: gpg::GpgComposeState::new(),
|
||||||
dirty: true,
|
dirty: true,
|
||||||
has_changes: false,
|
has_changes: false,
|
||||||
embed_area: ((0, 0), (0, 0)),
|
embed_area: ((0, 0), (0, 0)),
|
||||||
|
@ -125,6 +125,8 @@ enum ViewMode {
|
||||||
Edit,
|
Edit,
|
||||||
Embed,
|
Embed,
|
||||||
SelectRecipients(UIDialog<Address>),
|
SelectRecipients(UIDialog<Address>),
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
|
SelectEncryptKey(bool, gpg::KeySelection),
|
||||||
Send(UIConfirmationDialog),
|
Send(UIConfirmationDialog),
|
||||||
WaitingForSendResult(UIDialog<char>, JoinHandle<Result<()>>),
|
WaitingForSendResult(UIDialog<char>, JoinHandle<Result<()>>),
|
||||||
}
|
}
|
||||||
|
@ -430,14 +432,23 @@ impl Composer {
|
||||||
let attachments_no = self.draft.attachments().len();
|
let attachments_no = self.draft.attachments().len();
|
||||||
let theme_default = crate::conf::value(context, "theme_default");
|
let theme_default = crate::conf::value(context, "theme_default");
|
||||||
clear_area(grid, area, 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(
|
write_string_to_grid(
|
||||||
&format!(
|
&format!(
|
||||||
"☑ sign with {}",
|
"☑ sign with {}",
|
||||||
account_settings!(context[self.account_hash].pgp.sign_key)
|
if self.gpg_state.sign_keys.is_empty() {
|
||||||
.as_ref()
|
"default key"
|
||||||
.map(|s| s.as_str())
|
} else {
|
||||||
.unwrap_or("default key")
|
key_list.as_str()
|
||||||
|
}
|
||||||
),
|
),
|
||||||
grid,
|
grid,
|
||||||
theme_default.fg,
|
theme_default.fg,
|
||||||
|
@ -465,14 +476,29 @@ impl Composer {
|
||||||
None,
|
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(
|
write_string_to_grid(
|
||||||
&format!(
|
&format!(
|
||||||
"☑ encrypt with {}",
|
"{}{}",
|
||||||
account_settings!(context[self.account_hash].pgp.encrypt_key)
|
if self.gpg_state.encrypt_keys.is_empty() {
|
||||||
.as_ref()
|
"☐ no keys to encrypt with!"
|
||||||
.map(|s| s.as_str())
|
} else {
|
||||||
.unwrap_or("default key")
|
"☑ encrypt with "
|
||||||
|
},
|
||||||
|
if self.gpg_state.encrypt_keys.is_empty() {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
key_list.as_str()
|
||||||
|
}
|
||||||
),
|
),
|
||||||
grid,
|
grid,
|
||||||
theme_default.fg,
|
theme_default.fg,
|
||||||
|
@ -575,8 +601,9 @@ impl Component for Composer {
|
||||||
let width = width!(area);
|
let width = width!(area);
|
||||||
|
|
||||||
if !self.initialized {
|
if !self.initialized {
|
||||||
if self.sign_mail.is_unset() {
|
#[cfg(feature = "gpgme")]
|
||||||
self.sign_mail = ToggleFlag::InternalVal(*account_settings!(
|
if self.gpg_state.sign_mail.is_unset() {
|
||||||
|
self.gpg_state.sign_mail = ToggleFlag::InternalVal(*account_settings!(
|
||||||
context[self.account_hash].pgp.auto_sign
|
context[self.account_hash].pgp.auto_sign
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -752,6 +779,18 @@ impl Component for Composer {
|
||||||
ViewMode::Send(ref mut s) => {
|
ViewMode::Send(ref mut s) => {
|
||||||
s.draw(grid, center_area(area, s.content.size()), context);
|
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) => {
|
ViewMode::SelectRecipients(ref mut s) => {
|
||||||
s.draw(grid, center_area(area, s.content.size()), context);
|
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>() {
|
if let Some(true) = result.downcast_ref::<bool>() {
|
||||||
self.update_draft();
|
self.update_draft();
|
||||||
match send_draft_async(
|
match send_draft_async(
|
||||||
self.sign_mail,
|
#[cfg(feature = "gpgme")]
|
||||||
self.encrypt_mail,
|
self.gpg_state.clone(),
|
||||||
context,
|
context,
|
||||||
self.account_hash,
|
self.account_hash,
|
||||||
self.draft.clone(),
|
self.draft.clone(),
|
||||||
|
@ -846,6 +885,14 @@ impl Component for Composer {
|
||||||
self.mode = ViewMode::Edit;
|
self.mode = ViewMode::Edit;
|
||||||
self.set_dirty(true);
|
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), _) => {
|
(ViewMode::Send(ref mut selector), _) => {
|
||||||
if selector.process_event(event, context) {
|
if selector.process_event(event, context) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -954,6 +1001,34 @@ impl Component for Composer {
|
||||||
return true;
|
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
|
if self.cursor == Cursor::Headers
|
||||||
|
@ -1025,14 +1100,15 @@ impl Component for Composer {
|
||||||
if self.mode.is_edit()
|
if self.mode.is_edit()
|
||||||
&& (self.cursor == Cursor::Sign || self.cursor == Cursor::Encrypt) =>
|
&& (self.cursor == Cursor::Sign || self.cursor == Cursor::Encrypt) =>
|
||||||
{
|
{
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
match self.cursor {
|
match self.cursor {
|
||||||
Cursor::Sign => {
|
Cursor::Sign => {
|
||||||
let is_true = self.sign_mail.is_true();
|
let is_true = self.gpg_state.sign_mail.is_true();
|
||||||
self.sign_mail = ToggleFlag::from(!is_true);
|
self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
|
||||||
}
|
}
|
||||||
Cursor::Encrypt => {
|
Cursor::Encrypt => {
|
||||||
let is_true = self.encrypt_mail.is_true();
|
let is_true = self.gpg_state.encrypt_mail.is_true();
|
||||||
self.encrypt_mail = ToggleFlag::from(!is_true);
|
self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
};
|
};
|
||||||
|
@ -1194,6 +1270,86 @@ impl Component for Composer {
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
return true;
|
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)
|
UIEvent::Input(ref key)
|
||||||
if self.embed.is_some()
|
if self.embed.is_some()
|
||||||
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
|
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
|
||||||
|
@ -1498,15 +1654,17 @@ impl Component for Composer {
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
Action::Compose(ComposeAction::ToggleSign) => {
|
Action::Compose(ComposeAction::ToggleSign) => {
|
||||||
let is_true = self.sign_mail.is_true();
|
let is_true = self.gpg_state.sign_mail.is_true();
|
||||||
self.sign_mail = ToggleFlag::from(!is_true);
|
self.gpg_state.sign_mail = ToggleFlag::from(!is_true);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
Action::Compose(ComposeAction::ToggleEncrypt) => {
|
Action::Compose(ComposeAction::ToggleEncrypt) => {
|
||||||
let is_true = self.encrypt_mail.is_true();
|
let is_true = self.gpg_state.encrypt_mail.is_true();
|
||||||
self.encrypt_mail = ToggleFlag::from(!is_true);
|
self.gpg_state.encrypt_mail = ToggleFlag::from(!is_true);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1527,6 +1685,10 @@ impl Component for Composer {
|
||||||
ViewMode::SelectRecipients(ref widget) => {
|
ViewMode::SelectRecipients(ref widget) => {
|
||||||
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
|
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) => {
|
ViewMode::Send(ref widget) => {
|
||||||
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
|
widget.is_dirty() || self.pager.is_dirty() || self.form.is_dirty()
|
||||||
}
|
}
|
||||||
|
@ -1626,7 +1788,7 @@ impl Component for Composer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_draft(
|
pub fn send_draft(
|
||||||
sign_mail: ToggleFlag,
|
_sign_mail: ToggleFlag,
|
||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
account_hash: AccountHash,
|
account_hash: AccountHash,
|
||||||
mut draft: Draft,
|
mut draft: Draft,
|
||||||
|
@ -1635,7 +1797,7 @@ pub fn send_draft(
|
||||||
complete_in_background: bool,
|
complete_in_background: bool,
|
||||||
) -> Result<Option<JoinHandle<Result<()>>>> {
|
) -> Result<Option<JoinHandle<Result<()>>>> {
|
||||||
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
|
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();
|
let mut content_type = ContentType::default();
|
||||||
if format_flowed {
|
if format_flowed {
|
||||||
if let ContentType::Text {
|
if let ContentType::Text {
|
||||||
|
@ -1667,41 +1829,44 @@ pub fn send_draft(
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
let output = crate::components::mail::pgp::sign(
|
let output = todo!();
|
||||||
body.into(),
|
crate::components::mail::pgp::sign(
|
||||||
account_settings!(context[account_hash].pgp.gpg_binary)
|
body.into(),
|
||||||
.as_ref()
|
account_settings!(context[account_hash].pgp.gpg_binary)
|
||||||
.map(|s| s.as_str()),
|
.as_ref()
|
||||||
account_settings!(context[account_hash].pgp.sign_key)
|
.map(|s| s.as_str()),
|
||||||
.as_ref()
|
account_settings!(context[account_hash].pgp.sign_key)
|
||||||
.map(|s| s.as_str()),
|
.as_ref()
|
||||||
);
|
.map(|s| s.as_str()),
|
||||||
match output {
|
);
|
||||||
Err(err) => {
|
match output {
|
||||||
debug!("{:?} could not sign draft msg", err);
|
Err(err) => {
|
||||||
log(
|
debug!("{:?} could not sign draft msg", err);
|
||||||
format!(
|
log(
|
||||||
"Could not sign draft in account `{}`: {}.",
|
format!(
|
||||||
context.accounts[&account_hash].name(),
|
"Could not sign draft in account `{}`: {}.",
|
||||||
err.to_string()
|
context.accounts[&account_hash].name(),
|
||||||
),
|
err.to_string()
|
||||||
ERROR,
|
),
|
||||||
);
|
ERROR,
|
||||||
context.replies.push_back(UIEvent::Notification(
|
);
|
||||||
Some(format!(
|
context.replies.push_back(UIEvent::Notification(
|
||||||
"Could not sign draft in account `{}`.",
|
Some(format!(
|
||||||
context.accounts[&account_hash].name()
|
"Could not sign draft in account `{}`.",
|
||||||
)),
|
context.accounts[&account_hash].name()
|
||||||
err.to_string(),
|
)),
|
||||||
Some(NotificationType::Error(err.kind)),
|
err.to_string(),
|
||||||
));
|
Some(NotificationType::Error(err.kind)),
|
||||||
return Err(err);
|
));
|
||||||
}
|
return Err(err);
|
||||||
Ok(output) => {
|
|
||||||
draft.attachments.push(output);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Ok(output) => {
|
||||||
|
draft.attachments.push(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
*/
|
||||||
|
{
|
||||||
let mut content_type = ContentType::default();
|
let mut content_type = ContentType::default();
|
||||||
if format_flowed {
|
if format_flowed {
|
||||||
if let ContentType::Text {
|
if let ContentType::Text {
|
||||||
|
@ -1762,8 +1927,7 @@ pub fn save_draft(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_draft_async(
|
pub fn send_draft_async(
|
||||||
sign_mail: ToggleFlag,
|
#[cfg(feature = "gpgme")] gpg_state: gpg::GpgComposeState,
|
||||||
encrypt_mail: ToggleFlag,
|
|
||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
account_hash: AccountHash,
|
account_hash: AccountHash,
|
||||||
mut draft: Draft,
|
mut draft: Draft,
|
||||||
|
@ -1772,6 +1936,7 @@ pub fn send_draft_async(
|
||||||
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
|
) -> Result<Pin<Box<dyn Future<Output = Result<()>> + Send>>> {
|
||||||
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
|
let format_flowed = *account_settings!(context[account_hash].composing.format_flowed);
|
||||||
let event_sender = context.sender.clone();
|
let event_sender = context.sender.clone();
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
let mut filters_stack: Vec<
|
let mut filters_stack: Vec<
|
||||||
Box<
|
Box<
|
||||||
dyn FnOnce(
|
dyn FnOnce(
|
||||||
|
@ -1781,40 +1946,19 @@ pub fn send_draft_async(
|
||||||
+ Send,
|
+ Send,
|
||||||
>,
|
>,
|
||||||
> = vec![];
|
> = 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(
|
filters_stack.push(Box::new(crate::components::mail::pgp::sign_filter(
|
||||||
account_settings!(context[account_hash].pgp.gpg_binary)
|
gpg_state.sign_keys.clone(),
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.to_string()),
|
|
||||||
account_settings!(context[account_hash].pgp.sign_key)
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.to_string()),
|
|
||||||
)?));
|
)?));
|
||||||
}
|
} else if gpg_state.encrypt_mail.is_true() {
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter(
|
filters_stack.push(Box::new(crate::components::mail::pgp::encrypt_filter(
|
||||||
account_settings!(context[account_hash].pgp.gpg_binary)
|
if gpg_state.sign_mail.is_true() {
|
||||||
.as_ref()
|
Some(gpg_state.sign_keys.clone())
|
||||||
.map(|s| s.to_string()),
|
} else {
|
||||||
account_settings!(context[account_hash].pgp.encrypt_key)
|
None
|
||||||
.as_ref()
|
},
|
||||||
.map(|s| s.to_string()),
|
gpg_state.encrypt_keys.clone(),
|
||||||
recipients,
|
|
||||||
)?));
|
)?));
|
||||||
}
|
}
|
||||||
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
|
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
|
||||||
|
@ -1850,6 +1994,7 @@ pub fn send_draft_async(
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
Ok(Box::pin(async move {
|
Ok(Box::pin(async move {
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
for f in filters_stack {
|
for f in filters_stack {
|
||||||
body = f(body).await?;
|
body = f(body).await?;
|
||||||
}
|
}
|
||||||
|
@ -1882,3 +2027,277 @@ pub fn send_draft_async(
|
||||||
ret
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,215 +19,49 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use melib::email::{
|
||||||
use melib::email::pgp as melib_pgp;
|
attachment_types::{ContentDisposition, ContentType, MultipartType},
|
||||||
|
pgp as melib_pgp, Attachment, AttachmentBuilder,
|
||||||
|
};
|
||||||
|
use melib::error::*;
|
||||||
|
use melib::gpgme::*;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::io::Write;
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::process::{Command, Stdio};
|
|
||||||
|
|
||||||
pub fn verify_signature(a: &Attachment, context: &mut Context) -> Result<Vec<u8>> {
|
pub async fn decrypt(raw: Vec<u8>) -> Result<(melib_pgp::DecryptionMetadata, Vec<u8>)> {
|
||||||
let (bytes, sig) =
|
let mut ctx = Context::new()?;
|
||||||
melib_pgp::verify_signature(a).chain_err_summary(|| "Could not verify signature.")?;
|
let cipher = ctx.new_data_mem(&raw)?;
|
||||||
let bytes_file = create_temp_file(&bytes, None, None, true);
|
ctx.decrypt(cipher)?.await
|
||||||
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"),
|
|
||||||
)
|
|
||||||
})?)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns multipart/signed
|
pub async fn verify(a: Attachment) -> Result<()> {
|
||||||
pub fn sign(
|
let (data, sig) =
|
||||||
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) =
|
|
||||||
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
|
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
|
||||||
let bytes_file = create_temp_file(&bytes, None, None, true);
|
let mut ctx = Context::new()?;
|
||||||
let signature_file = create_temp_file(sig, None, None, true);
|
let sig = ctx.new_data_mem(&sig)?;
|
||||||
Ok(
|
let data = ctx.new_data_mem(&data)?;
|
||||||
Command::new(gpg_binary.as_ref().map(String::as_str).unwrap_or("gpg2"))
|
ctx.verify(sig, data)?.await
|
||||||
.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"),
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign_filter(
|
pub fn sign_filter(
|
||||||
gpg_binary: Option<String>,
|
sign_keys: Vec<Key>,
|
||||||
pgp_key: Option<String>,
|
|
||||||
) -> Result<
|
) -> Result<
|
||||||
impl FnOnce(AttachmentBuilder) -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>> + Send>>
|
impl FnOnce(AttachmentBuilder) -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>> + Send>>
|
||||||
+ 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(
|
Ok(
|
||||||
move |a: AttachmentBuilder| -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>>+Send>> {
|
move |a: AttachmentBuilder| -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>>+Send>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let a: Attachment = a.into();
|
let a: Attachment = a.into();
|
||||||
|
let mut ctx = Context::new()?;
|
||||||
let sig_attachment = command
|
let data = ctx.new_data_mem(&melib_pgp::convert_attachment_to_rfc_spec(
|
||||||
.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(),
|
a.into_raw().as_bytes(),
|
||||||
))?;
|
))?;
|
||||||
let gpg = gpg.wait_with_output()?;
|
let sig_attachment = Attachment::new(
|
||||||
Ok(Attachment::new(
|
ContentType::PGPSignature,
|
||||||
ContentType::PGPSignature,
|
Default::default(),
|
||||||
Default::default(),
|
ctx.sign(sign_keys, data)?.await?,
|
||||||
gpg.stdout,
|
);
|
||||||
))
|
|
||||||
})
|
|
||||||
.chain_err_summary(|| {
|
|
||||||
format!("Failed to launch {} to verify PGP signature", binary)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let a: AttachmentBuilder = a.into();
|
let a: AttachmentBuilder = a.into();
|
||||||
let parts = vec![a, sig_attachment.into()];
|
let parts = vec![a, sig_attachment.into()];
|
||||||
let boundary = ContentType::make_boundary(&parts);
|
let boundary = ContentType::make_boundary(&parts);
|
||||||
|
@ -247,61 +81,31 @@ pub fn sign_filter(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encrypt_filter(
|
pub fn encrypt_filter(
|
||||||
gpg_binary: Option<String>,
|
sign_keys: Option<Vec<Key>>,
|
||||||
my_public_key: Option<String>,
|
encrypt_keys: Vec<Key>,
|
||||||
recipients: Vec<String>,
|
|
||||||
) -> Result<
|
) -> Result<
|
||||||
impl FnOnce(AttachmentBuilder) -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>> + Send>>
|
impl FnOnce(AttachmentBuilder) -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>> + Send>>
|
||||||
+ 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(
|
Ok(
|
||||||
move |a: AttachmentBuilder| -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>>+Send>> {
|
move |a: AttachmentBuilder| -> Pin<Box<dyn Future<Output = Result<AttachmentBuilder>>+Send>> {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let a: Attachment = a.into();
|
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
|
let sig_attachment = {
|
||||||
.stdin(Stdio::piped())
|
let mut a = Attachment::new(
|
||||||
.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(
|
|
||||||
ContentType::OctetStream { name: None },
|
ContentType::OctetStream { name: None },
|
||||||
Default::default(),
|
Default::default(),
|
||||||
gpg.stdout,
|
ctx.encrypt(sign_keys, encrypt_keys, data)?.await?,
|
||||||
);
|
);
|
||||||
a.content_disposition = ContentDisposition::from(r#"attachment; filename="msg.asc""#.as_bytes());
|
a.content_disposition = ContentDisposition::from(r#"attachment; filename="msg.asc""#.as_bytes());
|
||||||
Ok(a)
|
a
|
||||||
})
|
};
|
||||||
.chain_err_summary(|| {
|
|
||||||
format!("Failed to launch {} to verify PGP signature", binary)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let mut a: AttachmentBuilder = AttachmentBuilder::new("Version: 1".as_bytes());
|
let mut a: AttachmentBuilder = AttachmentBuilder::new("Version: 1".as_bytes());
|
||||||
a.set_content_type_from_bytes("application/pgp-encrypted".as_bytes());
|
a.set_content_type_from_bytes("application/pgp-encrypted".as_bytes());
|
||||||
a.set_content_disposition(ContentDisposition::from("attachment".as_bytes()));
|
a.set_content_disposition(ContentDisposition::from("attachment".as_bytes()));
|
||||||
|
|
|
@ -105,7 +105,7 @@ pub enum AttachmentDisplay {
|
||||||
SignedPending {
|
SignedPending {
|
||||||
inner: Attachment,
|
inner: Attachment,
|
||||||
display: Vec<AttachmentDisplay>,
|
display: Vec<AttachmentDisplay>,
|
||||||
handle: std::result::Result<JoinHandle<Result<()>>, JoinHandle<Result<Vec<u8>>>>,
|
handle: JoinHandle<Result<()>>,
|
||||||
job_id: JobId,
|
job_id: JobId,
|
||||||
},
|
},
|
||||||
SignedFailed {
|
SignedFailed {
|
||||||
|
@ -645,99 +645,8 @@ impl MailView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MultipartType::Signed => {
|
MultipartType::Signed => {
|
||||||
if *mailbox_settings!(
|
#[cfg(not(feature = "gpgme"))]
|
||||||
context[coordinates.0][&coordinates.1]
|
{
|
||||||
.pgp
|
|
||||||
.auto_verify_signatures
|
|
||||||
) {
|
|
||||||
if let Some(bin) = mailbox_settings!(
|
|
||||||
context[coordinates.0][&coordinates.1].pgp.gpg_binary
|
|
||||||
) {
|
|
||||||
let verify_fut = crate::components::mail::pgp::verify(
|
|
||||||
a.clone(),
|
|
||||||
Some(bin.to_string()),
|
|
||||||
);
|
|
||||||
let handle = context.job_executor.spawn_blocking(verify_fut);
|
|
||||||
active_jobs.insert(handle.job_id);
|
|
||||||
acc.push(AttachmentDisplay::SignedPending {
|
|
||||||
inner: a.clone(),
|
|
||||||
display: {
|
|
||||||
let mut v = vec![];
|
|
||||||
rec(&parts[0], context, coordinates, &mut v, active_jobs);
|
|
||||||
v
|
|
||||||
},
|
|
||||||
job_id: handle.job_id,
|
|
||||||
handle: Err(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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
acc.push(AttachmentDisplay::SignedUnverified {
|
acc.push(AttachmentDisplay::SignedUnverified {
|
||||||
inner: a.clone(),
|
inner: a.clone(),
|
||||||
display: {
|
display: {
|
||||||
|
@ -747,63 +656,67 @@ impl MailView {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
#[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());
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
} 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 => {
|
MultipartType::Encrypted => {
|
||||||
for a in parts {
|
for a in parts {
|
||||||
if a.content_type == "application/octet-stream" {
|
if a.content_type == "application/octet-stream" {
|
||||||
if *mailbox_settings!(
|
#[cfg(not(feature = "gpgme"))]
|
||||||
context[coordinates.0][&coordinates.1].pgp.auto_decrypt
|
{
|
||||||
) {
|
acc.push(AttachmentDisplay::EncryptedFailed {
|
||||||
let _verify = *mailbox_settings!(
|
inner: a.clone(),
|
||||||
context[coordinates.0][&coordinates.1]
|
error: MeliError::new("Cannot decrypt: meli must be compiled with libgpgme support."),
|
||||||
.pgp
|
});
|
||||||
.auto_verify_signatures
|
}
|
||||||
);
|
#[cfg(feature = "gpgme")]
|
||||||
if let Some(bin) = mailbox_settings!(
|
{
|
||||||
context[coordinates.0][&coordinates.1].pgp.gpg_binary
|
if *mailbox_settings!(
|
||||||
|
context[coordinates.0][&coordinates.1].pgp.auto_decrypt
|
||||||
) {
|
) {
|
||||||
let decrypt_fut = crate::components::mail::pgp::decrypt(
|
let decrypt_fut =
|
||||||
a.raw().to_vec(),
|
crate::components::mail::pgp::decrypt(a.raw().to_vec());
|
||||||
Some(bin.to_string()),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
let handle =
|
let handle =
|
||||||
context.job_executor.spawn_blocking(decrypt_fut);
|
context.job_executor.spawn_specialized(decrypt_fut);
|
||||||
active_jobs.insert(handle.job_id);
|
active_jobs.insert(handle.job_id);
|
||||||
acc.push(AttachmentDisplay::EncryptedPending {
|
acc.push(AttachmentDisplay::EncryptedPending {
|
||||||
inner: a.clone(),
|
inner: a.clone(),
|
||||||
handle,
|
handle,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
#[cfg(not(feature = "gpgme"))]
|
acc.push(AttachmentDisplay::EncryptedFailed {
|
||||||
{
|
inner: a.clone(),
|
||||||
acc.push(AttachmentDisplay::EncryptedFailed {
|
error: MeliError::new("Undecrypted."),
|
||||||
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,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1529,64 +1442,27 @@ impl Component for MailView {
|
||||||
} if *our_job_id == *job_id => {
|
} if *our_job_id == *job_id => {
|
||||||
caught = true;
|
caught = true;
|
||||||
self.initialised = false;
|
self.initialised = false;
|
||||||
match handle.as_mut() {
|
match handle.chan.try_recv().unwrap().unwrap() {
|
||||||
Ok(handle) => match handle
|
Ok(()) => {
|
||||||
.chan
|
*d = AttachmentDisplay::SignedVerified {
|
||||||
.try_recv()
|
inner: std::mem::replace(
|
||||||
.unwrap()
|
inner,
|
||||||
.unwrap()
|
AttachmentBuilder::new(&[]).build(),
|
||||||
{
|
),
|
||||||
Ok(()) => {
|
display: std::mem::replace(display, vec![]),
|
||||||
*d = AttachmentDisplay::SignedVerified {
|
description: String::new(),
|
||||||
inner: std::mem::replace(
|
};
|
||||||
inner,
|
}
|
||||||
AttachmentBuilder::new(&[]).build(),
|
Err(error) => {
|
||||||
),
|
*d = AttachmentDisplay::SignedFailed {
|
||||||
display: std::mem::replace(display, vec![]),
|
inner: std::mem::replace(
|
||||||
description: String::new(),
|
inner,
|
||||||
};
|
AttachmentBuilder::new(&[]).build(),
|
||||||
}
|
),
|
||||||
Err(error) => {
|
display: std::mem::replace(display, vec![]),
|
||||||
*d = AttachmentDisplay::SignedFailed {
|
error,
|
||||||
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AttachmentDisplay::EncryptedPending { inner, handle }
|
AttachmentDisplay::EncryptedPending { inner, handle }
|
||||||
|
|
|
@ -375,6 +375,10 @@ impl FormWidget {
|
||||||
self.fields.insert(value.0, value.1);
|
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> {
|
pub fn values_mut(&mut self) -> &mut HashMap<String, Field> {
|
||||||
&mut self.fields
|
&mut self.fields
|
||||||
}
|
}
|
||||||
|
|
|
@ -285,45 +285,77 @@ impl Default for TagsSettingsOverride {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct PGPSettingsOverride {
|
pub struct PGPSettingsOverride {
|
||||||
#[doc = " auto verify signed e-mail according to RFC3156"]
|
#[doc = " auto verify signed e-mail according to RFC3156"]
|
||||||
|
#[doc = " Default: true"]
|
||||||
#[serde(alias = "auto-verify-signatures")]
|
#[serde(alias = "auto-verify-signatures")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub auto_verify_signatures: Option<bool>,
|
pub auto_verify_signatures: Option<bool>,
|
||||||
#[doc = " auto decrypt encrypted e-mail"]
|
#[doc = " auto decrypt encrypted e-mail"]
|
||||||
|
#[doc = " Default: true"]
|
||||||
#[serde(alias = "auto-decrypt")]
|
#[serde(alias = "auto-decrypt")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub auto_decrypt: Option<bool>,
|
pub auto_decrypt: Option<bool>,
|
||||||
#[doc = " always sign sent messages"]
|
#[doc = " always sign sent e-mail"]
|
||||||
|
#[doc = " Default: false"]
|
||||||
#[serde(alias = "auto-sign")]
|
#[serde(alias = "auto-sign")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub auto_sign: Option<bool>,
|
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(alias = "sign-key")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub sign_key: Option<Option<String>>,
|
pub sign_key: Option<Option<String>>,
|
||||||
|
#[doc = " Default: None"]
|
||||||
#[serde(alias = "decrypt-key")]
|
#[serde(alias = "decrypt-key")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub decrypt_key: Option<Option<String>>,
|
pub decrypt_key: Option<Option<String>>,
|
||||||
|
#[doc = " Default: None"]
|
||||||
#[serde(alias = "encrypt-key")]
|
#[serde(alias = "encrypt-key")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub encrypt_key: Option<Option<String>>,
|
pub encrypt_key: Option<Option<String>>,
|
||||||
#[doc = " gpg binary name or file location to use"]
|
#[doc = " Allow remote lookups"]
|
||||||
#[serde(alias = "gpg-binary")]
|
#[doc = " Default: None"]
|
||||||
|
#[serde(alias = "allow-remote-lookups")]
|
||||||
#[serde(default)]
|
#[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 {
|
impl Default for PGPSettingsOverride {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PGPSettingsOverride {
|
PGPSettingsOverride {
|
||||||
auto_verify_signatures: None,
|
auto_verify_signatures: None,
|
||||||
auto_decrypt: None,
|
auto_decrypt: None,
|
||||||
auto_sign: None,
|
auto_sign: None,
|
||||||
|
auto_encrypt: None,
|
||||||
sign_key: None,
|
sign_key: None,
|
||||||
decrypt_key: None,
|
decrypt_key: None,
|
||||||
encrypt_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 {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -19,49 +19,86 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
use super::default_vals::*;
|
use super::default_vals::*;
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
|
use melib::conf::ToggleFlag;
|
||||||
|
|
||||||
|
#[cfg(feature = "gpgme")]
|
||||||
/// Settings for digital signing and encryption
|
/// Settings for digital signing and encryption
|
||||||
#[derive(Debug, Deserialize, Clone, Serialize)]
|
#[derive(Debug, Deserialize, Clone, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct PGPSettings {
|
pub struct PGPSettings {
|
||||||
/// auto verify signed e-mail according to RFC3156
|
/// auto verify signed e-mail according to RFC3156
|
||||||
|
/// Default: true
|
||||||
#[serde(default = "true_val", alias = "auto-verify-signatures")]
|
#[serde(default = "true_val", alias = "auto-verify-signatures")]
|
||||||
pub auto_verify_signatures: bool,
|
pub auto_verify_signatures: bool,
|
||||||
|
|
||||||
/// auto decrypt encrypted e-mail
|
/// auto decrypt encrypted e-mail
|
||||||
|
/// Default: true
|
||||||
#[serde(default = "true_val", alias = "auto-decrypt")]
|
#[serde(default = "true_val", alias = "auto-decrypt")]
|
||||||
pub auto_decrypt: bool,
|
pub auto_decrypt: bool,
|
||||||
|
|
||||||
/// always sign sent messages
|
/// always sign sent e-mail
|
||||||
|
/// Default: false
|
||||||
#[serde(default = "false_val", alias = "auto-sign")]
|
#[serde(default = "false_val", alias = "auto-sign")]
|
||||||
pub auto_sign: bool,
|
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
|
// https://tools.ietf.org/html/rfc4880#section-12.2
|
||||||
|
/// Default: None
|
||||||
#[serde(default = "none", alias = "sign-key")]
|
#[serde(default = "none", alias = "sign-key")]
|
||||||
pub sign_key: Option<String>,
|
pub sign_key: Option<String>,
|
||||||
|
|
||||||
|
/// Default: None
|
||||||
#[serde(default = "none", alias = "decrypt-key")]
|
#[serde(default = "none", alias = "decrypt-key")]
|
||||||
pub decrypt_key: Option<String>,
|
pub decrypt_key: Option<String>,
|
||||||
|
|
||||||
|
/// Default: None
|
||||||
#[serde(default = "none", alias = "encrypt-key")]
|
#[serde(default = "none", alias = "encrypt-key")]
|
||||||
pub encrypt_key: Option<String>,
|
pub encrypt_key: Option<String>,
|
||||||
|
|
||||||
/// gpg binary name or file location to use
|
/// Allow remote lookups
|
||||||
#[serde(default, alias = "gpg-binary")]
|
/// Default: None
|
||||||
pub gpg_binary: Option<String>,
|
#[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 {
|
impl Default for PGPSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
PGPSettings {
|
PGPSettings {
|
||||||
auto_verify_signatures: true,
|
auto_verify_signatures: true,
|
||||||
auto_decrypt: true,
|
auto_decrypt: true,
|
||||||
auto_sign: false,
|
auto_sign: false,
|
||||||
|
auto_encrypt: false,
|
||||||
sign_key: None,
|
sign_key: None,
|
||||||
decrypt_key: None,
|
decrypt_key: None,
|
||||||
encrypt_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;
|
||||||
|
|
Loading…
Reference in New Issue