summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--melib/src/email/attachment_types.rs18
-rw-r--r--melib/src/email/attachments.rs69
-rw-r--r--ui/src/components/mail/compose.rs6
-rw-r--r--ui/src/components/mail/view.rs35
-rw-r--r--ui/src/components/mail/view/envelope.rs16
-rw-r--r--ui/src/components/mail/view/html.rs2
-rw-r--r--ui/src/types/helpers.rs12
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
};