diff options
Diffstat (limited to 'vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go')
-rw-r--r-- | vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go b/vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go new file mode 100644 index 00000000..95da606c --- /dev/null +++ b/vendor/github.com/Rhymen/go-whatsapp/binary/decoder.go @@ -0,0 +1,388 @@ +package binary + +import ( + "fmt" + "github.com/Rhymen/go-whatsapp/binary/token" + "io" + "strconv" +) + +type binaryDecoder struct { + data []byte + index int +} + +func NewDecoder(data []byte) *binaryDecoder { + return &binaryDecoder{data, 0} +} + +func (r *binaryDecoder) checkEOS(length int) error { + if r.index+length > len(r.data) { + return io.EOF + } + + return nil +} + +func (r *binaryDecoder) readByte() (byte, error) { + if err := r.checkEOS(1); err != nil { + return 0, err + } + + b := r.data[r.index] + r.index++ + + return b, nil +} + +func (r *binaryDecoder) readIntN(n int, littleEndian bool) (int, error) { + if err := r.checkEOS(n); err != nil { + return 0, err + } + + var ret int + + for i := 0; i < n; i++ { + var curShift int + if littleEndian { + curShift = i + } else { + curShift = n - i - 1 + } + ret |= int(r.data[r.index+i]) << uint(curShift*8) + } + + r.index += n + return ret, nil +} + +func (r *binaryDecoder) readInt8(littleEndian bool) (int, error) { + return r.readIntN(1, littleEndian) +} + +func (r *binaryDecoder) readInt16(littleEndian bool) (int, error) { + return r.readIntN(2, littleEndian) +} + +func (r *binaryDecoder) readInt20() (int, error) { + if err := r.checkEOS(3); err != nil { + return 0, err + } + + ret := ((int(r.data[r.index]) & 15) << 16) + (int(r.data[r.index+1]) << 8) + int(r.data[r.index+2]) + r.index += 3 + return ret, nil +} + +func (r *binaryDecoder) readInt32(littleEndian bool) (int, error) { + return r.readIntN(4, littleEndian) +} + +func (r *binaryDecoder) readInt64(littleEndian bool) (int, error) { + return r.readIntN(8, littleEndian) +} + +func (r *binaryDecoder) readPacked8(tag int) (string, error) { + startByte, err := r.readByte() + if err != nil { + return "", err + } + + ret := "" + + for i := 0; i < int(startByte&127); i++ { + currByte, err := r.readByte() + if err != nil { + return "", err + } + + lower, err := unpackByte(tag, currByte&0xF0>>4) + if err != nil { + return "", err + } + + upper, err := unpackByte(tag, currByte&0x0F) + if err != nil { + return "", err + } + + ret += lower + upper + } + + if startByte>>7 != 0 { + ret = ret[:len(ret)-1] + } + return ret, nil +} + +func unpackByte(tag int, value byte) (string, error) { + switch tag { + case token.NIBBLE_8: + return unpackNibble(value) + case token.HEX_8: + return unpackHex(value) + default: + return "", fmt.Errorf("unpackByte with unknown tag %d", tag) + } +} + +func unpackNibble(value byte) (string, error) { + switch { + case value < 0 || value > 15: + return "", fmt.Errorf("unpackNibble with value %d", value) + case value == 10: + return "-", nil + case value == 11: + return ".", nil + case value == 15: + return "\x00", nil + default: + return strconv.Itoa(int(value)), nil + } +} + +func unpackHex(value byte) (string, error) { + switch { + case value < 0 || value > 15: + return "", fmt.Errorf("unpackHex with value %d", value) + case value < 10: + return strconv.Itoa(int(value)), nil + default: + return string('A' + value - 10), nil + } +} + +func (r *binaryDecoder) readListSize(tag int) (int, error) { + switch tag { + case token.LIST_EMPTY: + return 0, nil + case token.LIST_8: + return r.readInt8(false) + case token.LIST_16: + return r.readInt16(false) + default: + return 0, fmt.Errorf("readListSize with unknown tag %d at position %d", tag, r.index) + } +} + +func (r *binaryDecoder) readString(tag int) (string, error) { + switch { + case tag >= 3 && tag <= len(token.SingleByteTokens): + tok, err := token.GetSingleToken(tag) + if err != nil { + return "", err + } + + if tok == "s.whatsapp.net" { + tok = "c.us" + } + + return tok, nil + case tag == token.DICTIONARY_0 || tag == token.DICTIONARY_1 || tag == token.DICTIONARY_2 || tag == token.DICTIONARY_3: + i, err := r.readInt8(false) + if err != nil { + return "", err + } + + return token.GetDoubleToken(tag-token.DICTIONARY_0, i) + case tag == token.LIST_EMPTY: + return "", nil + case tag == token.BINARY_8: + length, err := r.readInt8(false) + if err != nil { + return "", err + } + + return r.readStringFromChars(length) + case tag == token.BINARY_20: + length, err := r.readInt20() + if err != nil { + return "", err + } + + return r.readStringFromChars(length) + case tag == token.BINARY_32: + length, err := r.readInt32(false) + if err != nil { + return "", err + } + + return r.readStringFromChars(length) + case tag == token.JID_PAIR: + b, err := r.readByte() + if err != nil { + return "", err + } + i, err := r.readString(int(b)) + if err != nil { + return "", err + } + + b, err = r.readByte() + if err != nil { + return "", err + } + j, err := r.readString(int(b)) + if err != nil { + return "", err + } + + if i == "" || j == "" { + return "", fmt.Errorf("invalid jid pair: %s - %s", i, j) + } + + return i + "@" + j, nil + case tag == token.NIBBLE_8 || tag == token.HEX_8: + return r.readPacked8(tag) + default: + return "", fmt.Errorf("invalid string with tag %d", tag) + } +} + +func (r *binaryDecoder) readStringFromChars(length int) (string, error) { + if err := r.checkEOS(length); err != nil { + return "", err + } + + ret := r.data[r.index : r.index+length] + r.index += length + + return string(ret), nil +} + +func (r *binaryDecoder) readAttributes(n int) (map[string]string, error) { + if n == 0 { + return nil, nil + } + + ret := make(map[string]string) + for i := 0; i < n; i++ { + idx, err := r.readInt8(false) + if err != nil { + return nil, err + } + + index, err := r.readString(idx) + if err != nil { + return nil, err + } + + idx, err = r.readInt8(false) + if err != nil { + return nil, err + } + + ret[index], err = r.readString(idx) + if err != nil { + return nil, err + } + } + + return ret, nil +} + +func (r *binaryDecoder) readList(tag int) ([]Node, error) { + size, err := r.readListSize(tag) + if err != nil { + return nil, err + } + + ret := make([]Node, size) + for i := 0; i < size; i++ { + n, err := r.ReadNode() + + if err != nil { + return nil, err + } + + ret[i] = *n + } + + return ret, nil +} + +func (r *binaryDecoder) ReadNode() (*Node, error) { + ret := &Node{} + + size, err := r.readInt8(false) + if err != nil { + return nil, err + } + listSize, err := r.readListSize(size) + if err != nil { + return nil, err + } + + descrTag, err := r.readInt8(false) + if descrTag == token.STREAM_END { + return nil, fmt.Errorf("unexpected stream end") + } + ret.Description, err = r.readString(descrTag) + if err != nil { + return nil, err + } + if listSize == 0 || ret.Description == "" { + return nil, fmt.Errorf("invalid Node") + } + + ret.Attributes, err = r.readAttributes((listSize - 1) >> 1) + if err != nil { + return nil, err + } + + if listSize%2 == 1 { + return ret, nil + } + + tag, err := r.readInt8(false) + if err != nil { + return nil, err + } + + switch tag { + case token.LIST_EMPTY, token.LIST_8, token.LIST_16: + ret.Content, err = r.readList(tag) + case token.BINARY_8: + size, err = r.readInt8(false) + if err != nil { + return nil, err + } + + ret.Content, err = r.readBytes(size) + case token.BINARY_20: + size, err = r.readInt20() + if err != nil { + return nil, err + } + + ret.Content, err = r.readBytes(size) + case token.BINARY_32: + size, err = r.readInt32(false) + if err != nil { + return nil, err + } + + ret.Content, err = r.readBytes(size) + default: + ret.Content, err = r.readString(tag) + } + + if err != nil { + return nil, err + } + return ret, nil +} + +func (r *binaryDecoder) readBytes(n int) ([]byte, error) { + ret := make([]byte, n) + var err error + + for i := range ret { + ret[i], err = r.readByte() + if err != nil { + return nil, err + } + } + + return ret, nil +} |