// Convert converts a CFTypeRef to a go instance. func Convert(ref C.CFTypeRef) (interface{}, error) { typeID := C.CFGetTypeID(ref) if typeID == C.CFStringGetTypeID() { return CFStringToString(C.CFStringRef(ref)), nil } else if typeID == C.CFDictionaryGetTypeID() { return ConvertCFDictionary(C.CFDictionaryRef(ref)) } else if typeID == C.CFArrayGetTypeID() { arr := CFArrayToArray(C.CFArrayRef(ref)) results := make([]interface{}, 0, len(arr)) for _, ref := range arr { v, err := Convert(ref) if err != nil { return nil, err } results = append(results, v) return results, nil } } else if typeID == C.CFDataGetTypeID() { b, err := CFDataToBytes(C.CFDataRef(ref)) if err != nil { return nil, err } return b, nil } else if typeID == C.CFNumberGetTypeID() { return CFNumberToInterface(C.CFNumberRef(ref)), nil } else if typeID == C.CFBooleanGetTypeID() { if C.CFBooleanGetValue(C.CFBooleanRef(ref)) != 0 { return true, nil } return false, nil } return nil, fmt.Errorf("Invalid type: %s", CFTypeDescription(ref)) }
// QueryItem returns a list of query results. func QueryItem(item Item) ([]QueryResult, error) { resultsRef, err := QueryItemRef(item) if err != nil { return nil, err } if resultsRef == nil { return nil, nil } defer Release(resultsRef) results := make([]QueryResult, 0, 1) typeID := C.CFGetTypeID(resultsRef) if typeID == C.CFArrayGetTypeID() { arr := CFArrayToArray(C.CFArrayRef(resultsRef)) for _, ref := range arr { typeID := C.CFGetTypeID(ref) if typeID == C.CFDictionaryGetTypeID() { item, err := convertResult(C.CFDictionaryRef(ref)) if err != nil { return nil, err } results = append(results, *item) } else { return nil, fmt.Errorf("Invalid result type (If you SetReturnRef(true) you should use QueryItemRef directly).") } } } else if typeID == C.CFDictionaryGetTypeID() { item, err := convertResult(C.CFDictionaryRef(resultsRef)) if err != nil { return nil, err } results = append(results, *item) } else if typeID == C.CFDataGetTypeID() { b, err := CFDataToBytes(C.CFDataRef(resultsRef)) if err != nil { return nil, err } item := QueryResult{Data: b} results = append(results, item) } else { return nil, fmt.Errorf("Invalid result type: %s", CFTypeDescription(resultsRef)) } return results, nil }
func getIntProp(device C.IOHIDDeviceRef, key C.CFStringRef) int32 { var value int32 ref := C.IOHIDDeviceGetProperty(device, key) if ref != nil { if C.CFGetTypeID(ref) == C.CFNumberGetTypeID() { C.CFNumberGetValue(C.CFNumberRef(ref), C.kCFNumberSInt32Type, unsafe.Pointer(&value)) return value } } return 0 }
// GetAllAccountNames returns a list of all account names for the // given service name in the default keychain. func GetAllAccountNames(serviceName string) (accountNames []string, err error) { var serviceNameString C.CFStringRef if serviceNameString, err = _UTF8StringToCFString(serviceName); err != nil { return } defer C.CFRelease(C.CFTypeRef(serviceNameString)) query := map[C.CFTypeRef]C.CFTypeRef{ secClass: secClassGenericPassword, secAttrService: C.CFTypeRef(serviceNameString), secMatchLimit: secMatchLimitAll, secReturnAttributes: C.CFTypeRef(C.kCFBooleanTrue), } queryDict := mapToCFDictionary(query) defer C.CFRelease(C.CFTypeRef(queryDict)) var resultsRef C.CFTypeRef errCode := C.SecItemCopyMatching(queryDict, &resultsRef) err = newKeychainError(errCode) if err == ErrItemNotFound { return []string{}, nil } else if err != nil { return nil, err } defer C.CFRelease(resultsRef) // The resultsRef should always be an array (because kSecReturnAttributes is true) // but it's a good sanity check and useful if want to support kSecReturnRef in the future. typeID := C.CFGetTypeID(resultsRef) if typeID != C.CFArrayGetTypeID() { typeDesc := C.CFCopyTypeIDDescription(typeID) defer C.CFRelease(C.CFTypeRef(typeDesc)) err = fmt.Errorf("Invalid result type: %s", _CFStringToUTF8String(typeDesc)) return } results := _CFArrayToArray(C.CFArrayRef(resultsRef)) for _, result := range results { m := _CFDictionaryToMap(C.CFDictionaryRef(result)) resultServiceName := _CFStringToUTF8String(C.CFStringRef(m[secAttrService])) if resultServiceName != serviceName { err = fmt.Errorf("Expected service name %s, got %s", serviceName, resultServiceName) return } accountName := _CFStringToUTF8String(C.CFStringRef(m[secAttrAccount])) accountNames = append(accountNames, accountName) } return }
// QueryItem returns a list of query results. func QueryItem(item Item) ([]QueryResult, error) { cfDict, err := ConvertMapToCFDictionary(item.attr) if err != nil { return nil, err } defer Release(C.CFTypeRef(cfDict)) var resultsRef C.CFTypeRef errCode := C.SecItemCopyMatching(cfDict, &resultsRef) if Error(errCode) == ErrorItemNotFound { return nil, nil } err = checkError(errCode) if err != nil { return nil, err } defer Release(resultsRef) results := make([]QueryResult, 0, 1) typeID := C.CFGetTypeID(resultsRef) if typeID == C.CFArrayGetTypeID() { arr := CFArrayToArray(C.CFArrayRef(resultsRef)) for _, dictRef := range arr { item, err := convertResult(C.CFDictionaryRef(dictRef)) if err != nil { return nil, err } results = append(results, *item) } } else if typeID == C.CFDictionaryGetTypeID() { item, err := convertResult(C.CFDictionaryRef(resultsRef)) if err != nil { return nil, err } results = append(results, *item) } else if typeID == C.CFDataGetTypeID() { b, err := CFDataToBytes(C.CFDataRef(resultsRef)) if err != nil { return nil, err } item := QueryResult{Data: b} results = append(results, item) } else { return nil, fmt.Errorf("Invalid result type: %s", CFTypeDescription(resultsRef)) } return results, nil }
func getCFDictValueCFStringRef(dict C.CFDictionaryRef, key C.CFTypeRef) (C.CFStringRef, error) { val, err := getCFDictValueRef(dict, key) if err != nil { return nil, err } if val == nil { return nil, errors.New("getCFDictValueCFStringRef: Nil value returned") } if C.CFGetTypeID(val) != C.CFStringGetTypeID() { return nil, errors.New("getCFDictValueCFStringRef: value is not a string") } return C.CFStringRef(val), nil }
func convertCFDictionaryToMapHelper(cfDict C.CFDictionaryRef, helper func(key string, value cfTypeRef, count int) error) error { count := int(C.CFDictionaryGetCount(cfDict)) if count == 0 { return nil } cfKeys := make([]cfTypeRef, count) cfVals := make([]cfTypeRef, count) C.CFDictionaryGetKeysAndValues(cfDict, (*unsafe.Pointer)(&cfKeys[0]), (*unsafe.Pointer)(&cfVals[0])) for i := 0; i < count; i++ { cfKey := cfKeys[i] typeId := C.CFGetTypeID(C.CFTypeRef(cfKey)) if typeId != C.CFStringGetTypeID() { return &UnsupportedKeyTypeError{int(typeId)} } key := convertCFStringToString(C.CFStringRef(cfKey)) if err := helper(key, cfVals[i], count); err != nil { return err } } return nil }
// we shouldn't ever get an error from this, but I'd rather not panic func convertCFTypeToInterface(cfType cfTypeRef) (interface{}, error) { typeId := C.CFGetTypeID(C.CFTypeRef(cfType)) switch typeId { case C.CFStringGetTypeID(): return convertCFStringToString(C.CFStringRef(cfType)), nil case C.CFNumberGetTypeID(): return convertCFNumberToInterface(C.CFNumberRef(cfType)), nil case C.CFBooleanGetTypeID(): return convertCFBooleanToBool(C.CFBooleanRef(cfType)), nil case C.CFDataGetTypeID(): return convertCFDataToBytes(C.CFDataRef(cfType)), nil case C.CFDateGetTypeID(): return convertCFDateToTime(C.CFDateRef(cfType)), nil case C.CFArrayGetTypeID(): ary, err := convertCFArrayToSlice(C.CFArrayRef(cfType)) return ary, err case C.CFDictionaryGetTypeID(): dict, err := convertCFDictionaryToMap(C.CFDictionaryRef(cfType)) return dict, err } return nil, &UnknownCFTypeError{typeId} }
// CFTypeDescription returns type string for CFTypeRef. func CFTypeDescription(ref C.CFTypeRef) string { typeID := C.CFGetTypeID(ref) typeDesc := C.CFCopyTypeIDDescription(typeID) defer Release(C.CFTypeRef(typeDesc)) return CFStringToString(typeDesc) }
func (state *unmarshalState) unmarshalValue(cfObj cfTypeRef, v reflect.Value) error { vType := v.Type() var unmarshaler Unmarshaler if u, ok := v.Interface().(Unmarshaler); ok { unmarshaler = u } else if vType.Kind() != reflect.Ptr && vType.Name() != "" && v.CanAddr() { // matching the encoding/json behavior here // If v is a named type and is addressable, check its address for Unmarshaler. vA := v.Addr() if u, ok := vA.Interface().(Unmarshaler); ok { unmarshaler = u } } if unmarshaler != nil { // flip over to the dumb conversion routine so we have something to give UnmarshalPlist() plist, err := convertCFTypeToInterface(cfObj) if err != nil { return err } if vType.Kind() == reflect.Ptr && v.IsNil() { v.Set(reflect.New(vType.Elem())) unmarshaler = v.Interface().(Unmarshaler) } return unmarshaler.UnmarshalPlist(plist) } if vType.Kind() == reflect.Ptr { if v.IsNil() { v.Set(reflect.New(vType.Elem())) } return state.unmarshalValue(cfObj, v.Elem()) } typeID := C.CFGetTypeID(C.CFTypeRef(cfObj)) vSetter := v // receiver of any Set* calls vAddr := v.Addr() // used for re-setting v for maps/slices if vType.Kind() == reflect.Interface { if v.IsNil() { // pick an appropriate type based on the cfobj var typ reflect.Type if typeID == cfNumberTypeID { typ = cfNumberTypeToType(C.CFNumberGetType(C.CFNumberRef(cfObj))) } else { var ok bool typ, ok = cfTypeMap[typeID] if !ok { return &UnknownCFTypeError{typeID} } } if !typ.AssignableTo(vType) { // v must be some interface that our object doesn't conform to state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil } vSetter.Set(reflect.Zero(typ)) } vAddr = v v = v.Elem() vType = v.Type() } switch typeID { case cfArrayTypeID: if vType.Kind() != reflect.Slice && vType.Kind() != reflect.Array { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil } return convertCFArrayToSliceHelper(C.CFArrayRef(cfObj), func(elem cfTypeRef, idx, count int) (bool, error) { if idx == 0 && vType.Kind() == reflect.Slice { vSetter.Set(reflect.MakeSlice(vType, count, count)) v = vAddr.Elem() } else if vType.Kind() == reflect.Array && idx >= v.Len() { return false, nil } if err := state.unmarshalValue(elem, v.Index(idx)); err != nil { return false, err } return true, nil }) case cfBooleanTypeID: if vType.Kind() != reflect.Bool { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil } vSetter.Set(reflect.ValueOf(C.CFBooleanGetValue(C.CFBooleanRef(cfObj)) != C.false)) return nil case cfDataTypeID: if !byteSliceType.AssignableTo(vType) { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil } vSetter.Set(reflect.ValueOf(convertCFDataToBytes(C.CFDataRef(cfObj)))) return nil case cfDateTypeID: if !timeType.AssignableTo(vType) { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil } vSetter.Set(reflect.ValueOf(convertCFDateToTime(C.CFDateRef(cfObj)))) return nil case cfDictionaryTypeID: if vType.Kind() == reflect.Map { // it's a map. Check its key type first if !stringType.AssignableTo(vType.Key()) { state.recordError(&UnmarshalTypeError{cfTypeNames[cfStringTypeID], vType.Key()}) return nil } if v.IsNil() { vSetter.Set(reflect.MakeMap(vType)) v = vAddr.Elem() } return convertCFDictionaryToMapHelper(C.CFDictionaryRef(cfObj), func(key string, value cfTypeRef, count int) error { keyVal := reflect.ValueOf(key) val := reflect.New(vType.Elem()) if err := state.unmarshalValue(value, val); err != nil { return err } v.SetMapIndex(keyVal, val.Elem()) return nil }) } else if vType.Kind() == reflect.Struct { return convertCFDictionaryToMapHelper(C.CFDictionaryRef(cfObj), func(key string, value cfTypeRef, count int) error { // we need to iterate the fields because the tag might rename the key var f reflect.StructField var ok bool for i := 0; i < vType.NumField(); i++ { sf := vType.Field(i) tag := sf.Tag.Get("plist") if tag == "-" { // Pretend this field doesn't exist continue } if sf.Anonymous { // Match encoding/json's behavior here and pretend it doesn't exist continue } name, _ := parseTag(tag) if name == key { f = sf ok = true // This is unambiguously the right match break } if sf.Name == key { f = sf ok = true } // encoding/json does a case-insensitive match. Lets do that too if !ok && strings.EqualFold(sf.Name, key) { f = sf ok = true } } if ok { if f.PkgPath != "" { // this is an unexported field return &UnmarshalFieldError{key, vType, f} } vElem := v.FieldByIndex(f.Index) if err := state.unmarshalValue(value, vElem); err != nil { return err } } return nil }) } state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil case cfNumberTypeID: switch vType.Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: i := convertCFNumberToInt64(C.CFNumberRef(cfObj)) if v.OverflowInt(i) { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID] + " " + strconv.FormatInt(i, 10), vType}) return nil } if vSetter.Kind() == reflect.Interface { vSetter.Set(reflect.ValueOf(i)) } else { vSetter.SetInt(i) } return nil case reflect.Uint, reflect.Uintptr, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: u := uint64(convertCFNumberToUInt32(C.CFNumberRef(cfObj))) if v.OverflowUint(u) { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID] + " " + strconv.FormatUint(u, 10), vType}) return nil } if vSetter.Kind() == reflect.Interface { vSetter.Set(reflect.ValueOf(u)) } else { vSetter.SetUint(u) } return nil case reflect.Float32, reflect.Float64: f := convertCFNumberToFloat64(C.CFNumberRef(cfObj)) if v.OverflowFloat(f) { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID] + " " + strconv.FormatFloat(f, 'f', -1, 64), vType}) return nil } if vSetter.Kind() == reflect.Interface { vSetter.Set(reflect.ValueOf(f)) } else { vSetter.SetFloat(f) } return nil } state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil case cfStringTypeID: if vType.Kind() != reflect.String { state.recordError(&UnmarshalTypeError{cfTypeNames[typeID], vType}) return nil } vSetter.Set(reflect.ValueOf(convertCFStringToString(C.CFStringRef(cfObj)))) return nil } return &UnknownCFTypeError{typeID} }