diff options
-rw-r--r-- | melib/src/email/attachment_types.rs | 18 | ||||
-rw-r--r-- | melib/src/email/attachments.rs | 69 | ||||
-rw-r--r-- | ui/src/components/mail/compose.rs | 6 | ||||
-rw-r--r-- | ui/src/components/mail/view.rs | 35 | ||||
-rw-r--r-- | ui/src/components/mail/view/envelope.rs | 16 | ||||
-rw-r--r-- | ui/src/components/mail/view/html.rs | 2 | ||||
-rw-r--r-- | ui/src/types/helpers.rs | 12 |
7 files changed, 128 insertions, 30 deletions
diff --git a/melib/src/email/attachment_types.rs b/melib/src/email/attachment_types.rs index 6e280253..9a0d0066 100644 --- a/melib/src/email/attachment_types.rs +++ b/melib/src/email/attachment_types.rs @@ -127,8 +127,12 @@ pub enum ContentType { }, MessageRfc822, PGPSignature, - Unsupported { + Other { tag: Vec<u8>, + name: Option<String>, + }, + OctetStream { + name: Option<String>, }, } @@ -146,9 +150,10 @@ impl Display for ContentType { match self { ContentType::Text { kind: t, .. } => t.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::MessageRfc822 => write!(f, "message/rfc822"), + ContentType::OctetStream { .. } => write!(f, "application/octet-stream"), } } } @@ -161,6 +166,7 @@ impl ContentType { false } } + pub fn is_text_html(&self) -> bool { if let ContentType::Text { kind: Text::Html, .. @@ -171,6 +177,14 @@ impl ContentType { 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)] diff --git a/melib/src/email/attachments.rs b/melib/src/email/attachments.rs index 1a5352e0..e2d3dad2 100644 --- a/melib/src/email/attachments.rs +++ b/melib/src/email/attachments.rs @@ -27,13 +27,14 @@ use data_encoding::BASE64_MIME; pub use crate::email::attachment_types::*; -#[derive(Default, PartialEq)] +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct AttachmentBuilder { - content_type: ContentType, - content_transfer_encoding: ContentTransferEncoding, + pub content_type: ContentType, + pub content_transfer_encoding: ContentTransferEncoding, - raw: Vec<u8>, + pub raw: Vec<u8>, } + impl AttachmentBuilder { pub fn new(content: &[u8]) -> Self { 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 { self.content_type = val; self @@ -120,15 +130,26 @@ impl AttachmentBuilder { { self.content_type = ContentType::PGPSignature; } 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); tag.extend(ct); tag.push(b'/'); tag.extend(cst); - self.content_type = ContentType::Unsupported { tag }; + self.content_type = ContentType::Other { tag, name }; } } - Err(v) => { - debug!("parsing error in content_type: {:?} {:?}", value, v); + Err(e) => { + debug!( + "parsing error in content_type: {:?} {:?}", + String::from_utf8_lossy(value), + e + ); } } 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. #[derive(Clone, Serialize, Deserialize, PartialEq)] pub struct Attachment { - content_type: ContentType, - content_transfer_encoding: ContentTransferEncoding, + pub(in crate::email) content_type: ContentType, + pub(in crate::email) content_transfer_encoding: ContentTransferEncoding, - raw: Vec<u8>, + pub(in crate::email) raw: Vec<u8>, } impl fmt::Debug for Attachment { @@ -225,9 +261,10 @@ impl fmt::Display for Attachment { Err(e) => write!(f, "{}", e), }, ContentType::PGPSignature => write!(f, "pgp signature {}", self.mime_type()), - ContentType::Unsupported { .. } => { - write!(f, "Data attachment of type {}", self.mime_type()) + ContentType::OctetStream { ref name } => { + 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::Multipart { subattachments: ref sub_att_vec, @@ -258,6 +295,7 @@ impl Attachment { pub fn raw(&self) -> &[u8] { &self.raw } + fn get_text_recursive(&self, text: &mut Vec<u8>) { match self.content_type { 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> { match a.content_type { - ContentType::Unsupported { .. } => Vec::new(), + ContentType::Other { .. } => Vec::new(), 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::MessageRfc822 => { let temp = decode_rfc822(&a.raw); diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index b3db659e..a0135848 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -551,7 +551,11 @@ impl Component for Composer { } /* update Draft's headers based on form values */ 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()); // TODO: check exit status diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 621ca246..a4f2cfb2 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -750,11 +750,15 @@ impl Component for MailView { )); return true; } - ContentType::Unsupported { .. } => { + ContentType::Other { ref name, .. } => { let attachment_type = u.mime_type(); let binary = query_default_app(&attachment_type); 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) .arg(p.path()) .stdin(Stdio::piped()) @@ -766,14 +770,31 @@ impl Component for MailView { context.temp_files.push(p); } else { context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(format!( - "Couldn't find a default application for type {}", - attachment_type - )), - )); + StatusEvent::DisplayMessage(if name.is_some() { + format!( + "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; } } + 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 => { context.replies.push_back(UIEvent::StatusEvent( StatusEvent::DisplayMessage( diff --git a/ui/src/components/mail/view/envelope.rs b/ui/src/components/mail/view/envelope.rs index 532f2756..a6c4bbd0 100644 --- a/ui/src/components/mail/view/envelope.rs +++ b/ui/src/components/mail/view/envelope.rs @@ -435,11 +435,15 @@ impl Component for EnvelopeView { )); return true; } - ContentType::Unsupported { .. } => { + ContentType::Other { ref name, .. } => { let attachment_type = u.mime_type(); let binary = query_default_app(&attachment_type); 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) .arg(p.path()) .stdin(Stdio::piped()) @@ -459,6 +463,14 @@ impl Component for EnvelopeView { 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 => { context.replies.push_back(UIEvent::StatusEvent( StatusEvent::DisplayMessage( diff --git a/ui/src/components/mail/view/html.rs b/ui/src/components/mail/view/html.rs index 1b80c7ac..076db1ed 100644 --- a/ui/src/components/mail/view/html.rs +++ b/ui/src/components/mail/view/html.rs @@ -132,7 +132,7 @@ impl Component for HtmlView { // scripts) let binary = query_default_app("text/html"); 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) .arg(p.path()) .stdin(Stdio::piped()) diff --git a/ui/src/types/helpers.rs b/ui/src/types/helpers.rs index 5de577d0..fa82c10c 100644 --- a/ui/src/types/helpers.rs +++ b/ui/src/types/helpers.rs @@ -63,10 +63,10 @@ impl File { /// Returned `File` will be deleted when dropped, so make sure to add it on `context.temp_files` /// 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 path = if let Some(p) = filename { + let path = if let Some(p) = path { p } else { dir.push("meli"); @@ -74,8 +74,12 @@ pub fn create_temp_file(bytes: &[u8], filename: Option<&PathBuf>) -> File { .recursive(true) .create(&dir) .unwrap(); - let u = Uuid::new_v4(); - dir.push(u.hyphenated().to_string()); + if let Some(filename) = filename { + dir.push(filename) + } else { + let u = Uuid::new_v4(); + dir.push(u.hyphenated().to_string()); + } &dir }; |