diff --git a/server/smtp_server.go b/server/smtp_server.go index ee28efc2..02414d3b 100644 --- a/server/smtp_server.go +++ b/server/smtp_server.go @@ -33,6 +33,7 @@ var ( var ( onlySpacesRegex = regexp.MustCompile(`(?m)^\s+$`) consecutiveNewLinesRegex = regexp.MustCompile(`\n{3,}`) + htmlLineBreakRegex = regexp.MustCompile(`(?i)`) ) const ( @@ -327,6 +328,9 @@ func readHTMLMailBody(reader io.Reader, transferEncoding string) (string, error) if err != nil { return "", err } + // Convert
tags to newlines before stripping HTML, so that line breaks + // in HTML emails (e.g. from Synology DSM, and other appliances) are preserved. + body = htmlLineBreakRegex.ReplaceAllString(body, "\n") stripped := bluemonday. StrictPolicy(). AddSpaceWhenStrippingTag(true). diff --git a/server/smtp_server_test.go b/server/smtp_server_test.go index d4178a40..8b614819 100644 --- a/server/smtp_server_test.go +++ b/server/smtp_server_test.go @@ -694,7 +694,8 @@ home automation setup Now the light is on If you don't want to receive this message anymore, stop the push - services in your FRITZ!Box . + services in your FRITZ!Box . + Here you can see the active push services: "System > Push Service". This mail has ben sent by your FRITZ!Box automatically.` @@ -1354,9 +1355,11 @@ Congratulations! You have successfully set up the email notification on Synology s, c, conf, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) { require.Equal(t, "/synology", r.URL.Path) require.Equal(t, "[Synology NAS] Test Message from Litts_NAS", r.Header.Get("Title")) - actual := readAll(t, r.Body) - expected := `Congratulations! You have successfully set up the email notification on Synology_NAS. For further system configurations, please visit http://192.168.1.28:5000/, http://172.16.60.5:5000/. (If you cannot connect to the server, please contact the administrator.) From Synology_NAS` - require.Equal(t, expected, actual) + expected := "Congratulations! You have successfully set up the email notification on Synology_NAS.\n" + + "For further system configurations, please visit http://192.168.1.28:5000/, http://172.16.60.5:5000/.\n" + + "(If you cannot connect to the server, please contact the administrator.)\n\n" + + "From Synology_NAS" + require.Equal(t, expected, readAll(t, r.Body)) }) conf.SMTPServerDomain = "mydomain.me" conf.SMTPServerAddrPrefix = "" @@ -1365,6 +1368,36 @@ Congratulations! You have successfully set up the email notification on Synology writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued") } +func TestSmtpBackend_HTMLEmail_BrTagsPreserved(t *testing.T) { + email := `EHLO example.com +MAIL FROM: nas@example.com +RCPT TO: ntfy-alerts@ntfy.sh +DATA +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: 8bit +Subject: Task Scheduler: daily-backup + +Task Scheduler has completed a scheduled task.

Task: daily-backup
Start time: Mon, 01 Jan 2026 02:00:00 +0000
Stop time: Mon, 01 Jan 2024 02:03:00 +0000
Current status: 0 (Normal)
Standard output/error:
OK

From MyNAS +. +` + s, c, _, scanner := newTestSMTPServer(t, func(w http.ResponseWriter, r *http.Request) { + require.Equal(t, "/alerts", r.URL.Path) + require.Equal(t, "Task Scheduler: daily-backup", r.Header.Get("Title")) + expected := "Task Scheduler has completed a scheduled task.\n\n" + + "Task: daily-backup\n" + + "Start time: Mon, 01 Jan 2026 02:00:00 +0000\n" + + "Stop time: Mon, 01 Jan 2024 02:03:00 +0000\n" + + "Current status: 0 (Normal)\n" + + "Standard output/error:\n" + + "OK\n\n" + + "From MyNAS" + require.Equal(t, expected, readAll(t, r.Body)) + }) + defer s.Close() + defer c.Close() + writeAndReadUntilLine(t, email, c, scanner, "250 2.0.0 OK: queued") +} + func TestSmtpBackend_PlaintextWithToken(t *testing.T) { email := `EHLO example.com MAIL FROM: phil@example.com