diff --git a/server/server.go b/server/server.go index f3d2ac51..0b7880cd 100644 --- a/server/server.go +++ b/server/server.go @@ -991,7 +991,14 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi } else if call != "" && !isBoolValue(call) && !phoneNumberRegex.MatchString(call) { return false, false, "", "", "", false, errHTTPBadRequestPhoneNumberInvalid } - messageStr := strings.ReplaceAll(readParam(r, "x-message", "message", "m"), "\\n", "\n") + template = templateMode(readParam(r, "x-template", "template", "tpl")) + var messageStr string + if template.Enabled() && template.Name() == "" { + // don't convert "\n" to literal newline for inline templates + messageStr = readParam(r, "x-message", "message", "m") + } else { + messageStr = strings.ReplaceAll(readParam(r, "x-message", "message", "m"), "\\n", "\n") + } if messageStr != "" { m.Message = messageStr } @@ -1033,7 +1040,6 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi if markdown || strings.ToLower(contentType) == "text/markdown" { m.ContentType = "text/markdown" } - template = templateMode(readParam(r, "x-template", "template", "tpl")) unifiedpush = readBoolParam(r, false, "x-unifiedpush", "unifiedpush", "up") // see GET too! contentEncoding := readParam(r, "content-encoding") if unifiedpush || contentEncoding == "aes128gcm" { @@ -1198,7 +1204,7 @@ func (s *Server) renderTemplate(tpl string, source string) (string, error) { if err := t.Execute(limitWriter, data); err != nil { return "", errHTTPBadRequestTemplateExecuteFailed.Wrap("%s", err.Error()) } - return strings.TrimSpace(buf.String()), nil + return strings.TrimSpace(strings.ReplaceAll(buf.String(), "\\n", "\n")), nil // replace any remaining "\n" (those outside of template curly braces) with newlines } func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser) error { diff --git a/server/server_test.go b/server/server_test.go index 36bbae3f..41633dd5 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -3069,6 +3069,61 @@ func TestServer_MessageTemplate_UnsafeSprigFunctions(t *testing.T) { require.Equal(t, 40043, toHTTPError(t, response.Body.String()).Code) } +func TestServer_MessageTemplate_InlineNewlines(t *testing.T) { + t.Parallel() + s := newTestServer(t, newTestConfig(t)) + response := request(t, s, "PUT", "/mytopic", `{}`, map[string]string{ + "X-Message": `{{"New\nlines"}}`, + "X-Title": `{{"New\nlines"}}`, + "X-Template": "1", + }) + + require.Equal(t, 200, response.Code) + m := toMessage(t, response.Body.String()) + require.Equal(t, `New +lines`, m.Message) + require.Equal(t, `New +lines`, m.Title) +} + +func TestServer_MessageTemplate_InlineNewlinesOutsideOfTemplate(t *testing.T) { + t.Parallel() + s := newTestServer(t, newTestConfig(t)) + response := request(t, s, "PUT", "/mytopic", `{"foo":"bar","food":"bag"}`, map[string]string{ + "X-Message": `{{.foo}}{{"\n"}}{{.food}}`, + "X-Title": `{{.food}}{{"\n"}}{{.foo}}`, + "X-Template": "1", + }) + + require.Equal(t, 200, response.Code) + m := toMessage(t, response.Body.String()) + require.Equal(t, `bar +bag`, m.Message) + require.Equal(t, `bag +bar`, m.Title) +} + +func TestServer_MessageTemplate_TemplateFileNewlines(t *testing.T) { + t.Parallel() + c := newTestConfig(t) + c.TemplateDir = t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(c.TemplateDir, "newline.yml"), []byte(` +title: | + {{.food}}{{"\n"}}{{.foo}} +message: | + {{.foo}}{{"\n"}}{{.food}} +`), 0644)) + s := newTestServer(t, c) + response := request(t, s, "POST", "/mytopic?template=newline", `{"foo":"bar","food":"bag"}`, nil) + fmt.Println(response.Body.String()) + require.Equal(t, 200, response.Code) + m := toMessage(t, response.Body.String()) + require.Equal(t, `bar +bag`, m.Message) + require.Equal(t, `bag +bar`, m.Title) +} + var ( //go:embed testdata/webhook_github_comment_created.json githubCommentCreatedJSON string