compose: add encrypt layer

jmap-eventsource
Manos Pitsidianakis 2020-10-09 11:58:18 +03:00
parent 3ec1ecb349
commit be45b0c02d
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 141 additions and 9 deletions

View File

@ -82,6 +82,7 @@ pub struct Composer {
embed_area: Area,
embed: Option<EmbedStatus>,
sign_mail: ToggleFlag,
encrypt_mail: ToggleFlag,
dirty: bool,
has_changes: bool,
initialized: bool,
@ -104,6 +105,7 @@ impl Default for Composer {
mode: ViewMode::Edit,
sign_mail: ToggleFlag::Unset,
encrypt_mail: ToggleFlag::Unset,
dirty: true,
has_changes: false,
embed_area: ((0, 0), (0, 0)),
@ -452,6 +454,23 @@ impl Composer {
None,
);
}
if self.encrypt_mail.is_true() {
write_string_to_grid(
&format!(
"☑ encrypt with {}",
account_settings!(context[self.account_hash].pgp.encrypt_key)
.as_ref()
.map(|s| s.as_str())
.unwrap_or("default key")
),
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,
@ -461,6 +480,7 @@ impl Composer {
(pos_inc(upper_left!(area), (0, 2)), bottom_right!(area)),
None,
);
}
if attachments_no == 0 {
write_string_to_grid(
"no attachments",
@ -533,6 +553,11 @@ impl Component for Composer {
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()
{
self.draft.set_header(
@ -730,6 +755,7 @@ impl Component for Composer {
self.update_draft();
match send_draft_async(
self.sign_mail,
self.encrypt_mail,
context,
self.account_hash,
self.draft.clone(),
@ -1324,6 +1350,9 @@ impl Component for Composer {
return true;
}
Action::Compose(ComposeAction::ToggleEncrypt) => {
let is_true = self.encrypt_mail.is_true();
self.encrypt_mail = ToggleFlag::from(!is_true);
self.dirty = true;
return true;
}
_ => {}
@ -1567,6 +1596,7 @@ pub fn save_draft(
pub fn send_draft_async(
sign_mail: ToggleFlag,
encrypt_mail: ToggleFlag,
context: &mut Context,
account_hash: AccountHash,
mut draft: Draft,
@ -1594,6 +1624,32 @@ pub fn send_draft_async(
.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_cb = context.accounts[&account_hash].send_async(send_mail);
let mut content_type = ContentType::default();

View File

@ -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())
})
},
)
}