Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ package pushreceiver

import "github.com/pkg/errors"

type GCMError string

func (err GCMError) Error() string {
return "GCM error: " + string(err)
}

const ErrGCMDeviceRegistration = GCMError("PHONE_REGISTRATION_ERROR")

// ErrGcmAuthorization is authorization error of GCM.
var ErrGcmAuthorization = errors.New("GCM authorization error")

Expand Down
6 changes: 5 additions & 1 deletion events.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ type MessageEvent struct {
}

func newMessageEvent(data *pb.DataMessageStanza) *MessageEvent {
appID := data.GetAppID()
if appID == "" {
appID = data.GetCategory()
}
return &MessageEvent{
PersistentID: data.GetPersistentId(),
From: data.GetFrom(),
Expand All @@ -65,7 +69,7 @@ func newMessageEvent(data *pb.DataMessageStanza) *MessageEvent {
Token: data.GetToken(),
RegID: data.GetRegId(),
RawData: data.GetRawData(),
AppID: data.GetAppID(),
AppID: appID,
}
}

Expand Down
65 changes: 49 additions & 16 deletions gcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,44 @@ type GCMRegistrationOpts struct {
Expiry time.Duration
}

func setGCMAppID(values url.Values, appID string) {
if len(appID) == 32 && strings.Trim(appID, "abcdefghijklmnop") == "" {
values.Set("app", appID)
} else {
values.Set("app", "org.chromium.linux")
values.Set("X-subtype", appID)
}
}

func NewGCMAppID(authorizationEntity string) string {
if _, err := strconv.ParseUint(authorizationEntity, 10, 64); err != nil {
return "wp:" + strings.ToUpper(uuid.New().String())
}
return uuid.NewString()
}

func RegisterGCM(ctx context.Context, authorizationEntity string, creds GCMCredentials, opts *GCMRegistrationOpts) (*FCMCredentials, error) {
values := url.Values{}

var appID string
if opts != nil && opts.AppID != "" {
appID = opts.AppID
} else {
appID = "wp:" + strings.ToUpper(uuid.New().String())
appID = NewGCMAppID(authorizationEntity)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is the only actually necessary change and it broke when the wp: prefix was added (web push started requiring the prefix in february)

The rest of the changes look reasonable too

}

if opts != nil && opts.InstanceID != "" {
values.Set("appId", opts.InstanceID)
values.Set("appid", opts.InstanceID)
}

if opts != nil && opts.Expiry != 0 {
ttl := strconv.Itoa(int(opts.Expiry.Seconds()))
values.Set("ttl", ttl)
}

values.Set("app", "org.chromium.linux")
setGCMAppID(values, appID)
values.Set("scope", "GCM")
values.Set("X-scope", "GCM")
values.Set("X-subtype", appID)
values.Set("device", fmt.Sprint(creds.AndroidID))
values.Set("gmsv", strings.Split(chromeVersion, ".")[0])
values.Set("sender", authorizationEntity)
Expand All @@ -61,18 +76,14 @@ func RegisterGCM(ctx context.Context, authorizationEntity string, creds GCMCrede
if err != nil {
return nil, errors.Wrap(err, "request GCM register")
}
defer closeResponse(res)

data, err := io.ReadAll(res.Body)
subscription, err := parseGCMResponse(res)
if err != nil {
return nil, errors.Wrap(err, "read GCM register response")
}

subscription, err := url.ParseQuery(string(data))
if err != nil {
return nil, errors.Wrap(err, "parse GCM register URL")
}
token := subscription.Get("token")
if token == "" {
return nil, errors.New("GCM register response missing token")
}

return &FCMCredentials{
GCM: creds,
Expand All @@ -83,10 +94,9 @@ func RegisterGCM(ctx context.Context, authorizationEntity string, creds GCMCrede

func UnregisterGCM(ctx context.Context, authorizationEntity string, creds GCMCredentials, appID string) error {
values := url.Values{}
values.Set("app", "org.chromium.linux")
setGCMAppID(values, appID)
values.Set("scope", "GCM")
values.Set("X-scope", "GCM")
values.Set("X-subtype", appID)
values.Set("device", fmt.Sprint(creds.AndroidID))
values.Set("gmsv", strings.Split(chromeVersion, ".")[0])
values.Set("sender", authorizationEntity)
Expand All @@ -99,9 +109,32 @@ func UnregisterGCM(ctx context.Context, authorizationEntity string, creds GCMCre
return errors.Wrap(err, "failed to unregister with GCM")
}

if res.StatusCode != http.StatusOK {
return errors.New("failed to unregister with GCM")
response, err := parseGCMResponse(res)
if err != nil {
return errors.Wrap(err, "read GCM unregister response")
}
if response.Get("token") == "" && response.Get("deleted") != appID {
return errors.New("GCM unregister response missing confirmation")
}

return nil
}

func parseGCMResponse(res *http.Response) (url.Values, error) {
defer closeResponse(res)
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected HTTP status %d", res.StatusCode)
}
data, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
values, err := url.ParseQuery(string(data))
if err != nil {
return nil, err
}
if message := values.Get("Error"); message != "" {
return nil, GCMError(message)
}
return values, nil
}
Loading