| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- package wire
- import (
- "bytes"
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "reflect"
- "strings"
- )
- var (
- ErrUnmarshalFailure = errors.New("failed to unmarshal")
- errNotNullTerminated = errors.New("nullterm tag is set, but string is not null-terminated")
- )
- // UnmarshalBE unmarshalls OSCAR protocol messages in big-endian format.
- func UnmarshalBE(v any, r io.Reader) error {
- if err := unmarshal(reflect.TypeOf(v).Elem(), reflect.ValueOf(v).Elem(), "", r, binary.BigEndian, ""); err != nil {
- return fmt.Errorf("%w: %w", ErrUnmarshalFailure, err)
- }
- return nil
- }
- // UnmarshalLE unmarshalls OSCAR protocol messages in little-endian format.
- func UnmarshalLE(v any, r io.Reader) error {
- if err := unmarshal(reflect.TypeOf(v).Elem(), reflect.ValueOf(v).Elem(), "", r, binary.LittleEndian, ""); err != nil {
- return fmt.Errorf("%w: %w", ErrUnmarshalFailure, err)
- }
- return nil
- }
- func unmarshal(t reflect.Type, v reflect.Value, tag reflect.StructTag, r io.Reader, order binary.ByteOrder, activeQuirk string) error {
- oscTag, err := parseOSCARTag(tag)
- if err != nil {
- return fmt.Errorf("error parsing tag: %w", err)
- }
- effectiveQuirk := strings.TrimSpace(oscTag.quirk)
- if effectiveQuirk == "" {
- effectiveQuirk = activeQuirk
- }
- if oscTag.optional {
- v.Set(reflect.New(t.Elem()))
- err := unmarshalStruct(t.Elem(), v.Elem(), oscTag, r, order, effectiveQuirk)
- if errors.Is(err, io.EOF) {
- // no values to read, but that's ok since this struct is optional
- v.Set(reflect.Zero(t))
- err = nil
- }
- return err
- } else if v.Kind() == reflect.Ptr {
- return errNonOptionalPointer
- }
- switch v.Kind() {
- case reflect.Array:
- return unmarshalArray(v, r, order, effectiveQuirk)
- case reflect.Slice:
- return unmarshalSlice(v, oscTag, r, order, effectiveQuirk)
- case reflect.String:
- return unmarshalString(v, oscTag, r, order)
- case reflect.Struct:
- return unmarshalStruct(t, v, oscTag, r, order, effectiveQuirk)
- case reflect.Uint8:
- var l uint8
- if err := binary.Read(r, order, &l); err != nil {
- return err
- }
- v.Set(reflect.ValueOf(l))
- return nil
- case reflect.Uint16:
- var l uint16
- if err := binary.Read(r, order, &l); err != nil {
- return err
- }
- v.Set(reflect.ValueOf(l))
- return nil
- case reflect.Uint32:
- var l uint32
- if err := binary.Read(r, order, &l); err != nil {
- return err
- }
- v.Set(reflect.ValueOf(l))
- return nil
- case reflect.Uint64:
- var l uint64
- if err := binary.Read(r, order, &l); err != nil {
- return err
- }
- v.Set(reflect.ValueOf(l))
- return nil
- default:
- return fmt.Errorf("unsupported type %v", t.Kind())
- }
- }
- func unmarshalArray(v reflect.Value, r io.Reader, order binary.ByteOrder, activeQuirk string) error {
- arrLen := v.Len()
- arrType := v.Type().Elem()
- for i := 0; i < arrLen; i++ {
- elem := reflect.New(arrType).Elem()
- if err := unmarshal(arrType, elem, "", r, order, activeQuirk); err != nil {
- return err
- }
- v.Index(i).Set(elem)
- }
- return nil
- }
- func unmarshalSlice(v reflect.Value, oscTag oscarTag, r io.Reader, order binary.ByteOrder, activeQuirk string) error {
- slice := reflect.New(v.Type()).Elem()
- elemType := v.Type().Elem()
- if oscTag.hasLenPrefix {
- bufLen, err := unmarshalUnsignedInt(oscTag.lenPrefix, r, order)
- if err != nil {
- return err
- }
- b := make([]byte, bufLen)
- if bufLen > 0 {
- if _, err := io.ReadFull(r, b); err != nil {
- return err
- }
- }
- buf := bytes.NewBuffer(b)
- for buf.Len() > 0 {
- elem := reflect.New(elemType).Elem()
- if err := unmarshalSliceElement(elemType, elem, buf, order, activeQuirk); err != nil {
- return err
- }
- slice = reflect.Append(slice, elem)
- }
- } else if oscTag.hasCountPrefix {
- count, err := unmarshalUnsignedInt(oscTag.countPrefix, r, order)
- if err != nil {
- return err
- }
- for i := 0; i < count; i++ {
- elem := reflect.New(elemType).Elem()
- if err := unmarshalSliceElement(elemType, elem, r, order, activeQuirk); err != nil {
- return err
- }
- slice = reflect.Append(slice, elem)
- }
- } else {
- for {
- elem := reflect.New(elemType).Elem()
- if err := unmarshalSliceElement(elemType, elem, r, order, activeQuirk); err != nil {
- if errors.Is(err, io.EOF) {
- break
- }
- return err
- }
- slice = reflect.Append(slice, elem)
- }
- }
- v.Set(slice)
- return nil
- }
- // unmarshalSliceElement reads one element of a slice; for TLV under LE with decode quirks,
- // applies client-specific length workarounds (see unmarshalTLV* helpers).
- func unmarshalSliceElement(elemType reflect.Type, elem reflect.Value, r io.Reader, order binary.ByteOrder, activeQuirk string) error {
- if activeQuirk != "" {
- switch {
- case activeQuirk == "icq2003b_set_fullinfo" && order == binary.LittleEndian && elemType == reflect.TypeOf(TLV{}):
- return unmarshalTLVICQ2003bSetFullInfo(elem, r, order)
- case activeQuirk == "qip_2005_search_by_uin2" && order == binary.LittleEndian && elemType == reflect.TypeOf(TLV{}):
- return unmarshalTLVQIP2005SearchByUIN2(elem, r, order)
- }
- }
- return unmarshal(elemType, elem, "", r, order, activeQuirk)
- }
- // unmarshalTLVICQ2003bSetFullInfo decodes one TLV with ICQ 2003b save-info workaround for ICQTLVTagsEmail.
- func unmarshalTLVICQ2003bSetFullInfo(elem reflect.Value, r io.Reader, order binary.ByteOrder) error {
- var tag uint16
- if err := binary.Read(r, order, &tag); err != nil {
- return err
- }
- var n uint16
- if err := binary.Read(r, order, &n); err != nil {
- return err
- }
- if tag == ICQTLVTagsEmail && n == 3 {
- n = 4
- }
- buf := make([]byte, n)
- if n > 0 {
- if _, err := io.ReadFull(r, buf); err != nil {
- return err
- }
- }
- elem.Field(0).Set(reflect.ValueOf(tag))
- elem.Field(1).SetBytes(buf)
- return nil
- }
- // unmarshalTLVQIP2005SearchByUIN2 decodes one TLV for META SearchByUIN2 (0x0569): QIP 2005 sends
- // ICQTLVTagsUIN (0x0136) with an incorrect length (e.g. 6) for a 4-byte UIN, causing EOF on a strict read.
- func unmarshalTLVQIP2005SearchByUIN2(elem reflect.Value, r io.Reader, order binary.ByteOrder) error {
- var tag uint16
- if err := binary.Read(r, order, &tag); err != nil {
- return err
- }
- var n uint16
- if err := binary.Read(r, order, &n); err != nil {
- return err
- }
- if tag == ICQTLVTagsUIN && n != 4 {
- n = 4
- }
- buf := make([]byte, n)
- if n > 0 {
- if _, err := io.ReadFull(r, buf); err != nil {
- return err
- }
- }
- elem.Field(0).Set(reflect.ValueOf(tag))
- elem.Field(1).SetBytes(buf)
- return nil
- }
- func unmarshalString(v reflect.Value, oscTag oscarTag, r io.Reader, order binary.ByteOrder) error {
- if !oscTag.hasLenPrefix {
- return fmt.Errorf("missing len_prefix tag")
- }
- bufLen, err := unmarshalUnsignedInt(oscTag.lenPrefix, r, order)
- if err != nil {
- return err
- }
- buf := make([]byte, bufLen)
- if bufLen > 0 {
- if _, err := io.ReadFull(r, buf); err != nil {
- return err
- }
- if oscTag.nullTerminated {
- // search for null within string and truncate there if found
- // needed for icq 6 login to be working
- if nullPos := bytes.IndexByte(buf, 0x00); nullPos != -1 {
- buf = buf[0:nullPos]
- }
- }
- }
- // todo is there a more efficient way?
- v.SetString(string(buf))
- return nil
- }
- func unmarshalStruct(t reflect.Type, v reflect.Value, oscTag oscarTag, r io.Reader, order binary.ByteOrder, activeQuirk string) error {
- if oscTag.hasLenPrefix {
- bufLen, err := unmarshalUnsignedInt(oscTag.lenPrefix, r, order)
- if err != nil {
- return err
- }
- b := make([]byte, bufLen)
- if bufLen > 0 {
- if _, err := io.ReadFull(r, b); err != nil {
- return err
- }
- }
- r = bytes.NewBuffer(b)
- }
- for i := 0; i < v.NumField(); i++ {
- field := t.Field(i)
- value := v.Field(i)
- if field.Type.Kind() == reflect.Ptr {
- if i != v.NumField()-1 {
- return fmt.Errorf("pointer type found at non-final field %s", field.Name)
- }
- if field.Type.Elem().Kind() != reflect.Struct {
- return fmt.Errorf("%w: field %s must point to a struct, got %v instead",
- errNonOptionalPointer, field.Name, field.Type.Elem().Kind())
- }
- }
- if err := unmarshal(field.Type, value, field.Tag, r, order, activeQuirk); err != nil {
- return err
- }
- }
- return nil
- }
- func unmarshalUnsignedInt(intType reflect.Kind, r io.Reader, order binary.ByteOrder) (int, error) {
- var bufLen int
- switch intType {
- case reflect.Uint8:
- var l uint8
- if err := binary.Read(r, order, &l); err != nil {
- return 0, err
- }
- bufLen = int(l)
- case reflect.Uint16:
- var l uint16
- if err := binary.Read(r, order, &l); err != nil {
- return 0, err
- }
- bufLen = int(l)
- default:
- panic(fmt.Sprintf("unsupported type %s. allowed types: uint8, uint16", intType))
- }
- return bufLen, nil
- }
|