melib: add Other and OctetStream content types

embed
Manos Pitsidianakis 2019-07-31 13:29:55 +03:00
parent c17bb24f0d
commit d73069bc80
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
7 changed files with 128 additions and 30 deletions

View File

@ -127,8 +127,12 @@ pub enum ContentType {
}, },
MessageRfc822, MessageRfc822,
PGPSignature, PGPSignature,
Unsupported { Other {
tag: Vec<u8>, tag: Vec<u8>,
name: Option<String>,
},
OctetStream {
name: Option<String>,
}, },
} }
@ -146,9 +150,10 @@ impl Display for ContentType {
match self { match self {
ContentType::Text { kind: t, .. } => t.fmt(f), ContentType::Text { kind: t, .. } => t.fmt(f),
ContentType::Multipart { kind: k, .. } => k.fmt(f), ContentType::Multipart { kind: k, .. } => k.fmt(f),
ContentType::Unsupported { tag: ref t } => write!(f, "{}", String::from_utf8_lossy(t)), 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::MessageRfc822 => write!(f, "message/rfc822"), ContentType::MessageRfc822 => write!(f, "message/rfc822"),
ContentType::OctetStream { .. } => write!(f, "application/octet-stream"),
} }
} }
} }
@ -161,6 +166,7 @@ impl ContentType {
false false
} }
} }
pub fn is_text_html(&self) -> bool { pub fn is_text_html(&self) -> bool {
if let ContentType::Text { if let ContentType::Text {
kind: Text::Html, .. kind: Text::Html, ..
@ -171,6 +177,14 @@ impl ContentType {
false false
} }
} }
pub fn name(&self) -> Option<&str> {
match self {
ContentType::Other { ref name, .. } => name.as_ref().map(|n| n.as_ref()),
ContentType::OctetStream { ref name } => name.as_ref().map(|n| n.as_ref()),
_ => None,
}
}
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]

View File

