feat: add a per-recipient Format option to convert HTML bodies (FMG-9) #13
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
fmartingr/smtp2shoutrrr!13
Loading…
Reference in a new issue
No description provided.
Delete branch "agent/full-stack-developer/7fb9f472e93e"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes FMG-9
Targets that render no HTML — a Mattermost direct message, for one — used to receive the markup of an HTML-only message verbatim.
Formaton a recipient (or on[CatchAll]) now renders an HTML body as Markdown before it is forwarded:Only an HTML body is ever rewritten. A message that arrived as plain text is what the sender chose to write, and is forwarded untouched whatever the recipient asked for. An unknown
Formatis refused at startup rather than silently forwarding raw HTML.There is deliberately no plain-text format beside
markdown: Markdown reads as plain text wherever nothing renders it, so a second conversion would only be a worse copy of the first.The conversion
The Markdown is produced by html-to-markdown, which knows CommonMark — escaping, delimiter runs, fencing a code block past the backticks inside it. Cost: two modules (
html-to-markdown/v2andJohannesKaufmann/dom) on top of thegolang.org/x/netthis already used.What it does not know is mail, because it is written for documents. The parsed message is prepared before conversion — hidden preheaders removed, alt-less images dropped (with the tracking pixels and spacers), unusable destinations unwrapped while keeping the link text, tabs and line breaks stripped from URLs, table rows turned into lines with their cells kept apart, and a link left holding nothing but a pixel falls back to its own destination instead of rendering as an invisible
[](url).The DoS is ours either way. A line prefix is re-emitted per line and per level, so nesting depth multiplies against line count.
html-to-markdownamplifies exactly as a hand-written renderer does. Measured on one message at the server's own 1 MB limit, nested 250 deep:So quote and list nesting is flattened past six levels and the output is capped at 64 KiB with a
….Reading the body correctly
Converting a body first required reading it correctly, which fixes three faults that were also spoiling
rawdelivery:Content-Transfer-Encodingwas never undone outside multipart parts, so a quoted-printable or base64 body reached the target as=E2=80=99.multipart/mixedwrapping amultipart/alternativethat every forwarded message is produced an empty notification.Body selection now follows RFC 2046 per container: the parts of a
multipart/alternativeare one content in several forms, the parts of any other multipart are cumulative. Attachments are skipped, an empty alternative no longer wins over the HTML the sender actually wrote, and a decode that fails degrades to the bytes as they arrived rather than bouncing mail that was being delivered before.What a real message looks like
Checks
make format,make ci-lint,make test(93.6% coverage),make checkandmake buildall pass locally.🤖 Generated with Claude Code
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.Merge
Merge the changes and update on Forgejo.Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.