server: fix is_dmarc check

This commit is contained in:
Bill Thiede 2025-08-12 16:28:28 -07:00
parent 5c42d04598
commit 1f75627fd2

View File

@ -27,6 +27,8 @@ use crate::{
linkify_html, InlineStyle, Query, SanitizeHtml, Transformer,
};
const APPLICATION_GZIP: &'static str = "application/gzip";
const APPLICATION_ZIP: &'static str = "application/zip";
const IMAGE_JPEG: &'static str = "image/jpeg";
const IMAGE_PJPEG: &'static str = "image/pjpeg";
@ -351,9 +353,7 @@ pub async fn thread(
for i in 0..archive.len() {
if let Ok(mut file) = archive.by_index(i) {
let name = file.name().to_lowercase();
if name.ends_with(".xml")
&& (name.contains("dmarc") || name.starts_with("google.com!"))
{
if is_dmarc_report_filename(&name) {
let mut xml = String::new();
use std::io::Read;
if file.read_to_string(&mut xml).is_ok() {
@ -564,8 +564,6 @@ fn extract_body(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Body, Ser
ret
}
const APPLICATION_GZIP: &'static str = "application/gzip";
fn extract_zip(m: &ParsedMail) -> Result<Body, ServerError> {
if let Ok(zip_bytes) = m.get_body_raw() {
if let Ok(mut archive) = ZipArchive::new(Cursor::new(&zip_bytes)) {
@ -574,16 +572,14 @@ fn extract_zip(m: &ParsedMail) -> Result<Body, ServerError> {
let name = file.name().to_lowercase();
// Google DMARC reports are typically named like "google.com!example.com!...xml"
// and may or may not contain "dmarc" in the filename.
if name.ends_with(".xml")
&& (name.contains("dmarc") || name.starts_with("google.com!"))
{
if is_dmarc_report_filename(&name) {
let mut xml = String::new();
use std::io::Read;
if file.read_to_string(&mut xml).is_ok() {
match parse_dmarc_report(&xml) {
Ok(report) => {
return Ok(Body::html(format!(
"<div class=\"dmarc-report\">Google DMARC report summary:<br>{}</div>",
"<div class=\"dmarc-report\">DMARC report summary:<br>{}</div>",
report
)));
}
@ -605,23 +601,30 @@ fn extract_zip(m: &ParsedMail) -> Result<Body, ServerError> {
}
fn extract_gzip(m: &ParsedMail) -> Result<Body, ServerError> {
if let Ok(gz_bytes) = m.get_body_raw() {
let mut decoder = flate2::read::GzDecoder::new(&gz_bytes[..]);
let mut xml = String::new();
use std::io::Read;
if decoder.read_to_string(&mut xml).is_ok() {
match parse_dmarc_report(&xml) {
Ok(report) => {
return Ok(Body::html(format!(
"<div class=\"dmarc-report\">Microsoft DMARC report summary:<br>{}</div>",
report
)));
}
Err(e) => {
return Ok(Body::html(format!(
"<div class=\"dmarc-report-error\">Failed to parse DMARC report XML: {}</div>",
e
)));
let pcd = m.get_content_disposition();
let filename = pcd.params.get("filename").map(|s| s.to_lowercase());
let is_dmarc_xml_file = filename.map_or(false, |name| is_dmarc_report_filename(&name));
if is_dmarc_xml_file {
if let Ok(gz_bytes) = m.get_body_raw() {
let mut decoder = flate2::read::GzDecoder::new(&gz_bytes[..]);
let mut xml = String::new();
use std::io::Read;
if decoder.read_to_string(&mut xml).is_ok() {
match parse_dmarc_report(&xml) {
Ok(report) => {
return Ok(Body::html(format!(
"<div class=\"dmarc-report\">DMARC report summary:<br>{}</div>",
report
)));
}
Err(e) => {
return Ok(Body::html(format!(
"<div class=\"dmarc-report-error\">Failed to parse DMARC report XML: {}</div>",
e
)));
}
}
}
}
@ -752,6 +755,10 @@ fn extract_unhandled(m: &ParsedMail) -> Result<Body, ServerError> {
}))
}
fn is_dmarc_report_filename(name: &str) -> bool {
(name.ends_with(".xml.gz") || name.ends_with(".xml")) && name.contains("!")
}
// multipart/alternative defines multiple representations of the same message, and clients should
// show the fanciest they can display. For this program, the priority is text/html, text/plain,
// then give up.