summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Rhymen/go-whatsapp/read.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Rhymen/go-whatsapp/read.go')
-rw-r--r--vendor/github.com/Rhymen/go-whatsapp/read.go142
1 files changed, 142 insertions, 0 deletions
diff --git a/vendor/github.com/Rhymen/go-whatsapp/read.go b/vendor/github.com/Rhymen/go-whatsapp/read.go
new file mode 100644
index 00000000..a69147a6
--- /dev/null
+++ b/vendor/github.com/Rhymen/go-whatsapp/read.go
@@ -0,0 +1,142 @@
+package whatsapp
+
+import (
+ "crypto/hmac"
+ "crypto/sha256"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "strings"
+
+ "github.com/Rhymen/go-whatsapp/binary"
+ "github.com/Rhymen/go-whatsapp/crypto/cbc"
+ "github.com/gorilla/websocket"
+ "github.com/pkg/errors"
+)
+
+func (wac *Conn) readPump() {
+ defer func() {
+ wac.wg.Done()
+ _, _ = wac.Disconnect()
+ }()
+
+ var readErr error
+ var msgType int
+ var reader io.Reader
+
+ for {
+ readerFound := make(chan struct{})
+ go func() {
+ if wac.ws != nil {
+ msgType, reader, readErr = wac.ws.conn.NextReader()
+ }
+ close(readerFound)
+ }()
+ select {
+ case <-readerFound:
+ if readErr != nil {
+ wac.handle(&ErrConnectionFailed{Err: readErr})
+ return
+ }
+ msg, err := ioutil.ReadAll(reader)
+ if err != nil {
+ wac.handle(errors.Wrap(err, "error reading message from Reader"))
+ continue
+ }
+ err = wac.processReadData(msgType, msg)
+ if err != nil {
+ wac.handle(errors.Wrap(err, "error processing data"))
+ }
+ case <-wac.ws.close:
+ return
+ }
+ }
+}
+
+func (wac *Conn) processReadData(msgType int, msg []byte) error {
+ data := strings.SplitN(string(msg), ",", 2)
+
+ if data[0][0] == '!' { //Keep-Alive Timestamp
+ data = append(data, data[0][1:]) //data[1]
+ data[0] = "!"
+ }
+
+ if len(data) == 2 && len(data[1]) == 0 {
+ return nil
+ }
+
+ if len(data) != 2 || len(data[1]) == 0 {
+ return ErrInvalidWsData
+ }
+
+ wac.listener.RLock()
+ listener, hasListener := wac.listener.m[data[0]]
+ wac.listener.RUnlock()
+
+ if hasListener {
+ // listener only exists for TextMessages query messages out of contact.go
+ // If these binary query messages can be handled another way,
+ // then the TextMessages, which are all JSON encoded, can directly
+ // be unmarshalled. The listener chan could then be changed from type
+ // chan string to something like chan map[string]interface{}. The unmarshalling
+ // in several places, especially in session.go, would then be gone.
+ listener <- data[1]
+ close(listener)
+ wac.removeListener(data[0])
+ } else if msgType == websocket.BinaryMessage {
+ wac.loginSessionLock.RLock()
+ sess := wac.session
+ wac.loginSessionLock.RUnlock()
+ if sess == nil || sess.MacKey == nil || sess.EncKey == nil {
+ return ErrInvalidWsState
+ }
+ message, err := wac.decryptBinaryMessage([]byte(data[1]))
+ if err != nil {
+ return errors.Wrap(err, "error decoding binary")
+ }
+ wac.dispatch(message)
+ } else { //RAW json status updates
+ wac.handle(string(data[1]))
+ }
+ return nil
+}
+
+func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
+ //message validation
+ h2 := hmac.New(sha256.New, wac.session.MacKey)
+ if len(msg) < 33 {
+ var response struct {
+ Status int `json:"status"`
+ }
+
+ if err := json.Unmarshal(msg, &response); err == nil {
+ if response.Status == http.StatusNotFound {
+ return nil, ErrServerRespondedWith404
+ }
+ return nil, errors.New(fmt.Sprintf("server responded with %d", response.Status))
+ }
+
+ return nil, ErrInvalidServerResponse
+
+ }
+ h2.Write([]byte(msg[32:]))
+ if !hmac.Equal(h2.Sum(nil), msg[:32]) {
+ return nil, ErrInvalidHmac
+ }
+
+ // message decrypt
+ d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
+ if err != nil {
+ return nil, errors.Wrap(err, "decrypting message with AES-CBC failed")
+ }
+
+ // message unmarshal
+ message, err := binary.Unmarshal(d)
+ if err != nil {
+ return nil, errors.Wrap(err, "could not decode binary")
+ }
+
+ return message, nil
+}