diff --git a/server/message.go b/server/message.go index ad870e0..1afa34f 100644 --- a/server/message.go +++ b/server/message.go @@ -1,8 +1,9 @@ package server import ( - "heckel.io/ntfy/util" "time" + + "heckel.io/ntfy/util" ) // List of possible events @@ -18,14 +19,15 @@ const ( // message represents a message published to a topic type message struct { - ID string `json:"id"` // Random message ID - Time int64 `json:"time"` // Unix time in seconds - Event string `json:"event"` // One of the above - Topic string `json:"topic"` - Priority int `json:"priority,omitempty"` - Tags []string `json:"tags,omitempty"` - Title string `json:"title,omitempty"` - Message string `json:"message,omitempty"` + ID string `json:"id"` // Random message ID + Time int64 `json:"time"` // Unix time in seconds + Event string `json:"event"` // One of the above + Topic string `json:"topic"` + Priority int `json:"priority,omitempty"` + Tags []string `json:"tags,omitempty"` + Title string `json:"title,omitempty"` + Message string `json:"message,omitempty"` + UnifiedPush bool `json:"unifiedpush,omitempty"` //this could be 'up' } // messageEncoder is a function that knows how to encode a message diff --git a/server/server.go b/server/server.go index 122b2a0..5a4b4cb 100644 --- a/server/server.go +++ b/server/server.go @@ -5,11 +5,7 @@ import ( "context" "embed" "encoding/json" - firebase "firebase.google.com/go" - "firebase.google.com/go/messaging" "fmt" - "google.golang.org/api/option" - "heckel.io/ntfy/util" "html/template" "io" "log" @@ -20,6 +16,11 @@ import ( "strings" "sync" "time" + + firebase "firebase.google.com/go" + "firebase.google.com/go/messaging" + "google.golang.org/api/option" + "heckel.io/ntfy/util" ) // TODO add "max messages in a topic" limit @@ -253,8 +254,6 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request) error { return s.handleHome(w, r) } else if r.Method == http.MethodGet && r.URL.Path == "/example.html" { return s.handleExample(w, r) - } else if r.Method == http.MethodGet && r.URL.Path == "/up" { - return s.handleUnifiedPush(w, r) } else if r.Method == http.MethodHead && r.URL.Path == "/" { return s.handleEmpty(w, r) } else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) { @@ -295,13 +294,6 @@ func (s *Server) handleExample(w http.ResponseWriter, _ *http.Request) error { return err } -func (s *Server) handleUnifiedPush(w http.ResponseWriter, r *http.Request) error { - w.Header().Set("Content-Type", "application/json") - w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests - _, err := io.WriteString(w, `{"unifiedpush":{"version":1}}`) - return err -} - func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) error { http.FileServer(http.FS(webStaticFsCached)).ServeHTTP(w, r) return nil @@ -323,15 +315,25 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito return err } m := newDefaultMessage(t.ID, strings.TrimSpace(string(b))) - cache, firebase, email, err := s.parseParams(r, m) + cache, firebase, email, unifiedpush, err := s.parseParams(r, m) if err != nil { return err } + + if r.Method == http.MethodGet && unifiedpush { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests + _, err := io.WriteString(w, `{"unifiedpush":{"version":1}}`) + return err + } + if email != "" { if err := v.EmailAllowed(); err != nil { return err } } + + m.UnifiedPush = unifiedpush if s.mailer == nil && email != "" { return errHTTPBadRequest } @@ -344,20 +346,21 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito return err } } - if s.firebase != nil && firebase && !delayed { + if s.firebase != nil && firebase && !delayed && !unifiedpush { go func() { if err := s.firebase(m); err != nil { log.Printf("Unable to publish to Firebase: %v", err.Error()) } }() } - if s.mailer != nil && email != "" && !delayed { + if s.mailer != nil && email != "" && !delayed && !unifiedpush { go func() { if err := s.mailer.Send(v.ip, email, m); err != nil { log.Printf("Unable to send email: %v", err.Error()) } }() } + if cache { if err := s.cache.AddMessage(m); err != nil { return err @@ -365,6 +368,7 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests + if err := json.NewEncoder(w).Encode(m); err != nil { return err } @@ -372,10 +376,11 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito return nil } -func (s *Server) parseParams(r *http.Request, m *message) (cache bool, firebase bool, email string, err error) { +func (s *Server) parseParams(r *http.Request, m *message) (cache bool, firebase bool, email string, unifiedpush bool, err error) { cache = readParam(r, "x-cache", "cache") != "no" firebase = readParam(r, "x-firebase", "firebase") != "no" email = readParam(r, "x-email", "x-e-mail", "email", "e-mail", "mail", "e") + unifiedpush = readParam(r, "up", "unifiedpush") == "1" m.Title = readParam(r, "x-title", "title", "t") messageStr := readParam(r, "x-message", "message", "m") if messageStr != "" { @@ -383,7 +388,7 @@ func (s *Server) parseParams(r *http.Request, m *message) (cache bool, firebase } m.Priority, err = util.ParsePriority(readParam(r, "x-priority", "priority", "prio", "p")) if err != nil { - return false, false, "", errHTTPBadRequest + return false, false, "", false, errHTTPBadRequest } tagsStr := readParam(r, "x-tags", "tags", "tag", "ta") if tagsStr != "" { @@ -395,22 +400,22 @@ func (s *Server) parseParams(r *http.Request, m *message) (cache bool, firebase delayStr := readParam(r, "x-delay", "delay", "x-at", "at", "x-in", "in") if delayStr != "" { if !cache { - return false, false, "", errHTTPBadRequest + return false, false, "", false, errHTTPBadRequest } if email != "" { - return false, false, "", errHTTPBadRequest // we cannot store the email address (yet) + return false, false, "", false, errHTTPBadRequest // we cannot store the email address (yet) } delay, err := util.ParseFutureTime(delayStr, time.Now()) if err != nil { - return false, false, "", errHTTPBadRequest + return false, false, "", false, errHTTPBadRequest } else if delay.Unix() < time.Now().Add(s.config.MinDelay).Unix() { - return false, false, "", errHTTPBadRequest + return false, false, "", false, errHTTPBadRequest } else if delay.Unix() > time.Now().Add(s.config.MaxDelay).Unix() { - return false, false, "", errHTTPBadRequest + return false, false, "", false, errHTTPBadRequest } m.Time = delay.Unix() } - return cache, firebase, email, nil + return cache, firebase, email, unifiedpush, nil } func readParam(r *http.Request, names ...string) string {