server: properly filter inline vs attaments

This commit is contained in:
Bill Thiede 2024-04-06 08:34:26 -07:00
parent 7df11639ed
commit 1f5f10f78d
2 changed files with 36 additions and 26 deletions

View File

@ -75,7 +75,6 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for InlineAttachmentResponder {
fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> { fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> {
let mut resp = Response::build(); let mut resp = Response::build();
if let Some(filename) = self.0.filename { if let Some(filename) = self.0.filename {
info!("filename {:?}", filename);
resp.header(Header::new( resp.header(Header::new(
"Content-Disposition", "Content-Disposition",
format!(r#"inline; filename="{}""#, filename), format!(r#"inline; filename="{}""#, filename),
@ -97,7 +96,6 @@ impl<'r, 'o: 'r> Responder<'r, 'o> for DownloadAttachmentResponder {
fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> { fn respond_to(self, _: &'r Request<'_>) -> rocket::response::Result<'o> {
let mut resp = Response::build(); let mut resp = Response::build();
if let Some(filename) = self.0.filename { if let Some(filename) = self.0.filename {
info!("filename {:?}", filename);
resp.header(Header::new( resp.header(Header::new(
"Content-Disposition", "Content-Disposition",
format!(r#"attachment; filename="{}""#, filename), format!(r#"attachment; filename="{}""#, filename),

View File

@ -348,7 +348,10 @@ impl QueryRoot {
Body::Html(Html { Body::Html(Html {
html: format!( html: format!(
r#"<p class="view-part-text-plain">{}</p>"#, r#"<p class="view-part-text-plain">{}</p>"#,
sanitize_html(&linkify_html(&text))? // Trim newlines to prevent excessive white space at the beginning/end of
// presenation. Leave tabs and spaces incase plain text attempts to center a
// header on the first line.
sanitize_html(&linkify_html(&text.trim_matches('\n')))?
), ),
content_tree: if debug_content_tree { content_tree: if debug_content_tree {
render_content_type_tree(&m) render_content_type_tree(&m)
@ -584,23 +587,25 @@ fn extract_mixed(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Body, Er
TEXT_PLAIN => parts.push(Body::text(sp.get_body()?)), TEXT_PLAIN => parts.push(Body::text(sp.get_body()?)),
TEXT_HTML => parts.push(Body::html(sp.get_body()?)), TEXT_HTML => parts.push(Body::html(sp.get_body()?)),
IMAGE_JPEG | IMAGE_PNG => { IMAGE_JPEG | IMAGE_PNG => {
let filename = { let pcd = sp.get_content_disposition();
let pcd = sp.get_content_disposition(); let filename = pcd
pcd.params .params
.get("filename") .get("filename")
.map(|s| s.clone()) .map(|s| s.clone())
.unwrap_or("".to_string()) .unwrap_or("".to_string());
}; // Only add inline images, attachments are handled as an attribute of the top level Message and rendered separate client-side.
parts.push(Body::html(format!( if pcd.disposition == mailparse::DispositionType::Inline {
r#"<img src="/view/attachment/{}/{}/{filename}">"#, parts.push(Body::html(format!(
part_addr[0], r#"<img src="/view/attachment/{}/{}/{filename}">"#,
part_addr part_addr[0],
.iter() part_addr
.skip(1) .iter()
.map(|i| i.to_string()) .skip(1)
.collect::<Vec<_>>() .map(|i| i.to_string())
.join(".") .collect::<Vec<_>>()
))) .join(".")
)));
}
} }
_ => (), _ => (),
} }
@ -613,10 +618,15 @@ fn flatten_body_parts(parts: &[Body]) -> Body {
let html = parts let html = parts
.iter() .iter()
.map(|p| match p { .map(|p| match p {
Body::PlainText(PlainText { text, .. }) => format!( Body::PlainText(PlainText { text, .. }) => {
r#"<p class="view-part-text-plain">{}</p>"#, format!(
linkify_html(&text) r#"<p class="view-part-text-plain">{}</p>"#,
), // Trim newlines to prevent excessive white space at the beginning/end of
// presenation. Leave tabs and spaces incase plain text attempts to center a
// header on the first line.
linkify_html(&text.trim_matches('\n'))
)
}
Body::Html(Html { html, .. }) => html.clone(), Body::Html(Html { html, .. }) => html.clone(),
Body::UnhandledContentType(UnhandledContentType { text }) => { Body::UnhandledContentType(UnhandledContentType { text }) => {
format!(r#"<p class="view-part-unhandled">{text}</p>"#) format!(r#"<p class="view-part-unhandled">{text}</p>"#)
@ -683,7 +693,10 @@ fn extract_attachments(m: &ParsedMail, id: &str) -> Result<Vec<Attachment>, Erro
let mut attachments = Vec::new(); let mut attachments = Vec::new();
for (idx, sp) in m.subparts.iter().enumerate() { for (idx, sp) in m.subparts.iter().enumerate() {
if let Some(attachment) = extract_attachment(sp, id, &[idx]) { if let Some(attachment) = extract_attachment(sp, id, &[idx]) {
attachments.push(attachment); // Filter out inline attachements, they're flattened into the body of the message.
if attachment.disposition == DispositionType::Attachment {
attachments.push(attachment);
}
} }
} }
Ok(attachments) Ok(attachments)
@ -865,7 +878,6 @@ pub fn attachment_bytes(nm: &Notmuch, id: &str, idx: &[usize]) -> Result<Attachm
let mmap = unsafe { MmapOptions::new().map(&file)? }; let mmap = unsafe { MmapOptions::new().map(&file)? };
let m = parse_mail(&mmap)?; let m = parse_mail(&mmap)?;
if let Some(attachment) = walk_attachments(&m, |sp, cur_idx| { if let Some(attachment) = walk_attachments(&m, |sp, cur_idx| {
info!("checking {cur_idx:?}=={idx:?}");
if cur_idx == idx { if cur_idx == idx {
let attachment = extract_attachment(&sp, id, idx).unwrap_or(Attachment { let attachment = extract_attachment(&sp, id, idx).unwrap_or(Attachment {
..Attachment::default() ..Attachment::default()