UTF-8 headers
This commit is contained in:
parent
91d2603fe0
commit
cfa8d92af1
4 changed files with 33 additions and 5 deletions
|
@ -106,6 +106,7 @@ var (
|
||||||
errHTTPBadRequestNotAPaidUser = &errHTTP{40027, http.StatusBadRequest, "invalid request: not a paid user", "", nil}
|
errHTTPBadRequestNotAPaidUser = &errHTTP{40027, http.StatusBadRequest, "invalid request: not a paid user", "", nil}
|
||||||
errHTTPBadRequestBillingRequestInvalid = &errHTTP{40028, http.StatusBadRequest, "invalid request: not a valid billing request", "", nil}
|
errHTTPBadRequestBillingRequestInvalid = &errHTTP{40028, http.StatusBadRequest, "invalid request: not a valid billing request", "", nil}
|
||||||
errHTTPBadRequestBillingSubscriptionExists = &errHTTP{40029, http.StatusBadRequest, "invalid request: billing subscription already exists", "", nil}
|
errHTTPBadRequestBillingSubscriptionExists = &errHTTP{40029, http.StatusBadRequest, "invalid request: billing subscription already exists", "", nil}
|
||||||
|
errHTTPBadRequestInvalidMimeHeader = &errHTTP{40030, http.StatusBadRequest, "invalid request: invalid MIME encoding of header", "", nil}
|
||||||
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil}
|
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil}
|
||||||
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil}
|
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil}
|
||||||
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil}
|
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil}
|
||||||
|
|
|
@ -843,10 +843,17 @@ func (s *Server) forwardPollRequest(v *visitor, m *message) {
|
||||||
func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, firebase bool, email string, unifiedpush bool, err *errHTTP) {
|
func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, firebase bool, email 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 = 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")
|
||||||
|
title := readParam(r, "x-title", "title", "t")
|
||||||
|
if title != "" {
|
||||||
|
title, err := mimeDecoder.DecodeHeader(title)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, "", false, errHTTPBadRequestInvalidMimeHeader.Wrap("invalid X-Title header: %s", err.Error())
|
||||||
|
}
|
||||||
|
m.Title = title
|
||||||
|
}
|
||||||
attach := readParam(r, "x-attach", "attach", "a")
|
attach := readParam(r, "x-attach", "attach", "a")
|
||||||
if attach != "" || filename != "" {
|
if attach != "" || filename != "" {
|
||||||
m.Attachment = &attachment{}
|
m.Attachment = &attachment{}
|
||||||
|
@ -884,6 +891,10 @@ 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 != "" {
|
||||||
|
messageStr, err := mimeDecoder.DecodeHeader(messageStr)
|
||||||
|
if err != nil {
|
||||||
|
return false, false, "", false, errHTTPBadRequestInvalidMimeHeader.Wrap("invalid X-Message header: %s", err.Error())
|
||||||
|
}
|
||||||
m.Message = messageStr
|
m.Message = messageStr
|
||||||
}
|
}
|
||||||
var e error
|
var e error
|
||||||
|
|
|
@ -21,8 +21,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"heckel.io/ntfy/log"
|
"heckel.io/ntfy/log"
|
||||||
"heckel.io/ntfy/util"
|
"heckel.io/ntfy/util"
|
||||||
|
@ -2106,8 +2104,8 @@ func TestServer_PublishWhileUpdatingStatsWithLotsOfMessages(t *testing.T) {
|
||||||
start = time.Now()
|
start = time.Now()
|
||||||
response := request(t, s, "PUT", "/mytopic", "some body", nil)
|
response := request(t, s, "PUT", "/mytopic", "some body", nil)
|
||||||
m := toMessage(t, response.Body.String())
|
m := toMessage(t, response.Body.String())
|
||||||
assert.Equal(t, "some body", m.Message)
|
require.Equal(t, "some body", m.Message)
|
||||||
assert.True(t, time.Since(start) < 100*time.Millisecond)
|
require.True(t, time.Since(start) < 100*time.Millisecond)
|
||||||
log.Info("Done: Publishing message; took %s", time.Since(start).Round(time.Millisecond))
|
log.Info("Done: Publishing message; took %s", time.Since(start).Round(time.Millisecond))
|
||||||
|
|
||||||
// Wait for all goroutines
|
// Wait for all goroutines
|
||||||
|
@ -2469,6 +2467,21 @@ func TestServer_MessageCountPersistence(t *testing.T) {
|
||||||
require.Equal(t, int64(1234), s.messages)
|
require.Equal(t, int64(1234), s.messages)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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-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.?=",
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
func newTestConfig(t *testing.T) *Config {
|
func newTestConfig(t *testing.T) *Config {
|
||||||
conf := NewConfig()
|
conf := NewConfig()
|
||||||
conf.BaseURL = "http://127.0.0.1:12345"
|
conf.BaseURL = "http://127.0.0.1:12345"
|
||||||
|
|
|
@ -5,11 +5,14 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"heckel.io/ntfy/util"
|
"heckel.io/ntfy/util"
|
||||||
"io"
|
"io"
|
||||||
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var mimeDecoder mime.WordDecoder
|
||||||
|
|
||||||
func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool {
|
func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool {
|
||||||
value := strings.ToLower(readParam(r, names...))
|
value := strings.ToLower(readParam(r, names...))
|
||||||
if value == "" {
|
if value == "" {
|
||||||
|
|
Loading…
Reference in a new issue