| 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) |
| } |
| 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 == '_' |
| } |