@ -27,13 +27,14 @@ use data_encoding::BASE64_MIME;
pub use crate::email::attachment_types::*; pub use crate::email::attachment_types::*;
#[derive(Default, PartialEq)] #[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AttachmentBuilder { pub struct AttachmentBuilder {
content_type: ContentType, pub content_type: ContentType,
content_transfer_encoding: ContentTransferEncoding, pub content_transfer_encoding: ContentTransferEncoding,
raw: Vec<u8>, pub raw: Vec<u8>,
} }
impl AttachmentBuilder { impl AttachmentBuilder {
pub fn new(content: &[u8]) -> Self { pub fn new(content: &[u8]) -> Self {
AttachmentBuilder { AttachmentBuilder {
@ -43,6 +44,15 @@ impl AttachmentBuilder {
} }
} }
pub fn raw(&self) -> &[u8] {
&self.raw
}
pub fn set_raw(&mut self, raw: Vec<u8>) -> &mut Self {
self.raw = raw;
self
}
pub fn set_content_type(&mut self, val: ContentType) -> &mut Self { pub fn set_content_type(&mut self, val: ContentType) -> &mut Self {
self.content_type = val; self.content_type = val;
self self
@ -120,15 +130,26 @@ impl AttachmentBuilder {
{ {
self.content_type = ContentType::PGPSignature; self.content_type = ContentType::PGPSignature;
} else { } else {
let mut name: Option<String> = None;
for (n, v) in params {
if n.eq_ignore_ascii_case(b"name") {
name = Some(String::from_utf8_lossy(v).into());
break;
}
}
let mut tag: Vec<u8> = Vec::with_capacity(ct.len() + cst.len() + 1); let mut tag: Vec<u8> = Vec::with_capacity(ct.len() + cst.len() + 1);
tag.extend(ct); tag.extend(ct);
tag.push(b'/'); tag.push(b'/');
tag.extend(cst); tag.extend(cst);
self.content_type = ContentType::Unsupported { tag }; self.content_type = ContentType::Other { tag, name };
} }
} }
Err(v) => { Err(e) => {
debug!("parsing error in content_type: {:?} {:?}", value, v); debug!(
"parsing error in content_type: {:?} {:?}",
String::from_utf8_lossy(value),
e
);
} }
} }
self self
@ -187,13 +208,28 @@ impl AttachmentBuilder {
} }
} }
impl From<Attachment> for AttachmentBuilder {
fn from(val: Attachment) -> Self {
let Attachment {
content_type,
content_transfer_encoding,
raw,
} = val;
AttachmentBuilder {
content_type,
content_transfer_encoding,
raw,
}
}
}
/// Immutable attachment type. /// Immutable attachment type.
#[derive(Clone, Serialize, Deserialize, PartialEq)] #[derive(Clone, Serialize, Deserialize, PartialEq)]
pub struct Attachment { pub struct Attachment {
content_type: ContentType, pub(in crate::email) content_type: ContentType,
content_transfer_encoding: ContentTransferEncoding, pub(in crate::email) content_transfer_encoding: ContentTransferEncoding,
raw: Vec<u8>, pub(in crate::email) raw: Vec<u8>,
} }
impl fmt::Debug for Attachment { impl fmt::Debug for Attachment {
@ -225,9 +261,10 @@ impl fmt::Display for Attachment {
Err(e) => write!(f, "{}", e), Err(e) => write!(f, "{}", e),
}, },
ContentType::PGPSignature => write!(f, "pgp signature {}", self.mime_type()), ContentType::PGPSignature => write!(f, "pgp signature {}", self.mime_type()),
ContentType::Unsupported { .. } => { ContentType::OctetStream { ref name } => {
write!(f, "Data attachment of type {}", self.mime_type()) write!(f, "{}", name.clone().unwrap_or_else(|| self.mime_type()))
} }
ContentType::Other { .. } => write!(f, "Data attachment of type {}", self.mime_type()),
ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()), ContentType::Text { .. } => write!(f, "Text attachment of type {}", self.mime_type()),
ContentType::Multipart { ContentType::Multipart {
subattachments: ref sub_att_vec, subattachments: ref sub_att_vec,
@ -258,6 +295,7 @@ impl Attachment {
pub fn raw(&self) -> &[u8] { pub fn raw(&self) -> &[u8] {
&self.raw &self.raw
} }
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::Text { .. } => {
@ -403,8 +441,13 @@ type Filter<'a> = Box<FnMut(&'a Attachment, &mut Vec<u8>) -> () + 'a>;
fn decode_rec_helper<'a>(a: &'a Attachment, filter: &mut Option<Filter<'a>>) -> Vec<u8> { fn decode_rec_helper<'a>(a: &'a Attachment, filter: &mut Option<Filter<'a>>) -> Vec<u8> {
match a.content_type { match a.content_type {
ContentType::Unsupported { .. } => Vec::new(), ContentType::Other { .. } => Vec::new(),
ContentType::Text { .. } => decode_helper(a, filter), ContentType::Text { .. } => decode_helper(a, filter),
ContentType::OctetStream { ref name } => name
.clone()
.unwrap_or_else(|| a.mime_type())
.to_string()
.into_bytes(),
ContentType::PGPSignature => a.content_type.to_string().into_bytes(), ContentType::PGPSignature => a.content_type.to_string().into_bytes(),
ContentType::MessageRfc822 => { ContentType::MessageRfc822 => {
let temp = decode_rfc822(&a.raw); let temp = decode_rfc822(&a.raw);

View File

@ -551,7 +551,11 @@ impl Component for Composer {
} }
/* update Draft's headers based on form values */ /* update Draft's headers based on form values */
self.update_draft(); self.update_draft();
let f = create_temp_file(self.draft.to_string().unwrap().as_str().as_bytes(), None); let f = create_temp_file(
self.draft.to_string().unwrap().as_str().as_bytes(),
None,
None,
);
//let mut f = Box::new(std::fs::File::create(&dir).unwrap()); //let mut f = Box::new(std::fs::File::create(&dir).unwrap());
// TODO: check exit status // TODO: check exit status

View File

@ -750,11 +750,15 @@ impl Component for MailView {
)); ));
return true; return true;
} }
ContentType::Unsupported { .. } => { ContentType::Other { ref name, .. } => {
let attachment_type = u.mime_type(); let attachment_type = u.mime_type();
let binary = query_default_app(&attachment_type); let binary = query_default_app(&attachment_type);
if let Ok(binary) = binary { if let Ok(binary) = binary {
let p = create_temp_file(&decode(u, None), None); let p = create_temp_file(
&decode(u, None),
name.as_ref().map(|n| n.clone()),
None,
);
Command::new(&binary) Command::new(&binary)
.arg(p.path()) .arg(p.path())
.stdin(Stdio::piped()) .stdin(Stdio::piped())
@ -766,14 +770,31 @@ impl Component for MailView {
context.temp_files.push(p); context.temp_files.push(p);
} else { } else {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(format!( StatusEvent::DisplayMessage(if name.is_some() {
"Couldn't find a default application for type {}", format!(
attachment_type "Couldn't find a default application for file {} (type {})",
)), name.as_ref().unwrap(), attachment_type
)); )
} else {
format!( "Couldn't find a default application for type {}", attachment_type)
}
,
)));
return true; return true;
} }
} }
ContentType::OctetStream { ref name } => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(
format!(
"Failed to open {}. application/octet-stream isn't supported yet",
name.as_ref().map(|n| n.as_str()).unwrap_or("file")
)
),
));
return true;
}
ContentType::PGPSignature => { ContentType::PGPSignature => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage( StatusEvent::DisplayMessage(

View File

@ -435,11 +435,15 @@ impl Component for EnvelopeView {
)); ));
return true; return true;
} }
ContentType::Unsupported { .. } => { ContentType::Other { ref name, .. } => {
let attachment_type = u.mime_type(); let attachment_type = u.mime_type();
let binary = query_default_app(&attachment_type); let binary = query_default_app(&attachment_type);
if let Ok(binary) = binary { if let Ok(binary) = binary {
let p = create_temp_file(&decode(u, None), None); let p = create_temp_file(
&decode(u, None),
name.as_ref().map(|n| n.clone()),
None,
);
Command::new(&binary) Command::new(&binary)
.arg(p.path()) .arg(p.path())
.stdin(Stdio::piped()) .stdin(Stdio::piped())
@ -459,6 +463,14 @@ impl Component for EnvelopeView {
return true; return true;
} }
} }
ContentType::OctetStream { .. } => {
context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage(
"application/octet-stream isn't supported yet".to_string(),
),
));
return true;
}
ContentType::PGPSignature => { ContentType::PGPSignature => {
context.replies.push_back(UIEvent::StatusEvent( context.replies.push_back(UIEvent::StatusEvent(
StatusEvent::DisplayMessage( StatusEvent::DisplayMessage(

View File

@ -132,7 +132,7 @@ impl Component for HtmlView {
// scripts) // scripts)
let binary = query_default_app("text/html"); let binary = query_default_app("text/html");
if let Ok(binary) = binary { if let Ok(binary) = binary {
let p = create_temp_file(&self.bytes, None); let p = create_temp_file(&self.bytes, None, None);
Command::new(&binary) Command::new(&binary)
.arg(p.path()) .arg(p.path())
.stdin(Stdio::piped()) .stdin(Stdio::piped())

View File

@ -63,10 +63,10 @@ impl File {
/// Returned `File` will be deleted when dropped, so make sure to add it on `context.temp_files` /// Returned `File` will be deleted when dropped, so make sure to add it on `context.temp_files`
/// to reap it later. /// to reap it later.
pub fn create_temp_file(bytes: &[u8], filename: Option<&PathBuf>) -> File { pub fn create_temp_file(bytes: &[u8], filename: Option<String>, path: Option<&PathBuf>) -> File {
let mut dir = std::env::temp_dir(); let mut dir = std::env::temp_dir();
let path = if let Some(p) = filename { let path = if let Some(p) = path {
p p
} else { } else {
dir.push("meli"); dir.push("meli");
@ -74,8 +74,12 @@ pub fn create_temp_file(bytes: &[u8], filename: Option<&PathBuf>) -> File {
.recursive(true) .recursive(true)
.create(&dir) .create(&dir)
.unwrap(); .unwrap();
let u = Uuid::new_v4(); if let Some(filename) = filename {
dir.push(u.hyphenated().to_string()); dir.push(filename)
} else {
let u = Uuid::new_v4();
dir.push(u.hyphenated().to_string());
}
&dir &dir
}; };