compose: add encrypt layer
parent
3ec1ecb349
commit
be45b0c02d
|
@ -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,15 +454,33 @@ impl Composer {
|
|||
None,
|
||||
);
|
||||
}
|
||||
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 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,
|
||||
theme_default.fg,
|
||||
theme_default.bg,
|
||||
theme_default.attrs,
|
||||
(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();
|
||||
|
|
|
@ -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