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" }