/
apns.go
110 lines (85 loc) · 2.52 KB
/
apns.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package push
import (
"encoding/base64"
"strings"
"github.com/pkg/errors"
"berty.tech/core/chunk"
"berty.tech/core/pkg/errorcodes"
"github.com/sideshow/apns2"
"github.com/sideshow/apns2/certificate"
"github.com/sideshow/apns2/payload"
)
const asn1UID = "0.9.2342.19200300.100.1.1"
const appleCertDevNamePart = "Apple Development IOS Push Services"
type APNSDispatcher struct {
bundleID string
client *apns2.Client
}
var _ Dispatcher = &APNSDispatcher{}
func NewAPNSDispatcher(path string, forceDev bool) (Dispatcher, error) {
cert, err := certificate.FromP12File(path, "")
if err != nil {
return nil, errorcodes.ErrPushInvalidServerConfig.Wrap(err)
}
bundleID := ""
for _, kv := range cert.Leaf.Subject.Names {
if kv.Type.String() == asn1UID {
bundleID = kv.Value.(string)
break
}
}
if bundleID == "" {
return nil, errorcodes.ErrPushMissingBundleId.New()
}
production := !strings.Contains(cert.Leaf.Subject.CommonName, appleCertDevNamePart)
client := apns2.NewClient(cert)
if !forceDev && production {
client = client.Production()
} else {
client = client.Development()
}
dispatcher := &APNSDispatcher{
bundleID: bundleID,
client: client,
}
return dispatcher, nil
}
func (d *APNSDispatcher) CanDispatch(pushAttrs *PushData, pushDestination *PushDestination) bool {
if pushDestination.PushType != DevicePushType_APNS {
return false
}
apnsIdentifier := &PushNativeIdentifier{}
if err := apnsIdentifier.Unmarshal(pushDestination.PushId); err != nil {
return false
}
if d.bundleID != apnsIdentifier.PackageID {
return false
}
return true
}
func (d *APNSDispatcher) Dispatch(pushAttrs *PushData, pushDestination *PushDestination) error {
apnsIdentifier := &PushNativeIdentifier{}
if err := apnsIdentifier.Unmarshal(pushDestination.PushId); err != nil {
return errorcodes.ErrPushUnknownDestination.Wrap(err)
}
chunks, err := chunk.SplitMarshal(pushAttrs.Envelope, 2000)
if err != nil {
return err
}
for _, chunk := range chunks {
pushPayload := payload.NewPayload()
pushPayload.Custom("chunk", base64.StdEncoding.EncodeToString(chunk))
pushPayload.ContentAvailable()
notification := &apns2.Notification{}
notification.DeviceToken = apnsIdentifier.DeviceToken
notification.Topic = d.bundleID
notification.Payload = pushPayload
response, err := d.client.Push(notification)
if err != nil {
return errorcodes.ErrPushProvider.Wrap(err)
} else if response.StatusCode != 200 {
return errorcodes.ErrPushProvider.Wrap(errors.New(response.Reason))
}
}
return nil
}