From 6bdddc3d54ec0a0ed9573dd278e43d58f7de13b0 Mon Sep 17 00:00:00 2001 From: Olivier Date: Wed, 20 May 2026 15:26:01 +0200 Subject: [PATCH] Add RFC email headers to report messages --- src/pve_backup_report/email_report.py | 14 ++++++++++++++ tests/test_email_report.py | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/pve_backup_report/email_report.py b/src/pve_backup_report/email_report.py index fefeffa..058f6f5 100644 --- a/src/pve_backup_report/email_report.py +++ b/src/pve_backup_report/email_report.py @@ -2,6 +2,7 @@ from __future__ import annotations import smtplib from email.message import EmailMessage +from email.utils import formatdate, make_msgid, parseaddr from pathlib import Path from pve_backup_report.config import EmailConfig @@ -47,6 +48,9 @@ def build_report_message(config: EmailConfig, pdf_path: Path) -> EmailMessage: message["Subject"] = config.subject message["From"] = config.smtp_from or "" message["To"] = ", ".join(config.smtp_to) + message["Date"] = formatdate(localtime=True) + message["Message-ID"] = make_msgid(domain=message_id_domain(config.smtp_from)) + message["Auto-Submitted"] = "auto-generated" message.set_content( "Bonjour,\n\n" "Veuillez trouver ci-joint le rapport de sauvegardes Proxmox VE.\n\n" @@ -61,6 +65,16 @@ def build_report_message(config: EmailConfig, pdf_path: Path) -> EmailMessage: return message +def message_id_domain(address: str | None) -> str | None: + if address is None: + return None + parsed_address = parseaddr(address)[1] + if "@" not in parsed_address: + return None + domain = parsed_address.rsplit("@", 1)[1].strip() + return domain or None + + def authenticate_and_send( smtp: smtplib.SMTP | smtplib.SMTP_SSL, config: EmailConfig, diff --git a/tests/test_email_report.py b/tests/test_email_report.py index 741efa2..81064bf 100644 --- a/tests/test_email_report.py +++ b/tests/test_email_report.py @@ -1,7 +1,11 @@ from pathlib import Path from pve_backup_report.config import EmailConfig -from pve_backup_report.email_report import build_report_message, send_report_email +from pve_backup_report.email_report import ( + build_report_message, + message_id_domain, + send_report_email, +) def test_build_report_message_attaches_pdf(tmp_path: Path) -> None: @@ -20,6 +24,9 @@ def test_build_report_message_attaches_pdf(tmp_path: Path) -> None: assert message["Subject"] == "Rapport PVE" assert message["From"] == "report@example.invalid" assert message["To"] == "admin@example.invalid" + assert message["Date"] + assert message["Message-ID"].endswith("@example.invalid>") + assert message["Auto-Submitted"] == "auto-generated" attachments = list(message.iter_attachments()) assert len(attachments) == 1 assert attachments[0].get_filename() == "rapport.pdf" @@ -27,6 +34,13 @@ def test_build_report_message_attaches_pdf(tmp_path: Path) -> None: assert attachments[0].get_payload(decode=True) == b"%PDF-1.7" +def test_message_id_domain_uses_sender_domain() -> None: + assert message_id_domain("PVE Report ") == "example.invalid" + assert message_id_domain("pve@example.invalid") == "example.invalid" + assert message_id_domain("invalid") is None + assert message_id_domain(None) is None + + def test_send_report_email_uses_starttls_and_auth(tmp_path: Path, monkeypatch) -> None: pdf_path = tmp_path / "rapport.pdf" pdf_path.write_bytes(b"%PDF-1.7")