package dbus import ( "errors" "fmt" "reflect" "strings" ) var ( byteType = reflect.TypeOf(byte(0)) boolType = reflect.TypeOf(false) uint8Type = reflect.TypeOf(uint8(0)) int16Type = reflect.TypeOf(int16(0)) uint16Type = reflect.TypeOf(uint16(0)) intType = reflect.TypeOf(int(0)) uintType = reflect.TypeOf(uint(0)) int32Type = reflect.TypeOf(int32(0)) uint32Type = reflect.TypeOf(uint32(0)) int64Type = reflect.TypeOf(int64(0)) uint64Type = reflect.TypeOf(uint64(0)) float64Type = reflect.TypeOf(float64(0)) stringType = reflect.TypeOf("") signatureType = reflect.TypeOf(Signature{""}) objectPathType = reflect.TypeOf(ObjectPath("")) variantType = reflect.TypeOf(Variant{Signature{""}, nil}) interfacesType = reflect.TypeOf([]interface{}{}) interfaceType = reflect.TypeOf((*interface{})(nil)).Elem() unixFDType = reflect.TypeOf(UnixFD(0)) unixFDIndexType = reflect.TypeOf(UnixFDIndex(0)) ) // An InvalidTypeError signals that a value which cannot be represented in the // D-Bus wire format was passed to a function. type InvalidTypeError struct { Type reflect.Type } func (e InvalidTypeError) Error() string { return "dbus: invalid type " + e.Type.String() } // Store copies the values contained in src to dest, which must be a slice of // pointers. It converts slices of interfaces from src to corresponding structs // in dest. An error is returned if the lengths of src and dest or the types of // their elements don't match. func Store(src []interface{}, dest ...interface{}) error { if len(src) != len(dest) { return errors.New("dbus.Store: length mismatch") } for i := range src { if err := storeInterfaces(src[i], dest[i]); err != nil { return err } } return nil } func storeInterfaces(src, dest interface{}) error { return store(reflect.ValueOf(dest), reflect.ValueOf(src)) } func store(dest, src reflect.Value) error { if dest.Kind() == reflect.Ptr { return store(dest.Elem(), src) } switch src.Kind() { case reflect.Slice: return storeSlice(dest, src) case reflect.Map: return storeMap(dest, src) default: return storeBase(dest, src) } } func storeBase(dest, src reflect.Value) error { return setDest(dest, src) } func setDest(dest, src reflect.Value) error { if !isVariant(src.Type()) && isVariant(dest.Type()) { //special conversion for dbus.Variant dest.Set(reflect.ValueOf(MakeVariant(src.Interface()))) return nil } if isVariant(src.Type()) && !isVariant(dest.Type()) { src = getVariantValue(src) return store(dest, src) } if !src.Type().ConvertibleTo(dest.Type()) { return fmt.Errorf( "dbus.Store: type mismatch: cannot convert %s to %s", src.Type(), dest.Type()) } dest.Set(src.Convert(dest.Type())) return nil } func kindsAreCompatible(dest, src reflect.Type) bool { switch { case isVariant(dest): return true case dest.Kind() == reflect.Interface: return true default: return dest.Kind() == src.Kind() } } func isConvertibleTo(dest, src reflect.Type) bool { switch { case isVariant(dest): return true case dest.Kind() == reflect.Interface: return true case dest.Kind() == reflect.Slice: return src.Kind() == reflect.Slice && isConvertibleTo(dest.Elem(), src.Elem()) case dest.Kind() == reflect.Struct: return src == interfacesType default: return src.ConvertibleTo(dest) } } func storeMap(dest, src reflect.Value) error { switch { case !kindsAreCompatible(dest.Type(), src.Type()): return fmt.Errorf( "dbus.Store: type mismatch: "+ "map: cannot store a value of %s into %s", src.Type(), dest.Type()) case isVariant(dest.Type()): return storeMapIntoVariant(dest, src) case dest.Kind() == reflect.Interface: return storeMapIntoInterface(dest, src) case isConvertibleTo(dest.Type().Key(), src.Type().Key()) && isConvertibleTo(dest.Type().Elem(), src.Type().Elem()): return storeMapIntoMap(dest, src) default: return fmt.Errorf( "dbus.Store: type mismatch: "+ "map: cannot convert a value of %s into %s", src.Type(), dest.Type()) } } func storeMapIntoVariant(dest, src reflect.Value) error { dv := reflect.MakeMap(src.Type()) err := store(dv, src) if err != nil { return err } return storeBase(dest, dv) } func storeMapIntoInterface(dest, src reflect.Value) error { var dv reflect.Value if isVariant(src.Type().Elem()) { //Convert variants to interface{} recursively when converting //to interface{} dv = reflect.MakeMap( reflect.MapOf(src.Type().Key(), interfaceType)) } else { dv = reflect.MakeMap(src.Type()) } err := store(dv, src) if err != nil { return err } return storeBase(dest, dv) } func storeMapIntoMap(dest, src reflect.Value) error { if dest.IsNil() { dest.Set(reflect.MakeMap(dest.Type())) } keys := src.MapKeys() for _, key := range keys { dkey := key.Convert(dest.Type().Key()) dval := reflect.New(dest.Type().Elem()).Elem() err := store(dval, getVariantValue(src.MapIndex(key))) if err != nil { return err } dest.SetMapIndex(dkey, dval) } return nil } func storeSlice(dest, src reflect.Value) error { switch { case src.Type() == interfacesType && dest.Kind() == reflect.Struct: //The decoder always decodes structs as slices of interface{} return storeStruct(dest, src) case !kindsAreCompatible(dest.Type(), src.Type()): return fmt.Errorf( "dbus.Store: type mismatch: "+ "slice: cannot store a value of %s into %s", src.Type(), dest.Type()) case isVariant(dest.Type()): return storeSliceIntoVariant(dest, src) case dest.Kind() == reflect.Interface: return storeSliceIntoInterface(dest, src) case isConvertibleTo(dest.Type().Elem(), src.Type().Elem()): return storeSliceIntoSlice(dest, src) default: return fmt.Errorf( "dbus.Store: type mismatch: "+ "slice: cannot convert a value of %s into %s", src.Type(), dest.Type()) } } func storeStruct(dest, src reflect.Value) error { if isVariant(dest.Type()) { return storeBase(dest, src) } dval := make([]interface{}, 0, dest.NumField()) dtype := dest.Type() for i := 0; i < dest.NumField(); i++ { field := dest.Field(i) ftype := dtype.Field(i) if ftype.PkgPath != "" { continue } if ftype.Tag.Get("dbus") == "-" { continue } dval = append(dval, field.Addr().Interface()) } if src.Len() != len(dval) { return fmt.Errorf( "dbus.Store: type mismatch: "+ "destination struct does not have "+ "enough fields need: %d have: %d", src.Len(), len(dval)) } return Store(src.Interface().([]interface{}), dval...) } func storeSliceIntoVariant(dest, src reflect.Value) error { dv := reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) err := store(dv, src) if err != nil { return err } return storeBase(dest, dv) } func storeSliceIntoInterface(dest, src reflect.Value) error { var dv reflect.Value if isVariant(src.Type().Elem()) { //Convert variants to interface{} recursively when converting //to interface{} dv = reflect.MakeSlice(reflect.SliceOf(interfaceType), src.Len(), src.Cap()) } else { dv = reflect.MakeSlice(src.Type(), src.Len(), src.Cap()) } err := store(dv, src) if err != nil { return err } return storeBase(dest, dv) } func storeSliceIntoSlice(dest, src reflect.Value) error { if dest.IsNil() || dest.Len() < src.Len() { dest.Set(reflect.MakeSlice(dest.Type(), src.Len(), src.Cap())) } if dest.Len() != src.Len() { return fmt.Errorf( "dbus.Store: type mismatch: "+ "slices are different lengths "+ "need: %d have: %d", src.Len(), dest.Len()) } for i := 0; i < src.Len(); i++ { err := store(dest.Index(i), getVariantValue(src.Index(i))) if err != nil { return err } } return nil } func getVariantValue(in reflect.Value) reflect.Value { if isVariant(in.Type()) { return reflect.ValueOf(in.Interface().(Variant).Value()) } return in } func isVariant(t reflect.Type) bool { return t == variantType } // An ObjectPath is an object path as defined by the D-Bus spec. type ObjectPath string // IsValid returns whether the object path is valid. func (o ObjectPath) IsValid() bool { s := string(o) if len(s) == 0 { return false } if s[0] != '/' { return false } if s[len(s)-1] == '/' && len(s) != 1 { return false } // probably not used, but technically possible if s == "/" { return true } split := strings.Split(s[1:], "/") for _, v := range split { if len(v) == 0 { return false } for _, c := range v { if !isMemberChar(c) { return false } } } return true } // A UnixFD is a Unix file descriptor sent over the wire. See the package-level // documentation for more information about Unix file descriptor passsing. type UnixFD int32 // A UnixFDIndex is the representation of a Unix file descriptor in a message. type UnixFDIndex uint32 // alignment returns the alignment of values of type t. func alignment(t reflect.Type) int { switch t { case variantType: return 1 case objectPathType: return 4 case signatureType: return 1 case interfacesType: return 4 } switch t.Kind() { case reflect.Uint8: return 1 case reflect.Uint16, reflect.Int16: return 2 case reflect.Uint, reflect.Int, reflect.Uint32, reflect.Int32, reflect.String, reflect.Array, reflect.Slice, reflect.Map: return 4 case reflect.Uint64, reflect.Int64, reflect.Float64, reflect.Struct: return 8 case reflect.Ptr: return alignment(t.Elem()) } return 1 } // isKeyType returns whether t is a valid type for a D-Bus dict. func isKeyType(t reflect.Type) bool { switch t.Kind() { case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Float64, reflect.String, reflect.Uint, reflect.Int: return true } return false } // isValidInterface returns whether s is a valid name for an interface. func isValidInterface(s string) bool { if len(s) == 0 || len(s) > 255 || s[0] == '.' { return false } elem := strings.Split(s, ".") if len(elem) < 2 { return false } for _, v := range elem { if len(v) == 0 { return false } if v[0] >= '0' && v[0] <= '9' { return false } for _, c := range v { if !isMemberChar(c) { return false } } } return true } // isValidMember returns whether s is a valid name for a member. func isValidMember(s string) bool { if len(s) == 0 || len(s) > 255 { return false } i := strings.Index(s, ".") if i != -1 { return false } if s[0] >= '0' && s[0] <= '9' { return false } for _, c := range s { if !isMemberChar(c) { return false } } return true } func isMemberChar(c rune) bool { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_' }