diff --git a/docs/examples.md b/docs/examples.md
index 10bb014a..ee1de244 100644
--- a/docs/examples.md
+++ b/docs/examples.md
@@ -661,6 +661,8 @@ Add the following function and alias to your `.bashrc` or `.bash_profile`:
local token=$(< ~/.ntfy_token) # Securely read the token
local status_icon="$([ $exit_status -eq 0 ] && echo magic_wand || echo warning)"
local last_command=$(history | tail -n1 | sed -e 's/^[[:space:]]*[0-9]\{1,\}[[:space:]]*//' -e 's/[;&|][[:space:]]*alert$//')
+ # for zsh users, use the same sed pattern but get the history differently.
+ # local last_command=$(history "$HISTCMD" | sed -e 's/^[[:space:]]*[0-9]\{1,\}[[:space:]]*//' -e 's/[;&|][[:space:]]*alert$//')
curl -s -X POST "https://n.example.dev/alerts" \
-H "Authorization: Bearer $token" \
@@ -692,4 +694,4 @@ To test failure notifications:
false; alert # Always fails (exit 1)
ls --invalid; alert # Invalid option
cat nonexistent_file; alert # File not found
-```
\ No newline at end of file
+```
diff --git a/docs/integrations.md b/docs/integrations.md
index dd9b897f..a5a22530 100644
--- a/docs/integrations.md
+++ b/docs/integrations.md
@@ -185,6 +185,7 @@ I've added a ⭐ to projects or posts that have a significant following, or had
- [Uptime Monitor](https://uptime-monitor.org) - Self-hosted, enterprise-grade uptime monitoring and alerting system (TS)
- [send_to_ntfy_extension](https://github.com/TheDuffman85/send_to_ntfy_extension/) ⭐ - A browser extension to send the notifications to ntfy (JS)
- [SIA-Server](https://github.com/ZebMcKayhan/SIA-Server) - A light weight, self-hosted notification Server for Honywell Galaxy Flex alarm systems (Python)
+- [zabbix-ntfy](https://github.com/torgrimt/zabbix-ntfy) - Zabbix server Mediatype to add support for ntfy.sh services
## Blog + forum posts
diff --git a/docs/releases.md b/docs/releases.md
index b5ce5f31..5e810663 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -1698,6 +1698,12 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release
## Not released yet
+### ntfy server v2.18.x (UNRELEASED)
+
+**Bug fixes + maintenance:**
+
+* Preserve `
` line breaks in HTML-only emails received via SMTP ([#690](https://github.com/binwiederhier/ntfy/issues/690), [#1620](https://github.com/binwiederhier/ntfy/pull/1620), thanks to [@uzkikh](https://github.com/uzkikh) for the fix and to [@teastrainer](https://github.com/teastrainer) for reporting)
+
### ntfy Android v1.23.x (UNRELEASED)
**Features:**
diff --git a/server/smtp_server.go b/server/smtp_server.go
index e342e678..9e6588e0 100644
--- a/server/smtp_server.go
+++ b/server/smtp_server.go
@@ -34,6 +34,7 @@ var (
var (
onlySpacesRegex = regexp.MustCompile(`(?m)^\s+$`)
consecutiveNewLinesRegex = regexp.MustCompile(`\n{3,}`)
+ htmlLineBreakRegex = regexp.MustCompile(`(?i)
`)
)
const (
@@ -328,6 +329,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 3294fe51..b7df768d 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
diff --git a/web/public/static/langs/he.json b/web/public/static/langs/he.json
index 4674c993..9e46e071 100644
--- a/web/public/static/langs/he.json
+++ b/web/public/static/langs/he.json
@@ -48,5 +48,26 @@
"notifications_none_for_topic_title": "לא קיבלת התראות בנושא הזה עדיין.",
"notifications_none_for_topic_description": "כדי לשלוח התראות לנושא הזה, צריך לשלוח PUT או POST לכתובת הנושא הזה.",
"notifications_none_for_any_title": "לא קיבלת התראות כלל.",
- "notifications_no_subscriptions_title": "נראה שלא נרשמת למינויים עדיין."
+ "notifications_no_subscriptions_title": "נראה שלא נרשמת למינויים עדיין.",
+ "action_bar_toggle_mute": "השתקת/הפעלת התראות",
+ "action_bar_toggle_action_menu": "פתיחת/סגירת תפריט הפעולות",
+ "action_bar_profile_title": "פרופיל",
+ "action_bar_profile_settings": "הגדרות",
+ "action_bar_profile_logout": "יציאה",
+ "action_bar_sign_in": "כניסה",
+ "action_bar_sign_up": "הרשמה",
+ "message_bar_type_message": "כאן ניתן להקליד הודעה",
+ "message_bar_error_publishing": "שגיאה בפרסום ההתראה",
+ "message_bar_show_dialog": "הצגת חלונית פרסום",
+ "message_bar_publish": "פרסום הודעה",
+ "nav_topics_title": "נושאים שנרשמת אליהם",
+ "nav_button_all_notifications": "כל ההתראות",
+ "nav_button_account": "חשבון",
+ "nav_button_settings": "הגדרות",
+ "nav_button_documentation": "תיעוד",
+ "nav_button_publish_message": "פרסום התראה",
+ "nav_button_subscribe": "הרשמה לנושא",
+ "nav_button_muted": "התראות הושתקו",
+ "nav_button_connecting": "מתחבר",
+ "nav_upgrade_banner_label": "שדרוג ל־ntfy Pro"
}