ntfy/server/util.go

144 lines
3.8 KiB
Go
Raw Normal View History

2022-01-16 04:17:46 +00:00
package server
import (
2022-04-19 13:14:32 +00:00
"encoding/json"
"fmt"
"heckel.io/ntfy/util"
2022-01-16 04:17:46 +00:00
"net/http"
"strings"
)
2022-04-20 20:31:25 +00:00
const (
actionIDLength = 10
actionsMax = 3
)
2022-01-16 04:17:46 +00:00
func readBoolParam(r *http.Request, defaultValue bool, names ...string) bool {
value := strings.ToLower(readParam(r, names...))
if value == "" {
return defaultValue
}
return value == "1" || value == "yes" || value == "true"
}
func readParam(r *http.Request, names ...string) string {
value := readHeaderParam(r, names...)
if value != "" {
return value
}
return readQueryParam(r, names...)
}
func readHeaderParam(r *http.Request, names ...string) string {
2022-01-16 04:17:46 +00:00
for _, name := range names {
value := r.Header.Get(name)
if value != "" {
return strings.TrimSpace(value)
}
}
return ""
}
func readQueryParam(r *http.Request, names ...string) string {
2022-01-16 04:17:46 +00:00
for _, name := range names {
value := r.URL.Query().Get(strings.ToLower(name))
if value != "" {
return strings.TrimSpace(value)
}
}
return ""
}
2022-04-19 13:14:32 +00:00
func parseActions(s string) (actions []*action, err error) {
2022-04-20 03:26:46 +00:00
// Parse JSON or simple format
2022-04-19 13:14:32 +00:00
s = strings.TrimSpace(s)
if strings.HasPrefix(s, "[") {
actions, err = parseActionsFromJSON(s)
} else {
actions, err = parseActionsFromSimple(s)
}
if err != nil {
return nil, err
}
2022-04-20 03:26:46 +00:00
// Add ID field
2022-04-19 13:14:32 +00:00
for i := range actions {
actions[i].ID = util.RandomString(actionIDLength)
2022-04-20 03:26:46 +00:00
}
// Validate
2022-04-20 20:31:25 +00:00
if len(actions) > actionsMax {
return nil, fmt.Errorf("too many actions, only %d allowed", actionsMax)
}
2022-04-20 03:26:46 +00:00
for _, action := range actions {
if !util.InStringList([]string{"view", "broadcast", "http"}, action.Action) {
return nil, fmt.Errorf("cannot parse actions: action '%s' unknown", action.Action)
} else if action.Label == "" {
2022-04-19 13:14:32 +00:00
return nil, fmt.Errorf("cannot parse actions: label must be set")
2022-04-20 20:31:25 +00:00
} else if util.InStringList([]string{"view", "http"}, action.Action) && action.URL == "" {
2022-04-20 03:26:46 +00:00
return nil, fmt.Errorf("parameter 'url' is required for action '%s'", action.Action)
2022-04-19 13:14:32 +00:00
}
}
2022-04-20 03:26:46 +00:00
2022-04-19 13:14:32 +00:00
return actions, nil
}
func parseActionsFromJSON(s string) ([]*action, error) {
actions := make([]*action, 0)
if err := json.Unmarshal([]byte(s), &actions); err != nil {
return nil, err
}
return actions, nil
}
func parseActionsFromSimple(s string) ([]*action, error) {
actions := make([]*action, 0)
rawActions := util.SplitNoEmpty(s, ";")
for _, rawAction := range rawActions {
2022-04-21 13:58:28 +00:00
newAction := &action{
Headers: make(map[string]string),
Extras: make(map[string]string),
}
2022-04-19 13:14:32 +00:00
parts := util.SplitNoEmpty(rawAction, ",")
if len(parts) < 3 {
return nil, fmt.Errorf("cannot parse action: action requires at least keys 'action', 'label' and one parameter: %s", rawAction)
}
for i, part := range parts {
key, value := util.SplitKV(part, "=")
if key == "" && i == 0 {
newAction.Action = value
} else if key == "" && i == 1 {
newAction.Label = value
2022-04-20 20:31:25 +00:00
} else if key == "" && util.InStringList([]string{"view", "http"}, newAction.Action) && i == 2 {
newAction.URL = value
2022-04-21 13:58:28 +00:00
} else if strings.HasPrefix(key, "headers.") {
newAction.Headers[strings.TrimPrefix(key, "headers.")] = value
} else if strings.HasPrefix(key, "extras.") {
newAction.Extras[strings.TrimPrefix(key, "extras.")] = value
2022-04-19 13:14:32 +00:00
} else if key != "" {
switch strings.ToLower(key) {
case "action":
newAction.Action = value
case "label":
newAction.Label = value
case "clear":
lvalue := strings.ToLower(value)
newAction.Clear = lvalue == "true" || lvalue == "yes" || lvalue == "1"
2022-04-19 13:14:32 +00:00
case "url":
newAction.URL = value
case "method":
newAction.Method = value
case "body":
newAction.Body = value
default:
2022-04-21 13:58:28 +00:00
return nil, fmt.Errorf("cannot parse action: key '%s' not supported, please use JSON format instead", part)
2022-04-19 13:14:32 +00:00
}
} else {
2022-04-21 13:58:28 +00:00
return nil, fmt.Errorf("cannot parse action: unknown phrase '%s'", part)
2022-04-19 13:14:32 +00:00
}
}
actions = append(actions, newAction)
}
return actions, nil
}