From 21789df60a31bacfec4c0868aa0ae3e7d86832db Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Wed, 13 Nov 2024 17:42:53 -0800 Subject: [PATCH] server: handle attachements with name in content-type not disposition --- server/src/nm.rs | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/server/src/nm.rs b/server/src/nm.rs index dc444d0..b8a35b6 100644 --- a/server/src/nm.rs +++ b/server/src/nm.rs @@ -6,7 +6,7 @@ use std::{ }; use log::{error, info, warn}; -use mailparse::{parse_mail, MailHeader, MailHeaderMap, ParsedMail}; +use mailparse::{parse_content_type, parse_mail, MailHeader, MailHeaderMap, ParsedMail}; use memmap::MmapOptions; use notmuch::Notmuch; @@ -451,7 +451,7 @@ fn extract_alternative(m: &ParsedMail, part_addr: &mut Vec) -> Result) -> Result) -> Result { + //todo!("add some sort of visual indicator there are unhandled types, i.e. .ics files"); let handled_types = vec![ IMAGE_JPEG, IMAGE_PJPEG, @@ -532,13 +533,25 @@ fn extract_mixed(m: &ParsedMail, part_addr: &mut Vec) -> Result (), + mt => parts.push(unhandled_html(MULTIPART_MIXED, mt)), } part_addr.pop(); } Ok(flatten_body_parts(&parts)) } +fn unhandled_html(parent_type: &str, child_type: &str) -> Body { + Body::Html(Html { + html: format!( + r#" +
+Unhandled mimetype {child_type} in a {parent_type} message +
+ "# + ), + content_tree: String::new(), + }) +} fn flatten_body_parts(parts: &[Body]) -> Body { let html = parts .iter() @@ -663,6 +676,7 @@ fn walk_attachments_inner Option + Copy>( fn extract_attachments(m: &ParsedMail, id: &str) -> Result, ServerError> { let mut attachments = Vec::new(); for (idx, sp) in m.subparts.iter().enumerate() { + info!("sp: {:?}", sp.headers); if let Some(attachment) = extract_attachment(sp, id, &[idx]) { // Filter out inline attachements, they're flattened into the body of the message. if attachment.disposition == DispositionType::Attachment { @@ -675,11 +689,22 @@ fn extract_attachments(m: &ParsedMail, id: &str) -> Result, Serv fn extract_attachment(m: &ParsedMail, id: &str, idx: &[usize]) -> Option { let pcd = m.get_content_disposition(); - // TODO: do we need to handle empty filename attachments, or should we change the definition of - // Attachment::filename? - let Some(filename) = pcd.params.get("filename").map(|f| f.clone()) else { - return None; + let pct = m + .get_headers() + .get_first_value("Content-Type") + .map(|s| parse_content_type(&s)); + let filename = match ( + pcd.params.get("filename").map(|f| f.clone()), + pct.map(|pct| pct.params.get("name").map(|f| f.clone())), + ) { + // Use filename from Content-Disposition + (Some(filename), _) => filename, + // Use filename from Content-Type + (_, Some(Some(name))) => name, + // No known filename, assume it's not an attachment + _ => return None, }; + info!("filename {filename}"); // TODO: grab this from somewhere let content_id = None;