server_matrix.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. package server
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "heckel.io/ntfy/log"
  7. "heckel.io/ntfy/util"
  8. "io"
  9. "net/http"
  10. "strings"
  11. )
  12. const (
  13. matrixPushKeyHeader = "X-Matrix-Pushkey"
  14. )
  15. type matrixMessage struct {
  16. Notification *matrixNotification `json:"notification"`
  17. }
  18. type matrixNotification struct {
  19. Devices []*matrixDevice `json:"devices"`
  20. }
  21. type matrixDevice struct {
  22. PushKey string `json:"pushkey"`
  23. }
  24. type matrixResponse struct {
  25. Rejected []string `json:"rejected"`
  26. }
  27. type errMatrix struct {
  28. pushKey string
  29. err error
  30. }
  31. func (e errMatrix) Error() string {
  32. if e.err != nil {
  33. return fmt.Sprintf("message with push key %s rejected: %s", e.pushKey, e.err.Error())
  34. }
  35. return fmt.Sprintf("message with push key %s rejected", e.pushKey)
  36. }
  37. func newRequestFromMatrixJSON(r *http.Request, baseURL string, messageLimit int) (*http.Request, error) {
  38. if baseURL == "" {
  39. return nil, errHTTPInternalErrorMissingBaseURL
  40. }
  41. body, err := util.Peek(r.Body, messageLimit)
  42. if err != nil {
  43. return nil, err
  44. }
  45. defer r.Body.Close()
  46. var m matrixMessage
  47. if err := json.NewDecoder(body).Decode(&m); err != nil {
  48. return nil, errHTTPBadRequestMatrixMessageInvalid
  49. } else if m.Notification == nil || len(m.Notification.Devices) == 0 || m.Notification.Devices[0].PushKey == "" {
  50. return nil, errHTTPBadRequestMatrixMessageInvalid
  51. }
  52. pushKey := m.Notification.Devices[0].PushKey
  53. if !strings.HasPrefix(pushKey, baseURL+"/") {
  54. return nil, &errMatrix{pushKey: pushKey, err: errHTTPBadRequestMatrixPushkeyBaseURLMismatch}
  55. }
  56. newRequest, err := http.NewRequest(http.MethodPost, pushKey, io.NopCloser(bytes.NewReader(body.PeekedBytes)))
  57. if err != nil {
  58. return nil, &errMatrix{pushKey: pushKey, err: err}
  59. }
  60. newRequest.Header.Set(matrixPushKeyHeader, pushKey)
  61. return newRequest, nil
  62. }
  63. func handleMatrixDiscovery(w http.ResponseWriter) error {
  64. w.Header().Set("Content-Type", "application/json")
  65. _, err := io.WriteString(w, `{"unifiedpush":{"gateway":"matrix"}}`+"\n")
  66. return err
  67. }
  68. func writeMatrixError(w http.ResponseWriter, r *http.Request, v *visitor, err *errMatrix) error {
  69. log.Debug("%s Matrix gateway error: %s", logHTTPPrefix(v, r), err.Error())
  70. return writeMatrixResponse(w, err.pushKey)
  71. }
  72. func writeMatrixSuccess(w http.ResponseWriter) error {
  73. return writeMatrixResponse(w, "")
  74. }
  75. func writeMatrixResponse(w http.ResponseWriter, rejectedPushKey string) error {
  76. rejected := make([]string, 0)
  77. if rejectedPushKey != "" {
  78. rejected = append(rejected, rejectedPushKey)
  79. }
  80. response := &matrixResponse{
  81. Rejected: rejected,
  82. }
  83. w.Header().Set("Content-Type", "application/json")
  84. if err := json.NewEncoder(w).Encode(response); err != nil {
  85. return err
  86. }
  87. return nil
  88. }