diff options
Diffstat (limited to 'vendor/github.com/Rhymen/go-whatsapp/write.go')
-rw-r--r-- | vendor/github.com/Rhymen/go-whatsapp/write.go | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/vendor/github.com/Rhymen/go-whatsapp/write.go b/vendor/github.com/Rhymen/go-whatsapp/write.go new file mode 100644 index 00000000..e427825c --- /dev/null +++ b/vendor/github.com/Rhymen/go-whatsapp/write.go @@ -0,0 +1,195 @@ +package whatsapp + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/json" + "fmt" + "strconv" + + "time" + + "github.com/Rhymen/go-whatsapp/binary" + "github.com/Rhymen/go-whatsapp/crypto/cbc" + "github.com/gorilla/websocket" + "github.com/pkg/errors" +) + +func (wac *Conn) addListener(ch chan string, messageTag string) { + wac.listener.Lock() + wac.listener.m[messageTag] = ch + wac.listener.Unlock() +} + +func (wac *Conn) removeListener(answerMessageTag string) { + wac.listener.Lock() + delete(wac.listener.m, answerMessageTag) + wac.listener.Unlock() +} + +//writeJson enqueues a json message into the writeChan +func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) { + + ch := make(chan string, 1) + + wac.writerLock.Lock() + defer wac.writerLock.Unlock() + + d, err := json.Marshal(data) + if err != nil { + close(ch) + return ch, err + } + + ts := time.Now().Unix() + messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount) + bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d)) + + if wac.timeTag == "" { + tss := fmt.Sprintf("%d", ts) + wac.timeTag = tss[len(tss)-3:] + } + + wac.addListener(ch, messageTag) + + err = wac.write(websocket.TextMessage, bytes) + if err != nil { + close(ch) + wac.removeListener(messageTag) + return ch, err + } + + wac.msgCount++ + return ch, nil +} + +func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) { + + ch := make(chan string, 1) + + if len(messageTag) < 2 { + close(ch) + return ch, ErrMissingMessageTag + } + + wac.writerLock.Lock() + defer wac.writerLock.Unlock() + + data, err := wac.encryptBinaryMessage(node) + if err != nil { + close(ch) + return ch, errors.Wrap(err, "encryptBinaryMessage(node) failed") + } + + bytes := []byte(messageTag + ",") + bytes = append(bytes, byte(metric), byte(flag)) + bytes = append(bytes, data...) + + wac.addListener(ch, messageTag) + + err = wac.write(websocket.BinaryMessage, bytes) + if err != nil { + close(ch) + wac.removeListener(messageTag) + return ch, errors.Wrap(err, "failed to write message") + } + + wac.msgCount++ + return ch, nil +} + +func (wac *Conn) sendKeepAlive() error { + + respChan := make(chan string, 1) + wac.addListener(respChan, "!") + + bytes := []byte("?,,") + err := wac.write(websocket.TextMessage, bytes) + if err != nil { + close(respChan) + wac.removeListener("!") + return errors.Wrap(err, "error sending keepAlive") + } + + select { + case resp := <-respChan: + msecs, err := strconv.ParseInt(resp, 10, 64) + if err != nil { + return errors.Wrap(err, "Error converting time string to uint") + } + wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond)) + + case <-time.After(wac.msgTimeout): + return ErrConnectionTimeout + } + + return nil +} + +/* + When phone is unreachable, WhatsAppWeb sends ["admin","test"] time after time to try a successful contact. + Tested with Airplane mode and no connection at all. +*/ +func (wac *Conn) sendAdminTest() (bool, error) { + data := []interface{}{"admin", "test"} + + r, err := wac.writeJson(data) + if err != nil { + return false, errors.Wrap(err, "error sending admin test") + } + + var response []interface{} + + select { + case resp := <-r: + if err := json.Unmarshal([]byte(resp), &response); err != nil { + return false, fmt.Errorf("error decoding response message: %v\n", err) + } + case <-time.After(wac.msgTimeout): + return false, ErrConnectionTimeout + } + + if len(response) == 2 && response[0].(string) == "Pong" && response[1].(bool) == true { + return true, nil + } else { + return false, nil + } +} + +func (wac *Conn) write(messageType int, data []byte) error { + + if wac == nil || wac.ws == nil { + return ErrInvalidWebsocket + } + + wac.ws.Lock() + err := wac.ws.conn.WriteMessage(messageType, data) + wac.ws.Unlock() + + if err != nil { + return errors.Wrap(err, "error writing to websocket") + } + + return nil +} + +func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) { + b, err := binary.Marshal(node) + if err != nil { + return nil, errors.Wrap(err, "binary node marshal failed") + } + + cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b) + if err != nil { + return nil, errors.Wrap(err, "encrypt failed") + } + + h := hmac.New(sha256.New, wac.session.MacKey) + h.Write(cipher) + hash := h.Sum(nil) + + data = append(data, hash[:32]...) + data = append(data, cipher...) + + return data, nil +} |