-
Notifications
You must be signed in to change notification settings - Fork 2
/
simplepush.go
128 lines (107 loc) · 3.3 KB
/
simplepush.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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Package simplepush provides a library to send (end-to-end encrypted) push messages to Smartphones via https://simplepush.io
package simplepush
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha1"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"io"
"net/http"
"net/url"
)
// Message contains all the information necessary to send a, potentially encrypted, message.
type Message struct {
SimplePushKey string // Your simeplepush.io key
Title string // Title of your message
Message string // Message body
Event string // The event the message should be associated with
Encrypt bool // If set, the message will be sent end-to-end encrypted with the provided Password/Salt. If false, the message is sent unencrypted.
Password string // Your password
Salt string // If set, this salt is used, otherwise the default one gets used.
}
var defaultSalt = "1789F0B8C4A051E5"
// APIUrl is the public API entry point for https://simplepush.io. It is public to allow overriding in case
// of simplepush.io compatible services.
var APIUrl = "https://api.simplepush.io/"
func paddingPKCS5(src []byte, blockSize int) []byte {
padding := blockSize - len(src)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(src, padtext...)
}
func encrypt(key, iv, buf []byte) (string, error) {
block, err := aes.NewCipher(key)
if err != nil {
return "", err
}
buf = paddingPKCS5(buf, block.BlockSize())
if len(buf)%block.BlockSize() != 0 {
return "", errors.New("Plaintext is not a multiple of the block size")
}
mode := cipher.NewCBCEncrypter(block, iv)
mode.CryptBlocks(buf, buf)
return base64.URLEncoding.EncodeToString(buf), nil
}
// Send takes a message of type Message and sends it via the simplepush.io API.
// Please refer to the documentation of the Message struct for further information.
func Send(m Message) error {
title := m.Title
message := m.Message
var iv []byte
if m.SimplePushKey == "" {
return errors.New("simplepush.io key is not allowed to be empty")
}
if message == "" { // actually this is not true, but the upstream python lib handles it like that
return errors.New("Message is not allowed to be empty")
}
if m.Encrypt {
var err error
salt := defaultSalt
if m.Salt != "" {
salt = m.Salt
}
sha := sha1.Sum([]byte(m.Password + salt))
key, _ := hex.DecodeString(fmt.Sprintf("%X", sha)[:32])
iv = make([]byte, aes.BlockSize)
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return err
}
if title, err = encrypt(key, iv, []byte(title)); err != nil {
return err
}
if message, err = encrypt(key, iv, []byte(message)); err != nil {
return err
}
}
data := url.Values{}
data.Set("key", m.SimplePushKey)
data.Add("title", title)
data.Add("msg", message)
data.Add("event", m.Event)
if m.Encrypt {
data.Add("encrypted", "true")
data.Add("iv", fmt.Sprintf("%X", iv))
}
u, err := url.ParseRequestURI(APIUrl)
if err != nil {
return err
}
resource := "/send"
u.Path = resource
urlStr := fmt.Sprintf("%v", u)
tr := &http.Transport{
TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper),
}
client := &http.Client{Transport: tr}
resp, err := client.PostForm(urlStr, data)
if err != nil {
return err
}
resp.Body.Close()
return nil
}