melib/attachments: add SMIME signature variant
parent
6264ee011f
commit
a702a04043
|
@ -198,6 +198,7 @@ pub enum ContentType {
|
||||||
},
|
},
|
||||||
MessageRfc822,
|
MessageRfc822,
|
||||||
PGPSignature,
|
PGPSignature,
|
||||||
|
CMSSignature,
|
||||||
Other {
|
Other {
|
||||||
tag: Vec<u8>,
|
tag: Vec<u8>,
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
|
@ -275,6 +276,7 @@ impl PartialEq<&str> for ContentType {
|
||||||
"multipart/signed",
|
"multipart/signed",
|
||||||
) => true,
|
) => true,
|
||||||
(ContentType::PGPSignature, "application/pgp-signature") => true,
|
(ContentType::PGPSignature, "application/pgp-signature") => true,
|
||||||
|
(ContentType::CMSSignature, "application/pkcs7-signature") => true,
|
||||||
(ContentType::MessageRfc822, "message/rfc822") => true,
|
(ContentType::MessageRfc822, "message/rfc822") => true,
|
||||||
(ContentType::Other { tag, .. }, _) => {
|
(ContentType::Other { tag, .. }, _) => {
|
||||||
other.eq_ignore_ascii_case(&String::from_utf8_lossy(&tag))
|
other.eq_ignore_ascii_case(&String::from_utf8_lossy(&tag))
|
||||||
|
@ -292,6 +294,7 @@ impl Display for ContentType {
|
||||||
ContentType::Multipart { kind: k, .. } => k.fmt(f),
|
ContentType::Multipart { kind: k, .. } => k.fmt(f),
|
||||||
ContentType::Other { ref tag, .. } => write!(f, "{}", String::from_utf8_lossy(tag)),
|
ContentType::Other { ref tag, .. } => write!(f, "{}", String::from_utf8_lossy(tag)),
|
||||||
ContentType::PGPSignature => write!(f, "application/pgp-signature"),
|
ContentType::PGPSignature => write!(f, "application/pgp-signature"),
|
||||||
|
ContentType::CMSSignature => write!(f, "application/pkcs7-signature"),
|
||||||
ContentType::MessageRfc822 => write!(f, "message/rfc822"),
|
ContentType::MessageRfc822 => write!(f, "message/rfc822"),
|
||||||
ContentType::OctetStream { .. } => write!(f, "application/octet-stream"),
|
ContentType::OctetStream { .. } => write!(f, "application/octet-stream"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,10 @@ impl AttachmentBuilder {
|
||||||
&& cst.eq_ignore_ascii_case(b"pgp-signature")
|
&& cst.eq_ignore_ascii_case(b"pgp-signature")
|
||||||
{
|
{
|
||||||
self.content_type = ContentType::PGPSignature;
|
self.content_type = ContentType::PGPSignature;
|
||||||
|
} else if ct.eq_ignore_ascii_case(b"application")
|
||||||
|
&& cst.eq_ignore_ascii_case(b"pkcs7-signature")
|
||||||
|
{
|
||||||
|
self.content_type = ContentType::CMSSignature;
|
||||||
} else {
|
} else {
|
||||||
let mut name: Option<String> = None;
|
let mut name: Option<String> = None;
|
||||||
for (n, v) in params {
|
for (n, v) in params {
|
||||||
|
@ -384,6 +388,7 @@ impl fmt::Display for Attachment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ContentType::PGPSignature => write!(f, "pgp signature [{}]", self.mime_type()),
|
ContentType::PGPSignature => write!(f, "pgp signature [{}]", self.mime_type()),
|
||||||
|
ContentType::CMSSignature => write!(f, "S/MIME signature [{}]", self.mime_type()),
|
||||||
ContentType::OctetStream { .. } | ContentType::Other { .. } => {
|
ContentType::OctetStream { .. } | ContentType::Other { .. } => {
|
||||||
if let Some(name) = self.filename() {
|
if let Some(name) = self.filename() {
|
||||||
write!(
|
write!(
|
||||||
|
@ -548,7 +553,7 @@ impl Attachment {
|
||||||
|
|
||||||
fn get_text_recursive(&self, text: &mut Vec<u8>) {
|
fn get_text_recursive(&self, text: &mut Vec<u8>) {
|
||||||
match self.content_type {
|
match self.content_type {
|
||||||
ContentType::Text { .. } | ContentType::PGPSignature => {
|
ContentType::Text { .. } | ContentType::PGPSignature | ContentType::CMSSignature => {
|
||||||
text.extend(decode(self, None));
|
text.extend(decode(self, None));
|
||||||
}
|
}
|
||||||
ContentType::Multipart {
|
ContentType::Multipart {
|
||||||
|
@ -720,7 +725,7 @@ impl Attachment {
|
||||||
ret.push_str(&format!("Content-Type: {}\r\n\r\n", a.content_type));
|
ret.push_str(&format!("Content-Type: {}\r\n\r\n", a.content_type));
|
||||||
ret.push_str(&String::from_utf8_lossy(a.body()));
|
ret.push_str(&String::from_utf8_lossy(a.body()));
|
||||||
}
|
}
|
||||||
ContentType::PGPSignature => {
|
ContentType::CMSSignature | ContentType::PGPSignature => {
|
||||||
ret.push_str(&format!("Content-Type: {}\r\n\r\n", a.content_type));
|
ret.push_str(&format!("Content-Type: {}\r\n\r\n", a.content_type));
|
||||||
ret.push_str(&String::from_utf8_lossy(a.body()));
|
ret.push_str(&String::from_utf8_lossy(a.body()));
|
||||||
}
|
}
|
||||||
|
@ -808,7 +813,7 @@ fn decode_rec_helper<'a, 'b>(a: &'a Attachment, filter: &mut Option<Filter<'b>>)
|
||||||
ContentType::OctetStream { ref name } => {
|
ContentType::OctetStream { ref name } => {
|
||||||
name.clone().unwrap_or_else(|| a.mime_type()).into_bytes()
|
name.clone().unwrap_or_else(|| a.mime_type()).into_bytes()
|
||||||
}
|
}
|
||||||
ContentType::PGPSignature => Vec::new(),
|
ContentType::CMSSignature | ContentType::PGPSignature => Vec::new(),
|
||||||
ContentType::MessageRfc822 => {
|
ContentType::MessageRfc822 => {
|
||||||
if a.content_disposition.kind.is_inline() {
|
if a.content_disposition.kind.is_inline() {
|
||||||
let b = AttachmentBuilder::new(a.body()).build();
|
let b = AttachmentBuilder::new(a.body()).build();
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! Verification of OpenPGP signatures */
|
/*! Verification of OpenPGP signatures */
|
||||||
use crate::email::parser::BytesExt;
|
|
||||||
use crate::email::{
|
use crate::email::{
|
||||||
attachment_types::{ContentType, MultipartType},
|
attachment_types::{ContentType, MultipartType},
|
||||||
attachments::Attachment,
|
attachments::Attachment,
|
||||||
|
@ -88,7 +87,7 @@ pub fn convert_attachment_to_rfc_spec(input: &[u8]) -> Vec<u8> {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify_signature(a: &Attachment) -> Result<(Vec<u8>, &[u8])> {
|
pub fn verify_signature(a: &Attachment) -> Result<(Vec<u8>, &Attachment)> {
|
||||||
match a.content_type {
|
match a.content_type {
|
||||||
ContentType::Multipart {
|
ContentType::Multipart {
|
||||||
kind: MultipartType::Signed,
|
kind: MultipartType::Signed,
|
||||||
|
@ -107,7 +106,10 @@ pub fn verify_signature(a: &Attachment) -> Result<(Vec<u8>, &[u8])> {
|
||||||
let signed_part: Vec<u8> = if let Some(v) = parts
|
let signed_part: Vec<u8> = if let Some(v) = parts
|
||||||
.iter()
|
.iter()
|
||||||
.zip(part_boundaries.iter())
|
.zip(part_boundaries.iter())
|
||||||
.find(|(p, _)| p.content_type != ContentType::PGPSignature)
|
.find(|(p, _)| {
|
||||||
|
p.content_type != ContentType::PGPSignature
|
||||||
|
&& p.content_type != ContentType::CMSSignature
|
||||||
|
})
|
||||||
.map(|(_, s)| convert_attachment_to_rfc_spec(s.display_bytes(a.body())))
|
.map(|(_, s)| convert_attachment_to_rfc_spec(s.display_bytes(a.body())))
|
||||||
{
|
{
|
||||||
v
|
v
|
||||||
|
@ -116,12 +118,11 @@ pub fn verify_signature(a: &Attachment) -> Result<(Vec<u8>, &[u8])> {
|
||||||
"multipart/signed attachment without a signed part".to_string(),
|
"multipart/signed attachment without a signed part".to_string(),
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
let signature = if let Some(sig) = parts
|
let signature = if let Some(sig) = parts.iter().find(|s| {
|
||||||
.iter()
|
s.content_type == ContentType::PGPSignature
|
||||||
.find(|s| s.content_type == ContentType::PGPSignature)
|
|| s.content_type == ContentType::CMSSignature
|
||||||
.map(|a| a.body())
|
}) {
|
||||||
{
|
sig
|
||||||
sig.trim()
|
|
||||||
} else {
|
} else {
|
||||||
return Err(MeliError::new(
|
return Err(MeliError::new(
|
||||||
"multipart/signed attachment without a signature part".to_string(),
|
"multipart/signed attachment without a signature part".to_string(),
|
||||||
|
|
|
@ -25,6 +25,7 @@ use melib::email::{
|
||||||
};
|
};
|
||||||
use melib::error::*;
|
use melib::error::*;
|
||||||
use melib::gpgme::*;
|
use melib::gpgme::*;
|
||||||
|
use melib::parser::BytesExt;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ pub async fn verify(a: Attachment) -> Result<()> {
|
||||||
let (data, sig) =
|
let (data, sig) =
|
||||||
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
|
melib_pgp::verify_signature(&a).chain_err_summary(|| "Could not verify signature.")?;
|
||||||
let mut ctx = Context::new()?;
|
let mut ctx = Context::new()?;
|
||||||
let sig = ctx.new_data_mem(&sig)?;
|
let sig = ctx.new_data_mem(&sig.body().trim())?;
|
||||||
let data = ctx.new_data_mem(&data)?;
|
let data = ctx.new_data_mem(&data)?;
|
||||||
ctx.verify(sig, data)?.await
|
ctx.verify(sig, data)?.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1797,7 +1797,9 @@ impl Component for MailView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentType::Text { .. } | ContentType::PGPSignature => {
|
ContentType::Text { .. }
|
||||||
|
| ContentType::PGPSignature
|
||||||
|
| ContentType::CMSSignature => {
|
||||||
self.mode = ViewMode::Attachment(lidx);
|
self.mode = ViewMode::Attachment(lidx);
|
||||||
self.initialised = false;
|
self.initialised = false;
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
|
|
@ -419,7 +419,9 @@ impl Component for EnvelopeView {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
ContentType::Text { .. } => {
|
ContentType::Text { .. }
|
||||||
|
| ContentType::PGPSignature
|
||||||
|
| ContentType::CMSSignature => {
|
||||||
self.mode = ViewMode::Attachment(lidx);
|
self.mode = ViewMode::Attachment(lidx);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
@ -479,14 +481,6 @@ impl Component for EnvelopeView {
|
||||||
));
|
));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ContentType::PGPSignature => {
|
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
|
||||||
StatusEvent::DisplayMessage(
|
|
||||||
"Signatures aren't supported yet".to_string(),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
|
|
Loading…
Reference in New Issue