Compare commits

...

36 Commits

Author SHA1 Message Date
17ad5b3b0b chore: Release
All checks were successful
Continuous integration / Check (push) Successful in 1m47s
Continuous integration / Test Suite (push) Successful in 2m58s
Continuous integration / Trunk (push) Successful in 1m31s
Continuous integration / Rustfmt (push) Successful in 42s
Continuous integration / build (push) Successful in 3m18s
Continuous integration / Disallow unused dependencies (push) Successful in 3m4s
2025-10-20 19:50:27 -07:00
285b2f1591 server: handle message/delivery-status 2025-10-20 19:50:01 -07:00
1537333e76 server: add handling for multipart/report w/ ract_rfc822 subpart 2025-10-20 19:50:01 -07:00
285ff1d098 Merge pull request 'chore(deps): lock file maintenance' (#180) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 2m3s
Continuous integration / Test Suite (push) Successful in 3m1s
Continuous integration / Trunk (push) Successful in 8m7s
Continuous integration / Rustfmt (push) Successful in 42s
Continuous integration / build (push) Successful in 3m13s
Continuous integration / Disallow unused dependencies (push) Successful in 2m28s
2025-10-19 18:02:57 -07:00
1563bf05a3 chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m42s
Continuous integration / Test Suite (push) Successful in 6m17s
Continuous integration / Trunk (push) Successful in 7m54s
Continuous integration / Rustfmt (push) Successful in 50s
Continuous integration / build (push) Successful in 5m58s
Continuous integration / Disallow unused dependencies (push) Successful in 2m26s
2025-10-20 00:02:39 +00:00
458aab3167 Merge pull request 'chore(deps): lock file maintenance' (#179) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 1m40s
Continuous integration / Test Suite (push) Successful in 3m51s
Continuous integration / Trunk (push) Successful in 9m4s
Continuous integration / Rustfmt (push) Successful in 46s
Continuous integration / build (push) Successful in 4m7s
Continuous integration / Disallow unused dependencies (push) Successful in 2m41s
2025-10-12 18:02:38 -07:00
492e420337 chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m20s
Continuous integration / Test Suite (push) Successful in 6m20s
Continuous integration / Trunk (push) Successful in 8m17s
Continuous integration / Rustfmt (push) Successful in 41s
Continuous integration / build (push) Successful in 6m8s
Continuous integration / Disallow unused dependencies (push) Successful in 2m44s
2025-10-13 00:02:43 +00:00
330f9b1763 Merge pull request 'chore(deps): lock file maintenance' (#177) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 1m29s
Continuous integration / Test Suite (push) Successful in 2m33s
Continuous integration / Trunk (push) Successful in 8m10s
Continuous integration / Rustfmt (push) Successful in 42s
Continuous integration / build (push) Successful in 3m30s
Continuous integration / Disallow unused dependencies (push) Successful in 2m32s
2025-10-05 17:47:37 -07:00
ad904ac1c0 chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m0s
Continuous integration / Test Suite (push) Successful in 3m53s
Continuous integration / Trunk (push) Successful in 1m39s
Continuous integration / Rustfmt (push) Successful in 51s
Continuous integration / build (push) Successful in 4m35s
Continuous integration / Disallow unused dependencies (push) Successful in 2m23s
2025-10-06 00:02:48 +00:00
20f125bda5 Merge pull request 'chore(deps): update rust crate axum to v0.8.6' (#176) from renovate/axum-monorepo into master
All checks were successful
Continuous integration / Check (push) Successful in 1m36s
Continuous integration / Test Suite (push) Successful in 2m37s
Continuous integration / Trunk (push) Successful in 1m23s
Continuous integration / Rustfmt (push) Successful in 1m15s
Continuous integration / build (push) Successful in 3m40s
Continuous integration / Disallow unused dependencies (push) Successful in 2m16s
2025-09-30 06:46:31 -07:00
cf99e75ab8 chore(deps): update rust crate axum to v0.8.6
All checks were successful
Continuous integration / Check (push) Successful in 1m47s
Continuous integration / Test Suite (push) Successful in 3m37s
Continuous integration / Trunk (push) Successful in 8m1s
Continuous integration / Rustfmt (push) Successful in 48s
Continuous integration / build (push) Successful in 3m40s
Continuous integration / Disallow unused dependencies (push) Successful in 2m39s
2025-09-30 13:16:34 +00:00
54fc1e7962 Merge pull request 'chore(deps): lock file maintenance' (#175) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 1m27s
Continuous integration / Test Suite (push) Successful in 2m54s
Continuous integration / Trunk (push) Successful in 1m18s
Continuous integration / Rustfmt (push) Successful in 51s
Continuous integration / build (push) Successful in 2m52s
Continuous integration / Disallow unused dependencies (push) Successful in 2m42s
2025-09-28 21:02:01 -07:00
b187edc23b chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m4s
Continuous integration / Test Suite (push) Successful in 4m59s
Continuous integration / Trunk (push) Successful in 2m8s
Continuous integration / Rustfmt (push) Successful in 1m1s
Continuous integration / build (push) Successful in 5m7s
Continuous integration / Disallow unused dependencies (push) Successful in 2m32s
2025-09-29 01:33:22 +00:00
fdafba3eeb Merge pull request 'chore(deps): lock file maintenance' (#174) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 1m25s
Continuous integration / Test Suite (push) Successful in 2m48s
Continuous integration / Trunk (push) Successful in 8m45s
Continuous integration / Rustfmt (push) Successful in 48s
Continuous integration / build (push) Successful in 3m1s
Continuous integration / Disallow unused dependencies (push) Successful in 2m32s
2025-09-28 18:32:44 -07:00
c5fe9f67d2 chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m2s
Continuous integration / Test Suite (push) Successful in 4m21s
Continuous integration / Trunk (push) Successful in 1m58s
Continuous integration / Rustfmt (push) Successful in 41s
Continuous integration / build (push) Successful in 4m47s
Continuous integration / Disallow unused dependencies (push) Successful in 2m19s
2025-09-29 00:02:59 +00:00
ff970acf79 Merge pull request 'chore(deps): update rust crate axum to v0.8.5' (#173) from renovate/axum-monorepo into master
All checks were successful
Continuous integration / Check (push) Successful in 1m18s
Continuous integration / Test Suite (push) Successful in 2m1s
Continuous integration / Trunk (push) Successful in 1m22s
Continuous integration / Rustfmt (push) Successful in 38s
Continuous integration / build (push) Successful in 1m57s
Continuous integration / Disallow unused dependencies (push) Successful in 2m42s
2025-09-28 13:01:36 -07:00
2f9bc17873 chore(deps): update rust crate axum to v0.8.5
All checks were successful
Continuous integration / Check (push) Successful in 2m4s
Continuous integration / Test Suite (push) Successful in 3m30s
Continuous integration / Trunk (push) Successful in 7m57s
Continuous integration / Rustfmt (push) Successful in 54s
Continuous integration / build (push) Successful in 4m12s
Continuous integration / Disallow unused dependencies (push) Successful in 2m36s
2025-09-28 19:31:55 +00:00
7e82f4ce97 Merge pull request 'chore(deps): update rust crate serde to v1.0.228' (#172) from renovate/serde-monorepo into master
All checks were successful
Continuous integration / Check (push) Successful in 1m17s
Continuous integration / Test Suite (push) Successful in 1m47s
Continuous integration / Trunk (push) Successful in 1m28s
Continuous integration / Rustfmt (push) Successful in 1m17s
Continuous integration / build (push) Successful in 2m15s
Continuous integration / Disallow unused dependencies (push) Successful in 2m17s
2025-09-27 11:17:19 -07:00
5bb4f010d3 chore(deps): update rust crate serde to v1.0.228
All checks were successful
Continuous integration / Check (push) Successful in 1m39s
Continuous integration / Test Suite (push) Successful in 3m33s
Continuous integration / Trunk (push) Successful in 8m35s
Continuous integration / Rustfmt (push) Successful in 41s
Continuous integration / build (push) Successful in 3m40s
Continuous integration / Disallow unused dependencies (push) Successful in 2m32s
2025-09-27 17:32:25 +00:00
0af630acbe Merge pull request 'chore(deps): update rust crate serde to v1.0.227' (#171) from renovate/serde-monorepo into master
All checks were successful
Continuous integration / Check (push) Successful in 1m2s
Continuous integration / Test Suite (push) Successful in 1m30s
Continuous integration / Trunk (push) Successful in 1m22s
Continuous integration / Rustfmt (push) Successful in 1m9s
Continuous integration / build (push) Successful in 1m55s
Continuous integration / Disallow unused dependencies (push) Successful in 2m41s
2025-09-25 17:48:24 -07:00
d3d350e159 chore(deps): update rust crate serde to v1.0.227
All checks were successful
Continuous integration / Check (push) Successful in 1m45s
Continuous integration / Test Suite (push) Successful in 3m17s
Continuous integration / Trunk (push) Successful in 1m11s
Continuous integration / Rustfmt (push) Successful in 51s
Continuous integration / build (push) Successful in 3m31s
Continuous integration / Disallow unused dependencies (push) Successful in 3m35s
2025-09-26 00:17:20 +00:00
4013e4a7bf Merge pull request 'chore(deps): lock file maintenance' (#170) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 1m24s
Continuous integration / Test Suite (push) Successful in 1m56s
Continuous integration / Trunk (push) Successful in 1m47s
Continuous integration / Rustfmt (push) Successful in 54s
Continuous integration / build (push) Successful in 2m47s
Continuous integration / Disallow unused dependencies (push) Successful in 2m48s
2025-09-21 19:47:12 -07:00
b63171ea98 chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 1m26s
Continuous integration / Test Suite (push) Successful in 2m9s
Continuous integration / Trunk (push) Successful in 1m56s
Continuous integration / Rustfmt (push) Successful in 1m21s
Continuous integration / build (push) Successful in 2m8s
Continuous integration / Disallow unused dependencies (push) Successful in 2m38s
2025-09-22 01:03:58 +00:00
1c6ef02d11 Merge pull request 'chore(deps): lock file maintenance' (#169) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 1m48s
Continuous integration / Test Suite (push) Successful in 2m35s
Continuous integration / Trunk (push) Successful in 8m22s
Continuous integration / Rustfmt (push) Successful in 53s
Continuous integration / build (push) Successful in 2m4s
Continuous integration / Disallow unused dependencies (push) Successful in 2m43s
2025-09-21 18:03:09 -07:00
32e5837dbf chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m10s
Continuous integration / Test Suite (push) Successful in 4m11s
Continuous integration / Trunk (push) Successful in 8m17s
Continuous integration / Rustfmt (push) Successful in 58s
Continuous integration / build (push) Successful in 4m10s
Continuous integration / Disallow unused dependencies (push) Successful in 2m49s
2025-09-22 00:02:29 +00:00
38234d4d18 Merge pull request 'chore(deps): update rust crate serde to v1.0.226' (#168) from renovate/serde-monorepo into master
All checks were successful
Continuous integration / Check (push) Successful in 1m3s
Continuous integration / Test Suite (push) Successful in 1m31s
Continuous integration / Trunk (push) Successful in 1m10s
Continuous integration / Rustfmt (push) Successful in 48s
Continuous integration / build (push) Successful in 1m56s
Continuous integration / Disallow unused dependencies (push) Successful in 2m41s
2025-09-20 17:17:26 -07:00
f609a3c122 chore(deps): update rust crate serde to v1.0.226
All checks were successful
Continuous integration / Check (push) Successful in 1m39s
Continuous integration / Test Suite (push) Successful in 3m41s
Continuous integration / Trunk (push) Successful in 8m13s
Continuous integration / Rustfmt (push) Successful in 48s
Continuous integration / build (push) Successful in 3m29s
Continuous integration / Disallow unused dependencies (push) Successful in 2m28s
2025-09-20 23:47:09 +00:00
440a630414 Merge pull request 'chore(deps): update rust crate serde to v1.0.225' (#167) from renovate/serde-monorepo into master
All checks were successful
Continuous integration / Check (push) Successful in 1m13s
Continuous integration / Test Suite (push) Successful in 1m27s
Continuous integration / Trunk (push) Successful in 1m11s
Continuous integration / Rustfmt (push) Successful in 1m9s
Continuous integration / build (push) Successful in 2m8s
Continuous integration / Disallow unused dependencies (push) Successful in 3m0s
2025-09-15 21:46:51 -07:00
ebda258750 chore(deps): update rust crate serde to v1.0.225
All checks were successful
Continuous integration / Check (push) Successful in 1m53s
Continuous integration / Test Suite (push) Successful in 4m12s
Continuous integration / Trunk (push) Successful in 8m40s
Continuous integration / Rustfmt (push) Successful in 50s
Continuous integration / build (push) Successful in 4m40s
Continuous integration / Disallow unused dependencies (push) Successful in 2m38s
2025-09-16 04:02:50 +00:00
f766b3d529 Merge pull request 'chore(deps): update rust crate serde to v1.0.224' (#166) from renovate/serde-monorepo into master
All checks were successful
Continuous integration / Check (push) Successful in 1m23s
Continuous integration / Test Suite (push) Successful in 1m26s
Continuous integration / Trunk (push) Successful in 1m13s
Continuous integration / Rustfmt (push) Successful in 1m0s
Continuous integration / build (push) Successful in 2m14s
Continuous integration / Disallow unused dependencies (push) Successful in 2m40s
2025-09-15 10:17:33 -07:00
96d927d416 chore(deps): update rust crate serde to v1.0.224
All checks were successful
Continuous integration / Check (push) Successful in 2m51s
Continuous integration / Test Suite (push) Successful in 4m43s
Continuous integration / Trunk (push) Successful in 8m0s
Continuous integration / Rustfmt (push) Successful in 44s
Continuous integration / build (push) Successful in 4m47s
Continuous integration / Disallow unused dependencies (push) Successful in 2m25s
2025-09-15 16:17:25 +00:00
60543b7e5d Merge pull request 'chore(deps): lock file maintenance' (#165) from renovate/lock-file-maintenance into master
All checks were successful
Continuous integration / Check (push) Successful in 2m6s
Continuous integration / Test Suite (push) Successful in 4m26s
Continuous integration / Trunk (push) Successful in 7m26s
Continuous integration / Rustfmt (push) Successful in 33s
Continuous integration / build (push) Successful in 4m19s
Continuous integration / Disallow unused dependencies (push) Successful in 2m22s
2025-09-14 19:16:51 -07:00
97a7bb6083 chore(deps): lock file maintenance
All checks were successful
Continuous integration / Check (push) Successful in 2m6s
Continuous integration / Test Suite (push) Successful in 4m15s
Continuous integration / Trunk (push) Successful in 7m8s
Continuous integration / Rustfmt (push) Successful in 33s
Continuous integration / build (push) Successful in 4m52s
Continuous integration / Disallow unused dependencies (push) Successful in 2m7s
2025-09-15 00:02:42 +00:00
c493857188 web: remove wasm-bindgen-test
All checks were successful
Continuous integration / Check (push) Successful in 1m55s
Continuous integration / Test Suite (push) Successful in 4m4s
Continuous integration / Trunk (push) Successful in 7m18s
Continuous integration / Rustfmt (push) Successful in 28s
Continuous integration / build (push) Successful in 4m26s
Continuous integration / Disallow unused dependencies (push) Successful in 2m2s
2025-09-14 15:25:15 -07:00
21f344b01c Merge pull request 'chore(deps): update rust crate serde to v1.0.223' (#164) from renovate/serde-monorepo into master
Some checks failed
Continuous integration / Check (push) Has been cancelled
Continuous integration / Test Suite (push) Has been cancelled
Continuous integration / Trunk (push) Has been cancelled
Continuous integration / Rustfmt (push) Has been cancelled
Continuous integration / build (push) Has been cancelled
Continuous integration / Disallow unused dependencies (push) Has been cancelled
2025-09-14 15:16:51 -07:00
78f6d87c03 chore(deps): update rust crate serde to v1.0.223
All checks were successful
Continuous integration / Check (push) Successful in 2m2s
Continuous integration / Test Suite (push) Successful in 4m11s
Continuous integration / Trunk (push) Successful in 7m26s
Continuous integration / Rustfmt (push) Successful in 33s
Continuous integration / build (push) Successful in 4m21s
Continuous integration / Disallow unused dependencies (push) Successful in 2m5s
2025-09-14 20:47:14 +00:00
6 changed files with 727 additions and 689 deletions

1137
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ authors = ["Bill Thiede <git@xinu.tv>"]
edition = "2021" edition = "2021"
license = "UNLICENSED" license = "UNLICENSED"
publish = ["xinu"] publish = ["xinu"]
version = "0.17.44" version = "0.17.45"
repository = "https://git.z.xinu.tv/wathiede/letterbox" repository = "https://git.z.xinu.tv/wathiede/letterbox"
[profile.dev] [profile.dev]

View File

@@ -32,8 +32,8 @@ futures = "0.3.31"
headers = "0.4.0" headers = "0.4.0"
html-escape = "0.2.13" html-escape = "0.2.13"
ical = "0.11" ical = "0.11"
letterbox-notmuch = { path = "../notmuch", version = "0.17.44", registry = "xinu" } letterbox-notmuch = { path = "../notmuch", version = "0.17.45", registry = "xinu" }
letterbox-shared = { path = "../shared", version = "0.17.44", registry = "xinu" } letterbox-shared = { path = "../shared", version = "0.17.45", registry = "xinu" }
linkify = "0.10.0" linkify = "0.10.0"
lol_html = "2.3.0" lol_html = "2.3.0"
mailparse = "0.16.1" mailparse = "0.16.1"

View File

@@ -17,9 +17,11 @@ use crate::{
const APPLICATION_GZIP: &'static str = "application/gzip"; const APPLICATION_GZIP: &'static str = "application/gzip";
const APPLICATION_ZIP: &'static str = "application/zip"; const APPLICATION_ZIP: &'static str = "application/zip";
const APPLICATION_TLSRPT_GZIP: &'static str = "application/tlsrpt+gzip";
const IMAGE_JPEG: &'static str = "image/jpeg"; const IMAGE_JPEG: &'static str = "image/jpeg";
const IMAGE_PJPEG: &'static str = "image/pjpeg"; const IMAGE_PJPEG: &'static str = "image/pjpeg";
const IMAGE_PNG: &'static str = "image/png"; const IMAGE_PNG: &'static str = "image/png";
const MESSAGE_DELIVERY_STATUS: &'static str = "message/delivery-status";
const MESSAGE_RFC822: &'static str = "message/rfc822"; const MESSAGE_RFC822: &'static str = "message/rfc822";
const MULTIPART_ALTERNATIVE: &'static str = "multipart/alternative"; const MULTIPART_ALTERNATIVE: &'static str = "multipart/alternative";
const MULTIPART_MIXED: &'static str = "multipart/mixed"; const MULTIPART_MIXED: &'static str = "multipart/mixed";
@@ -641,115 +643,186 @@ pub fn extract_gzip(m: &ParsedMail) -> Result<(Body, Option<String>), ServerErro
Ok((extract_unhandled(m)?, None)) Ok((extract_unhandled(m)?, None))
} }
pub fn extract_report(m: &ParsedMail, _part_addr: &mut Vec<String>) -> Result<Body, ServerError> { pub fn extract_report(m: &ParsedMail, part_addr: &mut Vec<String>) -> Result<Body, ServerError> {
let mut html_part = None; let mut parts = Vec::new();
let mut tlsrpt_part = None;
for (idx, sp) in m.subparts.iter().enumerate() {
part_addr.push(idx.to_string());
for sp in &m.subparts {
match sp.ctype.mimetype.as_str() { match sp.ctype.mimetype.as_str() {
TEXT_HTML => html_part = Some(sp.get_body()?), APPLICATION_TLSRPT_GZIP => {
"application/tlsrpt+gzip" => tlsrpt_part = Some(sp.get_body_raw()?), let gz_bytes = sp.get_body_raw()?;
_ => {} // Ignore other parts for now let mut decoder = flate2::read::GzDecoder::new(&gz_bytes[..]);
} let mut buffer = Vec::new();
} if decoder.read_to_end(&mut buffer).is_ok() {
if let Ok(json_str) = String::from_utf8(buffer) {
let tlsrpt_summary_html = if let Some(gz_bytes) = tlsrpt_part { match serde_json::from_str::<TlsRpt>(&json_str) {
let mut decoder = flate2::read::GzDecoder::new(&gz_bytes[..]); Ok(tlsrpt) => {
let mut buffer = Vec::new(); let formatted_tlsrpt = FormattedTlsRpt {
if decoder.read_to_end(&mut buffer).is_ok() { organization_name: tlsrpt.organization_name,
if let Ok(json_str) = String::from_utf8(buffer) { date_range: FormattedTlsRptDateRange {
match serde_json::from_str::<TlsRpt>(&json_str) { start_datetime: tlsrpt.date_range.start_datetime,
Ok(tlsrpt) => { end_datetime: tlsrpt.date_range.end_datetime,
let formatted_tlsrpt = FormattedTlsRpt {
organization_name: tlsrpt.organization_name,
date_range: FormattedTlsRptDateRange {
start_datetime: tlsrpt.date_range.start_datetime,
end_datetime: tlsrpt.date_range.end_datetime,
},
contact_info: tlsrpt.contact_info.unwrap_or_else(|| "".to_string()),
report_id: tlsrpt.report_id,
policies: tlsrpt
.policies
.into_iter()
.map(|policy| FormattedTlsRptPolicy {
policy: FormattedTlsRptPolicyDetails {
policy_type: policy.policy.policy_type,
policy_string: policy.policy.policy_string,
policy_domain: policy.policy.policy_domain,
mx_host: policy
.policy
.mx_host
.unwrap_or_else(|| Vec::new())
.into_iter()
.map(|mx| match mx {
MxHost::String(s) => FormattedTlsRptMxHost {
hostname: s,
failure_count: 0,
result_type: "".to_string(),
},
MxHost::Object(o) => FormattedTlsRptMxHost {
hostname: o.hostname,
failure_count: o.failure_count,
result_type: o.result_type,
},
})
.collect(),
}, },
summary: policy.summary, contact_info: tlsrpt
failure_details: policy .contact_info
.failure_details .unwrap_or_else(|| "".to_string()),
.unwrap_or_else(|| Vec::new()) report_id: tlsrpt.report_id,
policies: tlsrpt
.policies
.into_iter() .into_iter()
.map(|detail| FormattedTlsRptFailureDetails { .map(|policy| FormattedTlsRptPolicy {
result_type: detail.result_type, policy: FormattedTlsRptPolicyDetails {
sending_mta_ip: detail policy_type: policy.policy.policy_type,
.sending_mta_ip policy_string: policy.policy.policy_string,
.unwrap_or_else(|| "".to_string()), policy_domain: policy.policy.policy_domain,
receiving_ip: detail mx_host: policy
.receiving_ip .policy
.unwrap_or_else(|| "".to_string()), .mx_host
receiving_mx_hostname: detail .unwrap_or_else(|| Vec::new())
.receiving_mx_hostname .into_iter()
.unwrap_or_else(|| "".to_string()), .map(|mx| match mx {
failed_session_count: detail.failed_session_count, MxHost::String(s) => {
additional_info: detail FormattedTlsRptMxHost {
.additional_info hostname: s,
.unwrap_or_else(|| "".to_string()), failure_count: 0,
failure_reason_code: detail result_type: "".to_string(),
.failure_reason_code }
.unwrap_or_else(|| "".to_string()), }
MxHost::Object(o) => {
FormattedTlsRptMxHost {
hostname: o.hostname,
failure_count: o.failure_count,
result_type: o.result_type,
}
}
})
.collect(),
},
summary: policy.summary,
failure_details: policy
.failure_details
.unwrap_or_else(|| Vec::new())
.into_iter()
.map(|detail| FormattedTlsRptFailureDetails {
result_type: detail.result_type,
sending_mta_ip: detail
.sending_mta_ip
.unwrap_or_else(|| "".to_string()),
receiving_ip: detail
.receiving_ip
.unwrap_or_else(|| "".to_string()),
receiving_mx_hostname: detail
.receiving_mx_hostname
.unwrap_or_else(|| "".to_string()),
failed_session_count: detail
.failed_session_count,
additional_info: detail
.additional_info
.unwrap_or_else(|| "".to_string()),
failure_reason_code: detail
.failure_reason_code
.unwrap_or_else(|| "".to_string()),
})
.collect(),
}) })
.collect(), .collect(),
}) };
.collect(), let template = TlsReportTemplate {
}; report: &formatted_tlsrpt,
let template = TlsReportTemplate { };
report: &formatted_tlsrpt, let html = template.render().unwrap_or_else(|e| format!("<div class=\"tlsrpt-error\">Failed to render TLS report template: {}</div>", e));
}; parts.push(Body::html(html));
template.render().unwrap_or_else(|e| format!("<div class=\"tlsrpt-error\">Failed to render TLS report template: {}</div>", e)) }
Err(e) => {
let html = format!(
"<div class=\"tlsrpt-error\">Failed to parse TLS report JSON: {}</div>",
e
);
parts.push(Body::html(html));
}
}
} else {
let html = format!("<div class=\"tlsrpt-error\">Failed to convert decompressed data to UTF-8.</div>");
parts.push(Body::html(html));
}
} else {
let html =
format!("<div class=\"tlsrpt-error\">Failed to decompress data.</div>");
parts.push(Body::html(html));
}
}
MESSAGE_RFC822 => {
parts.push(extract_rfc822(&sp, part_addr)?);
}
TEXT_HTML => {
let body = sp.get_body()?;
parts.push(Body::html(body));
}
MESSAGE_DELIVERY_STATUS => {
let body = extract_delivery_status(sp)?;
parts.push(body);
}
TEXT_PLAIN => {
let body = sp.get_body()?;
parts.push(Body::text(body));
}
_ => {
// For any other content type, try to extract the body using the general extract_body function
match extract_body(sp, part_addr) {
Ok(body) => parts.push(body),
Err(_) => {
// If extraction fails, create an unhandled content type body
let msg = format!(
"Unhandled report subpart content type: {}\n{}",
sp.ctype.mimetype,
sp.get_body()
.unwrap_or_else(|_| "Failed to get body".to_string())
);
parts.push(Body::UnhandledContentType(UnhandledContentType {
text: msg,
content_tree: render_content_type_tree(sp),
}));
} }
Err(e) => format!(
"<div class=\"tlsrpt-error\">Failed to parse TLS report JSON: {}</div>",
e
),
} }
} else {
format!("<div class=\"tlsrpt-error\">Failed to convert decompressed data to UTF-8.</div>")
} }
} else {
format!("<div class=\"tlsrpt-error\">Failed to decompressed data.</div>")
} }
} else {
"".to_string()
};
let final_html = if let Some(html) = html_part { part_addr.pop();
format!("{}<hr>{} ", html, tlsrpt_summary_html) }
} else {
tlsrpt_summary_html
};
Ok(Body::html(final_html)) if parts.is_empty() {
return Ok(Body::html(
"<div class=\"report-error\">No report content found</div>".to_string(),
));
}
// Add <hr> tags between subparts for better visual separation
let html = parts
.iter()
.map(|p| match p {
Body::PlainText(PlainText { text, .. }) => {
format!(
r#"<p class="view-part-text-plain font-mono whitespace-pre-line">{}</p>"#,
linkify_html(&html_escape::encode_text(text).trim_matches('\n'))
)
}
Body::Html(Html { html, .. }) => html.clone(),
Body::UnhandledContentType(UnhandledContentType { text, .. }) => {
format!(
r#"<p class="view-part-unhandled">{}</p>"#,
linkify_html(&html_escape::encode_text(text).trim_matches('\n'))
)
}
})
.collect::<Vec<_>>()
.join("<hr>\n");
Ok(Body::html(html))
}
pub fn extract_delivery_status(m: &ParsedMail) -> Result<Body, ServerError> {
Ok(Body::text(m.get_body()?))
} }
pub fn extract_unhandled(m: &ParsedMail) -> Result<Body, ServerError> { pub fn extract_unhandled(m: &ParsedMail) -> Result<Body, ServerError> {

View File

@@ -12,7 +12,7 @@ version.workspace = true
[dependencies] [dependencies]
build-info = "0.0.41" build-info = "0.0.41"
letterbox-notmuch = { path = "../notmuch", version = "0.17.44", registry = "xinu" } letterbox-notmuch = { path = "../notmuch", version = "0.17.45", registry = "xinu" }
regex = "1.11.1" regex = "1.11.1"
serde = { version = "1.0.219", features = ["derive"] } serde = { version = "1.0.219", features = ["derive"] }
sqlx = "0.8.5" sqlx = "0.8.5"

View File

@@ -12,7 +12,7 @@ version.workspace = true
build-info-build = "0.0.41" build-info-build = "0.0.41"
[dev-dependencies] [dev-dependencies]
wasm-bindgen-test = "0.3.50" #wasm-bindgen-test = "0.3.50"
[dependencies] [dependencies]
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1.7"
@@ -33,7 +33,7 @@ wasm-bindgen = "=0.2.100"
uuid = { version = "1.16.0", features = [ uuid = { version = "1.16.0", features = [
"js", "js",
] } # direct dep to set js feature, prevents Rng issues ] } # direct dep to set js feature, prevents Rng issues
letterbox-shared = { path = "../shared/", version = "0.17.44", registry = "xinu" } letterbox-shared = { path = "../shared/", version = "0.17.45", registry = "xinu" }
seed_hooks = { version = "0.4.1", registry = "xinu" } seed_hooks = { version = "0.4.1", registry = "xinu" }
strum_macros = "0.27.1" strum_macros = "0.27.1"
gloo-console = "0.3.0" gloo-console = "0.3.0"