melib: add Other and OctetStream content types
parent
c17bb24f0d
commit
d73069bc80
|
@ -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)]
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue