summaryrefslogtreecommitdiffstats
path: root/src/components/mail
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-10-09 11:58:18 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-10-11 16:53:04 +0300
commitbe45b0c02d3a7dbaec6e0ac15094e0448f5e5e29 (patch)
tree37b484500678455ef36aef7640561cde97710bd3 /src/components/mail
parent3ec1ecb34989cc942192fd6ea471a1f0bbd9dd71 (diff)
compose: add encrypt layer
Diffstat (limited to 'src/components/mail')
-rw-r--r--src/components/mail/compose.rs74
-rw-r--r--src/components/mail/pgp.rs76
2 files changed, 141 insertions, 9 deletions
diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs
index 463490be..93eecae0 100644
--- a/src/components/mail/compose.rs
+++ b/src/components/mail/compose.rs
@@ -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();
diff --git a/src/components/mail/pgp.rs b/src/components/mail/pgp.rs
index e0e40895..64c13c16 100644
--- a/src/components/mail/pgp.rs
+++ b/src/components/mail/pgp.rs
@@ -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())
+ })
+ },
+ )
+}