Support encoding any header as RFC 2047
This commit is contained in:
parent
b3a299ce22
commit
168ad8bf1b
6 changed files with 38 additions and 15 deletions
|
@ -393,8 +393,8 @@ you can set the `X-Title` header (or any of its aliases: `Title`, `ti`, or `t`).
|
||||||
|
|
||||||
!!! info
|
!!! 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/).
|
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 (including the title)
|
||||||
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)),
|
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)).
|
or `=?UTF-8?Q?=C3=84pfel?=` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)).
|
||||||
|
|
||||||
## Message priority
|
## Message priority
|
||||||
|
@ -619,7 +619,7 @@ them with a comma, e.g. `tag1,tag2,tag3`.
|
||||||
|
|
||||||
!!! info
|
!!! 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/).
|
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)),
|
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)).
|
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 |
|
| `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) |
|
| `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) |
|
| `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 |
|
| `filename` | - | *string* | `file.jpg` | File name of the attachment |
|
||||||
| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
|
| `delay` | - | *string* | `30min`, `9am` | Timestamp or duration for delayed delivery |
|
||||||
| `email` | - | *e-mail address* | `phil@example.com` | E-mail address for e-mail notifications |
|
| `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
|
## Action buttons
|
||||||
_Supported on:_ :material-android: :material-apple: :material-firefox:
|
_Supported on:_ :material-android: :material-apple: :material-firefox:
|
||||||
|
@ -1140,6 +1142,12 @@ 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
|
#### 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
|
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)):
|
(see [publish as JSON](#publish-as-json)):
|
||||||
|
@ -3465,7 +3473,7 @@ table in their canonical form.
|
||||||
|
|
||||||
!!! info
|
!!! 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/).
|
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)),
|
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)).
|
or `=?UTF-8?Q?=C3=84pfel?=` ([quoted-printable](https://en.wikipedia.org/wiki/Quoted-printable)).
|
||||||
|
|
||||||
|
|
|
@ -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)
|
* 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)
|
* 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
|
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`,
|
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/))
|
* Swedish (thanks to [@hellbown](https://hosted.weblate.org/user/Shjosan/))
|
||||||
|
|
||||||
### ntfy server v2.3.1
|
## ntfy server v2.3.1
|
||||||
Released March 30, 2023
|
Released March 30, 2023
|
||||||
|
|
||||||
This release disables server-initiated polling of iOS devices entirely, thereby eliminating the thundering herd problem
|
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:**
|
**Additional languages:**
|
||||||
|
|
||||||
* Swedish (thanks to [@hellbown](https://hosted.weblate.org/user/hellbown/))
|
* 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)
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
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")
|
cache = readBoolParam(r, true, "x-cache", "cache")
|
||||||
firebase = readBoolParam(r, true, "x-firebase", "firebase")
|
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")
|
m.Click = readParam(r, "x-click", "click")
|
||||||
icon := readParam(r, "x-icon", "icon")
|
icon := readParam(r, "x-icon", "icon")
|
||||||
filename := readParam(r, "x-filename", "filename", "file", "f")
|
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")
|
messageStr := strings.ReplaceAll(readParam(r, "x-message", "message", "m"), "\\n", "\n")
|
||||||
if messageStr != "" {
|
if messageStr != "" {
|
||||||
m.Message = maybeDecodeHeader(messageStr)
|
m.Message = messageStr
|
||||||
}
|
}
|
||||||
var e error
|
var e error
|
||||||
m.Priority, e = util.ParsePriority(readParam(r, "x-priority", "priority", "prio", "p"))
|
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
|
return false, false, "", "", false, errHTTPBadRequestPriorityInvalid
|
||||||
}
|
}
|
||||||
m.Tags = readCommaSeparatedParam(r, "x-tags", "tags", "tag", "ta")
|
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")
|
delayStr := readParam(r, "x-delay", "delay", "x-at", "at", "x-in", "in")
|
||||||
if delayStr != "" {
|
if delayStr != "" {
|
||||||
if !cache {
|
if !cache {
|
||||||
|
@ -1747,6 +1744,9 @@ func (s *Server) transformBodyJSON(next handleFunc) handleFunc {
|
||||||
if m.Delay != "" {
|
if m.Delay != "" {
|
||||||
r.Header.Set("X-Delay", m.Delay)
|
r.Header.Set("X-Delay", m.Delay)
|
||||||
}
|
}
|
||||||
|
if m.Call != "" {
|
||||||
|
r.Header.Set("X-Call", m.Call)
|
||||||
|
}
|
||||||
return next(w, r, v)
|
return next(w, r, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2478,18 +2478,25 @@ func TestServer_PublishWithUTF8MimeHeader(t *testing.T) {
|
||||||
s := newTestServer(t, newTestConfig(t))
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
|
||||||
response := request(t, s, "POST", "/mytopic", "some attachment", map[string]string{
|
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-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-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-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)
|
require.Equal(t, 200, response.Code)
|
||||||
m := toMessage(t, response.Body.String())
|
m := toMessage(t, response.Body.String())
|
||||||
require.Equal(t, "🇩🇪", m.Message)
|
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, "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, "🇩🇪", m.Tags[0])
|
||||||
require.Equal(t, "ntfy 很棒", m.Tags[1])
|
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) {
|
func TestServer_UpstreamBaseURL_Success(t *testing.T) {
|
||||||
|
|
|
@ -101,6 +101,7 @@ type publishMessage struct {
|
||||||
Attach string `json:"attach"`
|
Attach string `json:"attach"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
Call string `json:"call"`
|
||||||
Delay string `json:"delay"`
|
Delay string `json:"delay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ func readParam(r *http.Request, names ...string) string {
|
||||||
|
|
||||||
func readHeaderParam(r *http.Request, names ...string) string {
|
func readHeaderParam(r *http.Request, names ...string) string {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
value := r.Header.Get(name)
|
value := maybeDecodeHeader(r.Header.Get(name))
|
||||||
if value != "" {
|
if value != "" {
|
||||||
return strings.TrimSpace(value)
|
return strings.TrimSpace(value)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue