summaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-10-05 18:43:08 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-10-05 21:10:00 +0300
commit23ca41e3e878abb9e9206e984b6c1e1d2b905aaa (patch)
tree0a3c053b304fa349cfa999d3bdb2b1814ca7632a /src/components
parentb9c07bacef256e781a2f6884f9e01f8f211d4741 (diff)
add libgpgme feature
Diffstat (limited to 'src/components')
-rw-r--r--src/components/mail/compose.rs4
-rw-r--r--src/components/mail/pgp.rs171
-rw-r--r--src/components/mail/view.rs1391
3 files changed, 1090 insertions, 476 deletions
diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs
index f3a94e38..10e8700f 100644
--- a/src/components/mail/compose.rs
+++ b/src/components/mail/compose.rs
@@ -428,7 +428,7 @@ impl Composer {
write_string_to_grid(
&format!(
"☑ sign with {}",
- account_settings!(context[self.account_hash].pgp.key)
+ account_settings!(context[self.account_hash].pgp.sign_key)
.as_ref()
.map(|s| s.as_str())
.unwrap_or("default key")
@@ -1473,7 +1473,7 @@ pub fn send_draft(
account_settings!(context[account_hash].pgp.gpg_binary)
.as_ref()
.map(|s| s.as_str()),
- account_settings!(context[account_hash].pgp.key)
+ account_settings!(context[account_hash].pgp.sign_key)
.as_ref()
.map(|s| s.as_str()),
);
diff --git a/src/components/mail/pgp.rs b/src/components/mail/pgp.rs
index 264c9584..837c8d20 100644
--- a/src/components/mail/pgp.rs
+++ b/src/components/mail/pgp.rs
@@ -20,15 +20,38 @@
*/
use super::*;
+use melib::email::pgp as melib_pgp;
use std::io::Write;
use std::process::{Command, Stdio};
-pub fn verify_signature(a: &Attachment, context: &mut Context) -> Vec<u8> {
- match melib::signatures::verify_signature(a) {
- Ok((bytes, sig)) => {
- let bytes_file = create_temp_file(&bytes, None, None, true);
- let signature_file = create_temp_file(sig, None, None, true);
- match Command::new(
+pub fn verify_signature(a: &Attachment, context: &mut Context) -> Result<Vec<u8>> {
+ let (bytes, sig) =
+ melib_pgp::verify_signature(a).chain_err_summary(|| "Could not verify signature.")?;
+ let bytes_file = create_temp_file(&bytes, None, None, true);
+ 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
@@ -37,50 +60,7 @@ pub fn verify_signature(a: &Attachment, context: &mut Context) -> Vec<u8> {
.map(String::as_str)
.unwrap_or("gpg2"),
)
- .args(&[
- "--output",
- "-",
- "--verify",
- signature_file.path.to_str().unwrap(),
- bytes_file.path.to_str().unwrap(),
- ])
- .stdin(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn()
- {
- Ok(gpg) => {
- return gpg.wait_with_output().unwrap().stderr;
- }
- Err(err) => {
- context.replies.push_back(UIEvent::Notification(
- Some(format!(
- "Failed to launch {} to verify PGP signature",
- context
- .settings
- .pgp
- .gpg_binary
- .as_ref()
- .map(String::as_str)
- .unwrap_or("gpg2"),
- )),
- format!(
- "{}\nsee meli.conf(5) for configuration setting pgp.gpg_binary",
- &err
- ),
- Some(NotificationType::Error(melib::error::ErrorKind::External)),
- ));
- }
- }
- }
- Err(err) => {
- context.replies.push_back(UIEvent::Notification(
- Some("Could not verify signature.".to_string()),
- err.to_string(),
- Some(NotificationType::Error(err.kind)),
- ));
- }
- }
- Vec::new()
+ })?)
}
/// Returns multipart/signed
@@ -89,7 +69,8 @@ pub fn sign(
gpg_binary: Option<&str>,
pgp_key: Option<&str>,
) -> Result<AttachmentBuilder> {
- let mut command = Command::new(gpg_binary.unwrap_or("gpg2"));
+ let binary = gpg_binary.unwrap_or("gpg2");
+ let mut command = Command::new(binary);
command.args(&[
"--digest-algo",
"sha512",
@@ -102,23 +83,27 @@ pub fn sign(
command.args(&["--local-user", key]);
}
let a: Attachment = a.into();
- let mut gpg = command
+
+ let sig_attachment = command
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
- .spawn()?;
-
- let sig_attachment = {
- gpg.stdin
- .as_mut()
- .unwrap()
- .write_all(&melib::signatures::convert_attachment_to_rfc_spec(
- a.into_raw().as_bytes(),
+ .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,
))
- .unwrap();
- let gpg = gpg.wait_with_output().unwrap();
- 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()];
@@ -134,3 +119,61 @@ pub fn sign(
)
.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.")?;
+ let bytes_file = create_temp_file(&bytes, None, None, true);
+ let signature_file = create_temp_file(sig, None, None, true);
+ Ok(
+ Command::new(gpg_binary.as_ref().map(String::as_str).unwrap_or("gpg2"))
+ .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"),
+ )
+ })?,
+ )
+}
diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs
index 726cfef2..3329c82f 100644
--- a/src/components/mail/view.rs
+++ b/src/components/mail/view.rs
@@ -22,6 +22,7 @@
use super::*;
use crate::conf::accounts::JobRequest;
use crate::jobs::{oneshot, JobId};
+use melib::email::attachment_types::ContentType;
use melib::list_management;
use melib::parser::BytesExt;
use smallvec::SmallVec;
@@ -29,6 +30,7 @@ use std::collections::HashSet;
use std::io::Write;
use std::convert::TryFrom;
+use std::os::unix::fs::PermissionsExt;
use std::process::{Command, Stdio};
mod html;
@@ -39,7 +41,7 @@ pub use self::thread::*;
mod envelope;
pub use self::envelope::*;
-use linkify::{Link, LinkFinder};
+use linkify::LinkFinder;
use xdg_utils::query_default_app;
#[derive(PartialEq, Copy, Clone, Debug)]
@@ -54,7 +56,7 @@ enum ViewMode {
Url,
Attachment(usize),
Source(Source),
- Ansi(RawBuffer),
+ //Ansi(RawBuffer),
Subview,
ContactSelector(UIDialog<Card>),
}
@@ -66,12 +68,14 @@ impl Default for ViewMode {
}
impl ViewMode {
+ /*
fn is_ansi(&self) -> bool {
match self {
ViewMode::Ansi(_) => true,
_ => false,
}
}
+ */
fn is_attachment(&self) -> bool {
match self {
ViewMode::Attachment(_) => true,
@@ -86,6 +90,56 @@ impl ViewMode {
}
}
+#[derive(Debug)]
+pub enum AttachmentDisplay {
+ InlineText {
+ inner: Attachment,
+ text: String,
+ },
+ InlineOther {
+ inner: Attachment,
+ },
+ Attachment {
+ inner: Attachment,
+ },
+ SignedPending {
+ inner: Attachment,
+ display: Vec<AttachmentDisplay>,
+ chan:
+ std::result::Result<oneshot::Receiver<Result<()>>, oneshot::Receiver<Result<Vec<u8>>>>,
+ job_id: JobId,
+ },
+ SignedFailed {
+ inner: Attachment,
+ display: Vec<AttachmentDisplay>,
+ error: MeliError,
+ },
+ SignedUnverified {
+ inner: Attachment,
+ display: Vec<AttachmentDisplay>,
+ },
+ SignedVerified {
+ inner: Attachment,
+ display: Vec<AttachmentDisplay>,
+ description: String,
+ },
+ EncryptedPending {
+ inner: Attachment,
+ chan: oneshot::Receiver<Result<(melib::pgp::DecryptionMetadata, Vec<u8>)>>,
+ job_id: JobId,
+ },
+ EncryptedFailed {
+ inner: Attachment,
+ error: MeliError,
+ },
+ EncryptedSuccess {
+ inner: Attachment,
+ plaintext: Attachment,
+ plaintext_display: Vec<AttachmentDisplay>,
+ description: String,
+ },
+}
+
/// Contains an Envelope view, with sticky headers, a pager for the body, and subviews for more
/// menus
#[derive(Debug, Default)]
@@ -98,6 +152,7 @@ pub struct MailView {
mode: ViewMode,
expand_headers: bool,
attachment_tree: String,
+ attachment_paths: Vec<Vec<usize>>,
headers_no: usize,
headers_cursor: usize,
force_draw_headers: bool,
@@ -117,7 +172,7 @@ pub enum PendingReplyAction {
}
#[derive(Debug)]
-pub enum MailViewState {
+enum MailViewState {
Init {
pending_action: Option<PendingReplyAction>,
},
@@ -132,10 +187,25 @@ pub enum MailViewState {
Loaded {
bytes: Vec<u8>,
body: Attachment,
+ display: Vec<AttachmentDisplay>,
body_text: String,
+ links: Vec<Link>,
},
}
+#[derive(Copy, Clone, Debug)]
+enum LinkKind {
+ Url,
+ Email,
+}
+
+#[derive(Debug, Copy, Clone)]
+struct Link {
+ start: usize,
+ end: usize,
+ kind: LinkKind,
+}
+
impl Default for MailViewState {
fn default() -> Self {
MailViewState::Init {
@@ -152,6 +222,7 @@ impl Clone for MailView {
pager: self.pager.clone(),
mode: ViewMode::Normal,
attachment_tree: self.attachment_tree.clone(),
+ attachment_paths: self.attachment_paths.clone(),
state: MailViewState::default(),
active_jobs: self.active_jobs.clone(),
..*self
@@ -182,6 +253,7 @@ impl MailView {
mode: ViewMode::Normal,
expand_headers: false,
attachment_tree: String::new(),
+ attachment_paths: vec![],
headers_no: 5,
headers_cursor: 0,
@@ -235,11 +307,24 @@ impl MailView {
.populate_headers(&bytes);
}
let body = AttachmentBuilder::new(&bytes).build();
- let body_text = self.attachment_to_text(&body, context);
+ let display = Self::attachment_to(
+ &body,
+ context,
+ self.coordinates,
+ &mut self.active_jobs,
+ );
+ let (paths, attachment_tree_s) =
+ self.attachment_displays_to_tree(&display);
+ self.attachment_tree = attachment_tree_s;
+ self.attachment_paths = paths;
+ let body_text =
+ self.attachment_displays_to_text(&display, context);
self.state = MailViewState::Loaded {
+ display,
body,
bytes,
body_text,
+ links: vec![],
};
}
Err(err) => {
@@ -331,132 +416,418 @@ impl MailView {
.push_back(UIEvent::Action(Tab(New(Some(composer)))));
}
- /// Returns the string to be displayed in the Viewer
- fn attachment_to_text(&mut self, body: &Attachment, context: &mut Context) -> String {
- let finder = LinkFinder::new();
- let coordinates = self.coordinates;
- let body_text = String::from_utf8_lossy(&decode_rec(
- body,
- Some(Box::new(move |a: &Attachment, v: &mut Vec<u8>| {
- if a.content_type().is_text_html() {
- /* FIXME: duplication with view/html.rs */
- if let Some(filter_invocation) =
- mailbox_settings!(context[coordinates.0][&coordinates.1].pager.html_filter)
- .as_ref()
- {
- let command_obj = Command::new("sh")
- .args(&["-c", filter_invocation])
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn();
- match command_obj {
- Err(err) => {
- context.replies.push_back(UIEvent::Notification(
- Some(format!(
- "Failed to start html filter process: {}",
- filter_invocation,
- )),
- err.to_string(),
- Some(NotificationType::Error(melib::ErrorKind::External)),
- ));
- return;
- }
- Ok(mut html_filter) => {
- html_filter
- .stdin
- .as_mut()
- .unwrap()
- .write_all(&v)
- .expect("Failed to write to stdin");
- *v = format!(
+ fn attachment_displays_to_text(
+ &self,
+ displays: &[AttachmentDisplay],
+ context: &mut Context,
+ ) -> String {
+ let mut acc = String::new();
+ for d in displays {
+ use AttachmentDisplay::*;
+ match d {
+ InlineText { inner: _, text } => acc.push_str(&text),
+ InlineOther { inner } => {
+ if !acc.ends_with("\n\n") {
+ acc.push_str("\n\n");
+ }
+ acc.push_str(&inner.to_string());
+ if !acc.ends_with("\n\n") {
+ acc.push_str("\n\n");
+ }
+ }
+ Attachment { inner: _ } => {}
+ SignedPending {
+ inner: _,
+ display,
+ chan: _,
+ job_id: _,
+ } => {
+ acc.push_str("Waiting for signature verification.\n\n");
+ acc.push_str(&self.attachment_displays_to_text(display, context));
+ }
+ SignedUnverified { inner: _, display } => {
+ acc.push_str("Unverified signature.\n\n");
+ acc.push_str(&self.attachment_displays_to_text(display, context))
+ }
+ SignedFailed {
+ inner: _,
+ display,
+ error,
+ } => {
+ acc.push_str(&format!("Failed to verify signature: {}.\n\n", error));
+ acc.push_str(&self.attachment_displays_to_text(display, context));
+ }
+ SignedVerified {
+ inner: _,
+ display,
+ description,
+ } => {
+ if description.is_empty() {
+ acc.push_str("Verified signature.\n\n");
+ } else {
+ acc.push_str(&description);
+ acc.push_str("\n\n");
+ }
+ acc.push_str(&self.attachment_displays_to_text(display, context));
+ }
+ EncryptedPending { .. } => acc.push_str("Waiting for decryption result."),
+ EncryptedFailed { inner: _, error } => {
+ acc.push_str(&format!("Decryption failed: {}.", &error))
+ }
+ EncryptedSuccess {
+ inner: _,
+ plaintext: _,
+ plaintext_display,
+ description,
+ } => {
+ if description.is_empty() {
+ acc.push_str("Succesfully decrypted.\n\n");
+ } else {
+ acc.push_str(&description);
+ acc.push_str("\n\n");
+ }
+ acc.push_str(&self.attachment_displays_to_text(plaintext_display, context));
+ }
+ }
+ }
+ acc
+ }
+
+ fn attachment_displays_to_tree(
+ &self,
+ displays: &[AttachmentDisplay],
+ ) -> (Vec<Vec<usize>>, String) {
+ let mut acc = String::new();
+ let mut branches = SmallVec::new();
+ let mut paths = Vec::with_capacity(displays.len());
+ let mut cur_path = vec![];
+ let mut idx = 0;
+ for (i, d) in displays.iter().enumerate() {
+ use AttachmentDisplay::*;
+ cur_path.push(i);
+ match d {
+ InlineText { inner, text: _ }
+ | InlineOther { inner }
+ | Attachment { inner }
+ | SignedPending {
+ inner,
+ display: _,
+ chan: _,
+ job_id: _,
+ }
+ | SignedUnverified { inner, display: _ }
+ | SignedFailed {
+ inner,
+ display: _,
+ error: _,
+ }
+ | SignedVerified {
+ inner,
+ display: _,
+ description: _,
+ }
+ | EncryptedPending {
+ inner,
+ chan: _,
+ job_id: _,
+ }
+ | EncryptedFailed { inner, error: _ }
+ | EncryptedSuccess {
+ inner: _,
+ plaintext: inner,
+ plaintext_display: _,
+ description: _,
+ } => {
+ attachment_tree(
+ (&mut idx, (0, inner)),
+ &mut branches,
+ &mut paths,
+ &mut cur_path,
+ i + 1 < displays.len(),
+ &mut acc,
+ );
+ }
+ }
+ cur_path.pop();
+ idx += 1;
+ }
+ (paths, acc)
+ }
+
+ fn attachment_to(
+ body: &Attachment,
+ context: &mut Context,
+ coordinates: (AccountHash, MailboxHash, EnvelopeHash),
+ active_jobs: &mut HashSet<JobId>,
+ ) -> Vec<AttachmentDisplay> {
+ let mut ret = vec![];
+ fn rec(
+ a: &Attachment,
+ context: &mut Context,
+ coordinates: (AccountHash, MailboxHash, EnvelopeHash),
+ acc: &mut Vec<AttachmentDisplay>,
+ active_jobs: &mut HashSet<JobId>,
+ ) {
+ if a.content_disposition.kind.is_attachment() {
+ acc.push(AttachmentDisplay::Attachment { inner: a.clone() });
+ } else if a.content_type().is_text_html() {
+ let bytes = decode(a, None);
+ let filter_invocation =
+ mailbox_settings!(context[coordinates.0][&coordinates.1].pager.html_filter)
+ .as_ref()
+ .map(|s| s.as_str())
+ .unwrap_or("w3m -I utf-8 -T text/html");
+ let command_obj = Command::new("sh")
+ .args(&["-c", filter_invocation])
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .spawn();
+ match command_obj {
+ Err(err) => {
+ context.replies.push_back(UIEvent::Notification(
+ Some(format!(
+ "Failed to start html filter process: {}",
+ filter_invocation,
+ )),
+ err.to_string(),
+ Some(NotificationType::Error(melib::ErrorKind::External)),
+ ));
+ let mut s = format!(
+ "Failed to start html filter process: `{}`. Press `v` to open in web browser. \n\n",
+ filter_invocation
+ );
+ s.push_str(&String::from_utf8_lossy(&bytes));
+ acc.push(AttachmentDisplay::InlineText {
+ inner: a.clone(),
+ text: s,
+ });
+ }
+ Ok(mut html_filter) => {
+ html_filter
+ .stdin
+ .as_mut()
+ .unwrap()
+ .write_all(&bytes)
+ .expect("Failed to write to stdin");
+ let mut s = format!(
"Text piped through `{}`. Press `v` to open in web browser. \n\n",
filter_invocation
- )
- .into_bytes();
- v.extend(html_filter.wait_with_output().unwrap().stdout);
+ );
+ s.push_str(&String::from_utf8_lossy(
+ &html_filter.wait_with_output().unwrap().stdout,
+ ));
+ acc.push(AttachmentDisplay::InlineText {
+ inner: a.clone(),
+ text: s,
+ });
+ }
+ }
+ } else if a.is_text() {
+ let bytes = decode(a, None);
+ acc.push(AttachmentDisplay::InlineText {
+ inner: a.clone(),
+ text: String::from_utf8_lossy(&bytes).to_string(),
+ });
+ } else if let ContentType::Multipart {
+ ref kind,
+ ref parts,
+ ..
+ } = a.content_type
+ {
+ match kind {
+ MultipartType::Alternative => {
+ if let Some(text_attachment_pos) =
+ parts.iter().position(|a| a.content_type == "text/plain")
+ {
+ let bytes = decode(&parts[text_attachment_pos], None);
+ acc.push(AttachmentDisplay::InlineText {
+ inner: a.clone(),
+ text: String::from_utf8_lossy(&bytes).to_string(),
+ });
+ } else {
+ for a in parts {
+ rec(a, context, coordinates, acc, active_jobs);
}
}
- } else {
- match Command::new("w3m")
- .args(&["-I", "utf-8", "-T", "text/html"])
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- {
- Ok(mut html_filter) => {
- html_filter
- .stdin
- .as_mut()
- .unwrap()
- .write_all(&v)
- .expect("Failed to write to html filter stdin");
- *v = String::from(
- "Text piped through `w3m`. Press `v` to open in web browser. \n\n",
- )
- .into_bytes();
- v.extend(html_filter.wait_with_output().unwrap().stdout);
+ }
+ MultipartType::Signed => {
+ if *mailbox_settings!(
+ 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 (chan, _handle, job_id) =
+ context.job_executor.spawn_blocking(verify_fut);
+ active_jobs.insert(job_id);
+ acc.push(AttachmentDisplay::SignedPending {
+ inner: a.clone(),
+ display: {
+ let mut v = vec![];
+ rec(&parts[0], context, coordinates, &mut v, active_jobs);
+ v
+ },
+ chan: Err(chan),
+ job_id,
+ });
+ } 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 (chan, _handle, job_id) =
+ context.job_executor.spawn_specialized(verify_fut);
+ active_jobs.insert(job_id);
+ acc.push(AttachmentDisplay::SignedPending {
+ inner: a.clone(),
+ display: {
+ let mut v = vec![];
+ rec(
+ &parts[0],
+ context,
+ coordinates,
+ &mut v,
+ active_jobs,
+ );
+ v
+ },
+ chan: Ok(chan),
+ job_id,
+ });
+ }
+ 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,
+ });
+ }
+ }
}
- Err(err) => {
- context.replies.push_back(UIEvent::Notification(
- Some("Failed to launch w3m to use as html filter".to_string()),
- err.to_string(),
- Some(NotificationType::Error(melib::ErrorKind::External)),
- ));
+ } 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 => {
+ for a in parts {
+ if a.content_type == "application/octet-stream" {
+ if *mailbox_settings!(
+ context[coordinates.0][&coordinates.1].pgp.auto_decrypt
+ ) {
+ let _verify = *mailbox_settings!(
+ context[coordinates.0][&coordinates.1]
+ .pgp
+ .auto_verify_signatures
+ );
+ if let Some(bin) = mailbox_settings!(
+ context[coordinates.0][&coordinates.1].pgp.gpg_binary
+ ) {
+ let decrypt_fut = crate::components::mail::pgp::decrypt(
+ a.raw().to_vec(),
+ Some(bin.to_string()),
+ None,
+ );
+ let (chan, _handle, job_id) =
+ context.job_executor.spawn_blocking(decrypt_fut);
+ active_jobs.insert(job_id);
+ acc.push(AttachmentDisplay::EncryptedPending {
+ inner: a.clone(),
+ chan,
+ job_id,
+ });
+ } else {
+ #[cfg(not(feature = "gpgme"))]
+ {
+ acc.push(AttachmentDisplay::EncryptedFailed {
+ 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 (chan, _handle, job_id) = context
+ .job_executor
+ .spawn_specialized(decrypt_fut);
+ active_jobs.insert(job_id);
+ acc.push(AttachmentDisplay::EncryptedPending {
+ inner: a.clone(),
+ chan,
+ job_id,
+ });
+ }
+ Err(error) => {
+ acc.pus