WIP: iOS poll_request forwarder

This commit is contained in:
Philipp Heckel 2022-05-27 07:55:57 -04:00
parent 4dabc56952
commit 6a43c1a126
5 changed files with 85 additions and 11 deletions

View file

@ -69,6 +69,7 @@ type Config struct {
AtSenderInterval time.Duration
FirebaseKeepaliveInterval time.Duration
FirebasePollInterval time.Duration
ForwardPollURL string
SMTPSenderAddr string
SMTPSenderUser string
SMTPSenderPass string

View file

@ -3,6 +3,7 @@ package server
import (
"bytes"
"context"
"crypto/sha256"
"embed"
"encoding/base64"
"encoding/json"
@ -93,6 +94,7 @@ const (
firebaseControlTopic = "~control" // See Android if changed
firebasePollTopic = "~poll" // See iOS if changed
emptyMessageBody = "triggered" // Used if message body is empty
newMessageBody = "New message" // Used in poll requests as generic message
defaultAttachmentMessage = "You received a file: %s" // Used if message body is empty, and there is an attachment
encodingBase64 = "base64"
)
@ -422,6 +424,9 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
if err != nil {
return err
}
if m.PollID != "" {
m = newPollRequestMessage(t.ID, m.PollID)
}
if err := s.handlePublishBody(r, v, m, body, unifiedpush); err != nil {
return err
}
@ -448,6 +453,28 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
}
}()
}
if s.config.ForwardPollURL != "" {
go func() {
topicURL := fmt.Sprintf("%s/%s", s.config.BaseURL, m.Topic)
topicHash := fmt.Sprintf("%x", sha256.Sum256([]byte(topicURL)))
forwardURL := fmt.Sprintf("%s/%s", s.config.ForwardPollURL, topicHash)
log.Printf("forwarding: topicURL %s, to upstream url %s", topicURL, forwardURL)
req, err := http.NewRequest("POST", forwardURL, strings.NewReader(""))
if err != nil {
log.Printf("[%s] FWD - Unable to forward poll request: %v", v.ip, err.Error())
return
}
req.Header.Set("X-Poll-ID", m.ID)
response, err := http.DefaultClient.Do(req)
if err != nil {
log.Printf("[%s] FWD - Unable to forward poll request: %v", v.ip, err.Error())
return
} else if response.StatusCode != http.StatusOK {
log.Printf("[%s] FWD - Unable to forward poll request, unexpected status: %d", v.ip, response.StatusCode)
return
}
}()
}
if cache {
if err := s.messageCache.AddMessage(m); err != nil {
return err
@ -549,6 +576,12 @@ func (s *Server) parsePublishParams(r *http.Request, v *visitor, m *message) (ca
firebase = false
unifiedpush = true
}
m.PollID = readParam(r, "x-poll-id", "poll-id", "poll")
if m.PollID != "" {
unifiedpush = false
cache = false
email = ""
}
return cache, firebase, email, unifiedpush, nil
}
@ -565,7 +598,9 @@ func (s *Server) parsePublishParams(r *http.Request, v *visitor, m *message) (ca
// 5. curl -T file.txt ntfy.sh/mytopic
// If file.txt is > message limit, treat it as an attachment
func (s *Server) handlePublishBody(r *http.Request, v *visitor, m *message, body *util.PeekedReadCloser, unifiedpush bool) error {
if unifiedpush {
if m.Event == pollRequestEvent {
return nil // Ignore body
} else if unifiedpush {
return s.handleBodyAsMessageAutoDetect(m, body) // Case 1
} else if m.Attachment != nil && m.Attachment.URL != "" {
return s.handleBodyAsTextMessage(m, body) // Case 2
@ -710,6 +745,7 @@ func (s *Server) handleSubscribeHTTP(w http.ResponseWriter, r *http.Request, v *
w.Header().Set("Access-Control-Allow-Origin", "*") // CORS, allow cross-origin requests
w.Header().Set("Content-Type", contentType+"; charset=utf-8") // Android/Volley client needs charset!
if poll {
log.Printf("polling %#v", r.URL)
return s.sendOldMessages(topics, since, scheduled, sub)
}
subscriberIDs := make([]int, 0)

View file

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"log"
"strings"
firebase "firebase.google.com/go"
@ -64,6 +65,7 @@ func createFirebaseSubscriber(credentialsFile string, auther auth.Auther) (subsc
if err != nil {
return err
}
log.Printf("Sending %#v %#v", m, fbm)
_, err = msg.Send(context.Background(), fbm)
return err
}, nil
@ -98,6 +100,31 @@ func toFirebaseMessage(m *message, auther auth.Auther) (*messaging.Message, erro
CustomData: apnsData,
},
}
case pollRequestEvent:
data = map[string]string{
"id": m.ID,
"time": fmt.Sprintf("%d", m.Time),
"event": m.Event,
"topic": m.Topic,
"message": m.Message,
"poll_id": m.PollID,
}
apnsData := make(map[string]interface{})
for k, v := range data {
apnsData[k] = v
}
apnsConfig = &messaging.APNSConfig{
Payload: &messaging.APNSPayload{
CustomData: apnsData,
Aps: &messaging.Aps{
MutableContent: true,
Alert: &messaging.ApsAlert{
Title: m.Title,
Body: maybeTruncateAPNSBodyMessage(m.Message),
},
},
},
}
case messageEvent:
allowForward := true
if auther != nil {

View file

@ -24,13 +24,14 @@ type message struct {
Time int64 `json:"time"` // Unix time in seconds
Event string `json:"event"` // One of the above
Topic string `json:"topic"`
Title string `json:"title,omitempty"`
Message string `json:"message,omitempty"`
Priority int `json:"priority,omitempty"`
Tags []string `json:"tags,omitempty"`
Click string `json:"click,omitempty"`
Actions []*action `json:"actions,omitempty"`
Attachment *attachment `json:"attachment,omitempty"`
Title string `json:"title,omitempty"`
Message string `json:"message,omitempty"`
PollID string `json:"poll_id,omitempty"`
Encoding string `json:"encoding,omitempty"` // empty for raw UTF-8, or "base64" for encoded bytes
}
@ -84,14 +85,11 @@ type messageEncoder func(msg *message) (string, error)
// newMessage creates a new message with the current timestamp
func newMessage(event, topic, msg string) *message {
return &message{
ID: util.RandomString(messageIDLength),
Time: time.Now().Unix(),
Event: event,
Topic: topic,
Priority: 0,
Tags: nil,
Title: "",
Message: msg,
ID: util.RandomString(messageIDLength),
Time: time.Now().Unix(),
Event: event,
Topic: topic,
Message: msg,
}
}
@ -110,6 +108,13 @@ func newDefaultMessage(topic, msg string) *message {
return newMessage(messageEvent, topic, msg)
}
// newPollRequestMessage is a convenience method to create a poll request message
func newPollRequestMessage(topic, pollID string) *message {
m := newMessage(pollRequestEvent, topic, newMessageBody)
m.PollID = pollID
return m
}
func validMessageID(s string) bool {
return util.ValidRandomString(s, messageIDLength)
}