server: handle attachements with name in content-type not disposition
This commit is contained in:
parent
584ff1504d
commit
21789df60a
@ -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<String>) -> Result<Bo
|
||||
}
|
||||
for sp in &m.subparts {
|
||||
if sp.ctype.mimetype.as_str() == MULTIPART_MIXED {
|
||||
return extract_related(sp, part_addr);
|
||||
return extract_mixed(sp, part_addr);
|
||||
}
|
||||
}
|
||||
for sp in &m.subparts {
|
||||
@ -480,6 +480,7 @@ fn extract_alternative(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Bo
|
||||
// multipart/mixed defines multiple types of context all of which should be presented to the user
|
||||
// 'serially'.
|
||||
fn extract_mixed(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Body, ServerError> {
|
||||
//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<String>) -> Result<Body, Se
|
||||
)));
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
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#"
|
||||
<div class="p-4 error">
|
||||
Unhandled mimetype {child_type} in a {parent_type} message
|
||||
</div>
|
||||
"#
|
||||
),
|
||||
content_tree: String::new(),
|
||||
})
|
||||
}
|
||||
fn flatten_body_parts(parts: &[Body]) -> Body {
|
||||
let html = parts
|
||||
.iter()
|
||||
@ -663,6 +676,7 @@ fn walk_attachments_inner<T, F: Fn(&ParsedMail, &[usize]) -> Option<T> + Copy>(
|
||||
fn extract_attachments(m: &ParsedMail, id: &str) -> Result<Vec<Attachment>, 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<Vec<Attachment>, Serv
|
||||
|
||||
fn extract_attachment(m: &ParsedMail, id: &str, idx: &[usize]) -> Option<Attachment> {
|
||||
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user