From 168ad8bf1b46fc1edc5fb1388e0ce86e2b73c9ef Mon Sep 17 00:00:00 2001 From: binwiederhier Date: Sun, 21 May 2023 20:56:56 -0400 Subject: [PATCH] Support encoding any header as RFC 2047 --- docs/publish.md | 18 +++++++++++++----- docs/releases.md | 11 +++++++++-- server/server.go | 10 +++++----- server/server_test.go | 11 +++++++++-- server/types.go | 1 + server/util.go | 2 +- 6 files changed, 38 insertions(+), 15 deletions(-) diff --git a/docs/publish.md b/docs/publish.md index 80d05d17..41801241 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -393,8 +393,8 @@ you can set the `X-Title` header (or any of its aliases: `Title`, `ti`, or `t`). !!! info ntfy supports UTF-8 in HTTP headers, but [not every library or programming language does](https://www.jmix.io/blog/utf-8-in-http-headers/). - If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode the `X-Title` or `X-Message` - header as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)), + If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode any header (including the title) + as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)), or `=?UTF-8?Q?=C3=84pfel?=` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)). ## Message priority @@ -619,7 +619,7 @@ them with a comma, e.g. `tag1,tag2,tag3`. !!! info ntfy supports UTF-8 in HTTP headers, but [not every library or programming language does](https://www.jmix.io/blog/utf-8-in-http-headers/). - If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode the individual tags + If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode the tags header or individual tags as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `tag1,=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)), or `=?UTF-8?Q?=C3=84pfel?=,tag2` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)). @@ -1004,9 +1004,11 @@ all the supported fields: | `actions` | - | *JSON array* | *(see [action buttons](#action-buttons))* | Custom [user action buttons](#action-buttons) for notifications | | `click` | - | *URL* | `https://example.com` | Website opened when notification is [clicked](#click-action) | | `attach` | - | *URL* | `https://example.com/file.jpg` | URL of an attachment, see [attach via URL](#attach-file-from-url) | +| `icon` | - | *string* | `https://example.com/icon.png` | URL to use as notification [icon](#icons) | | `filename` | - | *string* | `file.jpg` | File name of the attachment | | `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery | | `email` | - | *e-mail address* | `phil@example.com` | E-mail address for e-mail notifications | +| `call` | - | *phone number or 'yes'* | `+1222334444` or `yes` | Phone number to use for [voice call](#phone-calls) | ## Action buttons _Supported on:_ :material-android: :material-apple: :material-firefox: @@ -1139,7 +1141,13 @@ As an example, here's how you can create the above notification using this forma ] ])); ``` - + +!!! info + ntfy supports UTF-8 in HTTP headers, but [not every library or programming language does](https://www.jmix.io/blog/utf-8-in-http-headers/). + If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode any header (including actions) + as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)), + or `=?UTF-8?Q?=C3=84pfel?=` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)). + #### Using a JSON array Alternatively, the same actions can be defined as **JSON array**, if the notification is defined as part of the JSON body (see [publish as JSON](#publish-as-json)): @@ -3465,7 +3473,7 @@ table in their canonical form. !!! info ntfy supports UTF-8 in HTTP headers, but [not every library or programming language does](https://www.jmix.io/blog/utf-8-in-http-headers/). - If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode the `X-Title` or `X-Message` + If non-ASCII characters are causing issues for you in the title (i.e. you're seeing `?` symbols), you may also encode any header as [RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047#section-2), e.g. `=?UTF-8?B?8J+HqfCfh6o=?=` ([base64](https://en.wikipedia.org/wiki/Base64)), or `=?UTF-8?Q?=C3=84pfel?=` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)). diff --git a/docs/releases.md b/docs/releases.md index 633fa15f..89ecc516 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -28,7 +28,7 @@ if you use promo code `MYTOPIC`). ntfy will always remain open source. * Attachments with filenames that are downloaded using a browser will now download with the proper filename ([#726](https://github.com/binwiederhier/ntfy/issues/726), thanks to [@un99known99](https://github.com/un99known99) for reporting, and [@wunter8](https://github.com/wunter8) for fixing) * Fix web app i18n issue in account preferences ([#730](https://github.com/binwiederhier/ntfy/issues/730), thanks to [@codebude](https://github.com/codebude) for reporting) -### ntfy server v2.4.0 +## ntfy server v2.4.0 Released Apr 26, 2023 This release adds a tiny `v1/stats` endpoint to expose how many messages have been published, and adds suport to encode the `X-Title`, @@ -57,7 +57,7 @@ will always remain open source. * Swedish (thanks to [@hellbown](https://hosted.weblate.org/user/Shjosan/)) -### ntfy server v2.3.1 +## ntfy server v2.3.1 Released March 30, 2023 This release disables server-initiated polling of iOS devices entirely, thereby eliminating the thundering herd problem @@ -1219,3 +1219,10 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release **Additional languages:** * Swedish (thanks to [@hellbown](https://hosted.weblate.org/user/hellbown/)) + +### ntfy server v2.6.0 (UNRELEASED) + +**Bug fixes + maintenance:** + +* Support encoding any header as RFC 2047 ([#737](https://github.com/binwiederhier/ntfy/issues/737), thanks to [@cfouche3005](https://github.com/cfouche3005) for reporting) + diff --git a/server/server.go b/server/server.go index a451baa7..14a8c7f1 100644 --- a/server/server.go +++ b/server/server.go @@ -876,7 +876,7 @@ func (s *Server) forwardPollRequest(v *visitor, m *message) { func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, firebase bool, email, call string, unifiedpush bool, err *errHTTP) { cache = readBoolParam(r, true, "x-cache", "cache") firebase = readBoolParam(r, true, "x-firebase", "firebase") - m.Title = maybeDecodeHeader(readParam(r, "x-title", "title", "t")) + m.Title = readParam(r, "x-title", "title", "t") m.Click = readParam(r, "x-click", "click") icon := readParam(r, "x-icon", "icon") filename := readParam(r, "x-filename", "filename", "file", "f") @@ -923,7 +923,7 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi } messageStr := strings.ReplaceAll(readParam(r, "x-message", "message", "m"), "\\n", "\n") if messageStr != "" { - m.Message = maybeDecodeHeader(messageStr) + m.Message = messageStr } var e error m.Priority, e = util.ParsePriority(readParam(r, "x-priority", "priority", "prio", "p")) @@ -931,9 +931,6 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi return false, false, "", "", false, errHTTPBadRequestPriorityInvalid } m.Tags = readCommaSeparatedParam(r, "x-tags", "tags", "tag", "ta") - for i, t := range m.Tags { - m.Tags[i] = maybeDecodeHeader(t) - } delayStr := readParam(r, "x-delay", "delay", "x-at", "at", "x-in", "in") if delayStr != "" { if !cache { @@ -1747,6 +1744,9 @@ func (s *Server) transformBodyJSON(next handleFunc) handleFunc { if m.Delay != "" { r.Header.Set("X-Delay", m.Delay) } + if m.Call != "" { + r.Header.Set("X-Call", m.Call) + } return next(w, r, v) } } diff --git a/server/server_test.go b/server/server_test.go index 35d89707..fe84b856 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -2478,18 +2478,25 @@ func TestServer_PublishWithUTF8MimeHeader(t *testing.T) { s := newTestServer(t, newTestConfig(t)) response := request(t, s, "POST", "/mytopic", "some attachment", map[string]string{ - "X-Filename": "some attachment.txt", + "X-Filename": "some =?UTF-8?q?=C3=A4?=ttachment.txt", "X-Message": "=?UTF-8?B?8J+HqfCfh6o=?=", "X-Title": "=?UTF-8?B?bnRmeSDlvojmo5I=?=, no really I mean it! =?UTF-8?Q?This is q=C3=BC=C3=B6ted-print=C3=A4ble.?=", "X-Tags": "=?UTF-8?B?8J+HqfCfh6o=?=, =?UTF-8?B?bnRmeSDlvojmo5I=?=", + "X-Click": "=?uTf-8?b?aHR0cHM6Ly/wn5KpLmxh?=", + "X-Actions": "http, \"=?utf-8?q?Mettre =C3=A0 jour?=\", \"https://my.tld/webhook/netbird-update\"; =?utf-8?b?aHR0cCwg6L+Z5piv5LiA5Liq5qCH562+LCBodHRwczovL/CfkqkubGE=?=", }) require.Equal(t, 200, response.Code) m := toMessage(t, response.Body.String()) require.Equal(t, "🇩🇪", m.Message) require.Equal(t, "ntfy 很棒, no really I mean it! This is qüöted-printäble.", m.Title) - require.Equal(t, "some attachment.txt", m.Attachment.Name) + require.Equal(t, "some ättachment.txt", m.Attachment.Name) require.Equal(t, "🇩🇪", m.Tags[0]) require.Equal(t, "ntfy 很棒", m.Tags[1]) + require.Equal(t, "https://💩.la", m.Click) + require.Equal(t, "Mettre à jour", m.Actions[0].Label) + require.Equal(t, "http", m.Actions[1].Action) + require.Equal(t, "这是一个标签", m.Actions[1].Label) + require.Equal(t, "https://💩.la", m.Actions[1].URL) } func TestServer_UpstreamBaseURL_Success(t *testing.T) { diff --git a/server/types.go b/server/types.go index 4280f6c9..9e4ff558 100644 --- a/server/types.go +++ b/server/types.go @@ -101,6 +101,7 @@ type publishMessage struct { Attach string `json:"attach"` Filename string `json:"filename"` Email string `json:"email"` + Call string `json:"call"` Delay string `json:"delay"` } diff --git a/server/util.go b/server/util.go index a3a45547..03eb8661 100644 --- a/server/util.go +++ b/server/util.go @@ -50,7 +50,7 @@ func readParam(r *http.Request, names ...string) string { func readHeaderParam(r *http.Request, names ...string) string { for _, name := range names { - value := r.Header.Get(name) + value := maybeDecodeHeader(r.Header.Get(name)) if value != "" { return strings.TrimSpace(value) }