compose: add encrypt layer
parent
3ec1ecb349
commit
be45b0c02d
|
@ -82,6 +82,7 @@ pub struct Composer {
|
||||||
embed_area: Area,
|
embed_area: Area,
|
||||||
embed: Option<EmbedStatus>,
|
embed: Option<EmbedStatus>,
|
||||||
sign_mail: ToggleFlag,
|
sign_mail: ToggleFlag,
|
||||||
|
encrypt_mail: ToggleFlag,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
has_changes: bool,
|
has_changes: bool,
|
||||||
initialized: bool,
|
initialized: bool,
|
||||||
|
@ -104,6 +105,7 @@ impl Default for Composer {
|
||||||
|
|
||||||
mode: ViewMode::Edit,
|
mode: ViewMode::Edit,
|
||||||
sign_mail: ToggleFlag::Unset,
|
sign_mail: ToggleFlag::Unset,
|
||||||
|
encrypt_mail: ToggleFlag::Unset,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
has_changes: false,
|
has_changes: false,
|
||||||
embed_area: ((0, 0), (0, 0)),
|
embed_area: ((0, 0), (0, 0)),
|
||||||
|
@ -452,15 +454,33 @@ impl Composer {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
write_string_to_grid(
|
if self.encrypt_mail.is_true() {
|
||||||
"☐ don't encrypt",
|
write_string_to_grid(
|
||||||
grid,
|
&format!(
|
||||||
theme_default.fg,
|
"☑ encrypt with {}",
|
||||||
theme_default.bg,
|
account_settings!(context[self.account_hash].pgp.encrypt_key)
|
||||||
theme_default.attrs,
|
.as_ref()
|
||||||
(pos_inc(upper_left!(area), (0, 2)), bottom_right!(area)),
|
.map(|s| s.as_str())
|
||||||
None,
|
.unwrap_or("default key")
|
||||||
);
|
),
|
||||||
|
grid,
|
||||||
|
theme_default.fg,
|
||||||
|
theme_default.bg,
|
||||||
|
theme_default.attrs,
|
||||||
|
(pos_inc(upper_left!(area), (0, 2)), bottom_right!(area)),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
write_string_to_grid(
|
||||||
|
"☐ don't encrypt",
|
||||||
|
grid,
|
||||||
|
theme_default.fg,
|
||||||
|
theme_default.bg,
|
||||||
|
theme_default.attrs,
|
||||||
|
(pos_inc(upper_left!(area), (0, 2)), bottom_right!(area)),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
}
|
||||||
if attachments_no == 0 {
|
if attachments_no == 0 {
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
"no attachments",
|
"no attachments",
|
||||||
|
@ -533,6 +553,11 @@ impl Component for Composer {
|
||||||
context[self.account_hash].pgp.auto_sign
|
context[self.account_hash].pgp.auto_sign
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if self.encrypt_mail.is_unset() {
|
||||||
|
self.encrypt_mail = ToggleFlag::InternalVal(*account_settings!(
|
||||||
|
context[self.account_hash].pgp.auto_encrypt
|
||||||
|
));
|
||||||
|
}
|
||||||
if !self.draft.headers().contains_key("From") || self.draft.headers()["From"].is_empty()
|
if !self.draft.headers().contains_key("From") || self.draft.headers()["From"].is_empty()
|
||||||
{
|
{
|
||||||
self.draft.set_header(
|
self.draft.set_header(
|
||||||
|
@ -730,6 +755,7 @@ impl Component for Composer {
|
||||||
self.update_draft();
|
self.update_draft();
|
||||||
match send_draft_async(
|
match send_draft_async(
|
||||||
self.sign_mail,
|
self.sign_mail,
|
||||||
|
self.encrypt_mail,
|
||||||
context,
|
context,
|
||||||
self.account_hash,
|
self.account_hash,
|
||||||
self.draft.clone(),
|
self.draft.clone(),
|
||||||
|
@ -1324,6 +1350,9 @@ impl Component for Composer {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Action::Compose(ComposeAction::ToggleEncrypt) => {
|
Action::Compose(ComposeAction::ToggleEncrypt) => {
|
||||||
|
let is_true = self.encrypt_mail.is_true();
|
||||||
|
self.encrypt_mail = ToggleFlag::from(!is_true);
|
||||||
|
self.dirty = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -1567,6 +1596,7 @@ pub fn save_draft(
|
||||||
|
|
||||||
pub fn send_draft_async(
|
pub fn send_draft_async(
|
||||||
sign_mail: ToggleFlag,
|
sign_mail: ToggleFlag,
|
||||||
|
encrypt_mail: ToggleFlag,
|
||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
account_hash: AccountHash,
|
account_hash: AccountHash,
|
||||||
mut draft: Draft,
|
mut draft: Draft,
|
||||||
|
@ -1594,6 +1624,32 @@ pub fn send_draft_async(
|
||||||
.map(|s| s.to_string()),
|
.map(|s| s.to_string()),
|
||||||
)?));
|
)?));
|
||||||
}
|
}
|
||||||
|
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(
|
||||||
|
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,
|
||||||
|
)?));
|
||||||
|
}
|
||||||
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
|
let send_mail = account_settings!(context[account_hash].composing.send_mail).clone();
|
||||||
let send_cb = context.accounts[&account_hash].send_async(send_mail);
|
let send_cb = context.accounts[&account_hash].send_async(send_mail);
|
||||||
let mut content_type = ContentType::default();
|
let mut content_type = ContentType::default();
|
||||||
|
|
|
@ -245,3 +245,79 @@ pub fn sign_filter(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn encrypt_filter(
|
||||||
|
gpg_binary: Option<String>,
|
||||||
|
my_public_key: Option<String>,
|
||||||
|
recipients: Vec<String>,
|
||||||
|
) -> 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();
|
||||||
|
|
||||||
|
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(
|
||||||
|
ContentType::OctetStream { name: None },
|
||||||
|
Default::default(),
|
||||||
|
gpg.stdout,
|
||||||
|
);
|
||||||
|
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)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
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()));
|
||||||
|
let parts = vec![a, sig_attachment.into()];
|
||||||
|
let boundary = ContentType::make_boundary(&parts);
|
||||||
|
Ok(Attachment::new(
|
||||||
|
ContentType::Multipart {
|
||||||
|
boundary: boundary.into_bytes(),
|
||||||
|
kind: MultipartType::Encrypted,
|
||||||
|
parts: parts.into_iter().map(|a| a.into()).collect::<Vec<_>>(),
|
||||||
|
},
|
||||||
|
Default::default(),
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
|
.into())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue