func CGImageCreateWithImage(img image.Image) (cgimg C.CGImageRef, err error) { var data imageData if data, err = extractImageData(img); err != nil { return } memory := C.CFDataCreate( nil, (*C.UInt8)(unsafe.Pointer(&data.pixels[0])), C.CFIndex(len(data.pixels)), ) provider := C.CGDataProviderCreateWithCFData(memory) cgimg = C.CGImageCreate( C.size_t(data.width), C.size_t(data.height), C.size_t(data.bpc), C.size_t(data.bpp), C.size_t(data.stride), data.colors, data.info, provider, nil, false, C.kCGRenderingIntentDefault, ) C.CFRelease(provider) C.CFRelease(memory) C.CFRelease(data.colors) return }
// ===== CFData ===== func convertBytesToCFData(data []byte) C.CFDataRef { var ptr *C.UInt8 if len(data) > 0 { ptr = (*C.UInt8)((&data[0])) } return C.CFDataCreate(nil, ptr, C.CFIndex(len(data))) }
func (c osx16Collator) Compare(a, b Input) int { sa := C.CFStringCreateWithCharactersNoCopy( nil, osxCharP(a.UTF16), C.CFIndex(len(a.UTF16)), nil, ) sb := C.CFStringCreateWithCharactersNoCopy( nil, osxCharP(b.UTF16), C.CFIndex(len(b.UTF16)), nil, ) _range := C.CFRangeMake(0, C.CFStringGetLength(sa)) return int(C.CFStringCompareWithOptionsAndLocale(sa, sb, _range, c.opt, c.loc)) }
// The returned CFDataRef, if non-nil, must be released via CFRelease. func bytesToCFData(b []byte) C.CFDataRef { var p *C.UInt8 if len(b) > 0 { p = (*C.UInt8)(&b[0]) } return C.CFDataCreate(nil, p, C.CFIndex(len(b))) }
func convertSliceToCFArrayHelper(slice reflect.Value, helper func(reflect.Value) (cfTypeRef, error)) (C.CFArrayRef, error) { if slice.Len() == 0 { // short-circuit 0, so we can assume plists[0] is valid later return C.CFArrayCreate(nil, nil, 0, nil), nil } // assume slice is a slice/array, because our caller already checked plists := make([]cfTypeRef, slice.Len()) // defer the release defer func() { for _, cfObj := range plists { cfRelease(cfObj) } }() // convert the slice for i := 0; i < slice.Len(); i++ { cfType, err := helper(slice.Index(i)) if err != nil { return nil, err } plists[i] = cfType } // create the array callbacks := (*C.CFArrayCallBacks)(&C.kCFTypeArrayCallBacks) return C.CFArrayCreate(nil, (*unsafe.Pointer)(&plists[0]), C.CFIndex(len(plists)), callbacks), nil }
// ExportFromKeychain ... func ExportFromKeychain(itemRefsToExport []C.CFTypeRef, outputFilePath string, isAskForPassword bool) error { passphraseCString := C.CString("") defer C.free(unsafe.Pointer(passphraseCString)) var exportedData C.CFDataRef var exportParams C.SecItemImportExportKeyParameters exportParams.keyUsage = nil exportParams.keyAttributes = nil exportParams.version = C.SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION if isAskForPassword { exportParams.flags = C.kSecKeySecurePassphrase exportParams.passphrase = nil exportParams.alertTitle = nil promptText := C.CString("Enter a password which will be used to protect the exported items") defer C.free(unsafe.Pointer(promptText)) exportParams.alertPrompt = convertCStringToCFString(promptText) } else { exportParams.flags = 0 exportParams.passphrase = (C.CFTypeRef)(convertCStringToCFString(passphraseCString)) exportParams.alertTitle = nil exportParams.alertPrompt = nil } // create a C array from the input ptr := (*unsafe.Pointer)(&itemRefsToExport[0]) cfArrayForExport := C.CFArrayCreate( C.kCFAllocatorDefault, ptr, C.CFIndex(len(itemRefsToExport)), &C.kCFTypeArrayCallBacks) // do the export! status := C.SecItemExport(C.CFTypeRef(cfArrayForExport), C.kSecFormatPKCS12, 0, //C.kSecItemPemArmour, // Use kSecItemPemArmour to add PEM armour - the .p12 generated by Keychain Access.app does NOT have PEM armour &exportParams, &exportedData) if status != C.errSecSuccess { return fmt.Errorf("SecItemExport: error (OSStatus): %d", status) } // exportedData now contains your PKCS12 data // make sure it'll be released properly! defer C.CFRelease(C.CFTypeRef(exportedData)) dataBytes := convertCFDataRefToGoBytes(exportedData) if dataBytes == nil || len(dataBytes) < 1 { return errors.New("ExportFromKeychain: failed to convert export data - nil or empty") } if err := fileutil.WriteBytesToFile(outputFilePath, dataBytes); err != nil { return fmt.Errorf("ExportFromKeychain: failed to write into file: %s", err) } log.Debug("Export - success") return nil }
func NewCFString(s string) C.CFStringRef { s_ := C.CString(s) defer C.free(unsafe.Pointer(s_)) retval := C.CFStringCreateWithBytes( C.CFAllocatorRef(nil), (*C.UInt8)(unsafe.Pointer(s_)), C.CFIndex(len(s)), C.kCFStringEncodingUTF8, C.Boolean(0), ) return retval }
// cfstringGo creates a Go string for a CoreFoundation string using the CoreFoundation UTF-8 converter. // For short strings this is an efficiency nightmare! In this package this function is not currently used // in any critical path. func cfstringGo(cfs C.CFStringRef) string { var usedBufLen C.CFIndex n := C.cfstring_utf8_length(cfs, &usedBufLen) if n <= 0 { return "" } rng := C.CFRange{location: C.CFIndex(0), length: n} buf := make([]byte, int(usedBufLen)) bufp := unsafe.Pointer(&buf[0]) C.CFStringGetBytes(cfs, rng, C.kCFStringEncodingUTF8, 0, 0, (*C.UInt8)(bufp), C.CFIndex(len(buf)), &usedBufLen) sh := &reflect.StringHeader{ Data: uintptr(bufp), Len: int(usedBufLen), } return *(*string)(unsafe.Pointer(sh)) }
// Converts a go string to a C.CFStringRef. The content of the string is copied // by the function so if the Go string gets garbage collected the returned object // is still valid. // The program needs to call C.CFRelease on the returned C.CFStringRef when it // doesn't need it anymore to avoid any memory leak. func GoStringToCFString(s string) C.CFStringRef { h := (*reflect.StringHeader)(unsafe.Pointer(&s)) return C.CFStringCreateWithBytes( nil, (*C.UInt8)(unsafe.Pointer(h.Data)), C.CFIndex(len(s)), C.kCFStringEncodingUTF8, 0, ) }
func CFNetServiceSetTXTData(cns *CFNetService, data []byte) bool { p := unsafe.Pointer(nil) if data != nil && len(data) > 0 { p = unsafe.Pointer(&data[0]) } data_ := C.CFDataCreate(nil, (*C.UInt8)(p), C.CFIndex(len(data))) retval := C.CFNetServiceSetTXTData(cns.ref, data_) C.CFRelease((C.CFTypeRef)(data_)) return retval != 0 }
func (c *osxCollator) init(locale string) { l := C.CFStringCreateWithBytes( nil, osxUInt8P([]byte(locale)), C.CFIndex(len(locale)), C.kCFStringEncodingUTF8, C.Boolean(0), ) c.loc = C.CFLocaleCreate(nil, l) }
func (c osx8Collator) Compare(a, b Input) int { sa := C.CFStringCreateWithBytesNoCopy( nil, osxUInt8P(a.UTF8), C.CFIndex(len(a.UTF8)), C.kCFStringEncodingUTF8, C.Boolean(0), nil, ) sb := C.CFStringCreateWithBytesNoCopy( nil, osxUInt8P(b.UTF8), C.CFIndex(len(b.UTF8)), C.kCFStringEncodingUTF8, C.Boolean(0), nil, ) _range := C.CFRangeMake(0, C.CFStringGetLength(sa)) return int(C.CFStringCompareWithOptionsAndLocale(sa, sb, _range, c.opt, c.loc)) }
// ArrayToCFArray will return a CFArrayRef and if non-nil, must be released with // Release(ref). func ArrayToCFArray(a []C.CFTypeRef) C.CFArrayRef { var values []unsafe.Pointer for _, value := range a { values = append(values, unsafe.Pointer(value)) } numValues := len(values) var valuesPointer *unsafe.Pointer if numValues > 0 { valuesPointer = &values[0] } return C.CFArrayCreate(nil, valuesPointer, C.CFIndex(numValues), &C.kCFTypeArrayCallBacks) }
// The returned CFStringRef, if non-nil, must be released via CFRelease. func _UTF8StringToCFString(s string) (C.CFStringRef, error) { if !utf8.ValidString(s) { return nil, errors.New("invalid UTF-8 string") } bytes := []byte(s) var p *C.UInt8 if len(bytes) > 0 { p = (*C.UInt8)(&bytes[0]) } return C.CFStringCreateWithBytes(nil, p, C.CFIndex(len(s)), C.kCFStringEncodingUTF8, C.false), nil }
func buildFont(f C.CTFontRef) []byte { ctags := C.CTFontCopyAvailableTables(f, C.kCTFontTableOptionExcludeSynthetic) tagsCount := C.CFArrayGetCount(ctags) var tags []uint32 var dataRefs []C.CFDataRef var dataLens []uint32 for i := C.CFIndex(0); i < tagsCount; i++ { tag := (C.CTFontTableTag)((uintptr)(C.CFArrayGetValueAtIndex(ctags, i))) dataRef := C.CTFontCopyTable(f, tag, 0) // retained tags = append(tags, uint32(tag)) dataRefs = append(dataRefs, dataRef) dataLens = append(dataLens, uint32(C.CFDataGetLength(dataRef))) } totalLen := 0 for _, l := range dataLens { totalLen += int(l) } // Big-endian output. buf := make([]byte, 0, 12+16*len(tags)+totalLen) write16 := func(x uint16) { buf = append(buf, byte(x>>8), byte(x)) } write32 := func(x uint32) { buf = append(buf, byte(x>>24), byte(x>>16), byte(x>>8), byte(x)) } // File format description: http://www.microsoft.com/typography/otspec/otff.htm write32(0x00010000) // version 1.0 write16(uint16(len(tags))) // numTables write16(0) // searchRange write16(0) // entrySelector write16(0) // rangeShift // Table tags, includes offsets into following data segments. offset := uint32(12 + 16*len(tags)) // offset starts after table tags for i, tag := range tags { write32(tag) write32(0) write32(offset) write32(dataLens[i]) offset += dataLens[i] } // Data segments. for i, dataRef := range dataRefs { data := (*[1<<31 - 2]byte)((unsafe.Pointer)(C.CFDataGetBytePtr(dataRef)))[:dataLens[i]] buf = append(buf, data...) C.CFRelease(C.CFTypeRef(dataRef)) } return buf }
// CFDictionaryToMap converts CFDictionaryRef to a map. func CFDictionaryToMap(cfDict C.CFDictionaryRef) (m map[C.CFTypeRef]C.CFTypeRef) { count := C.CFDictionaryGetCount(cfDict) if count > 0 { keys := make([]C.CFTypeRef, count) values := make([]C.CFTypeRef, count) C.CFDictionaryGetKeysAndValues(cfDict, (*unsafe.Pointer)(&keys[0]), (*unsafe.Pointer)(&values[0])) m = make(map[C.CFTypeRef]C.CFTypeRef, count) for i := C.CFIndex(0); i < count; i++ { m[keys[i]] = values[i] } } return }
// wrapper for C.CFDictionaryCreate, since referencing the callbacks in 2 separate files // seems to be triggering some sort of "redefinition" error in cgo func createCFDictionary(keys, values []cfTypeRef) C.CFDictionaryRef { if len(keys) != len(values) { panic("plist: unexpected length difference between keys and values") } var keyPtr, valPtr *unsafe.Pointer if len(keys) > 0 { keyPtr = (*unsafe.Pointer)(&keys[0]) valPtr = (*unsafe.Pointer)(&values[0]) } keyCallbacks := (*C.CFDictionaryKeyCallBacks)(&C.kCFTypeDictionaryKeyCallBacks) valCallbacks := (*C.CFDictionaryValueCallBacks)(&C.kCFTypeDictionaryValueCallBacks) return C.CFDictionaryCreate(nil, keyPtr, valPtr, C.CFIndex(len(keys)), keyCallbacks, valCallbacks) }
// BytesToCFData will return a CFDataRef and if non-nil, must be released with // Release(ref). func BytesToCFData(b []byte) (C.CFDataRef, error) { if uint64(len(b)) > math.MaxUint32 { return nil, errors.New("Data is too large") } var p *C.UInt8 if len(b) > 0 { p = (*C.UInt8)(&b[0]) } cfData := C.CFDataCreate(nil, p, C.CFIndex(len(b))) if cfData == nil { return nil, fmt.Errorf("CFDataCreate failed") } return cfData, nil }
// The returned CFDictionaryRef, if non-nil, must be released via CFRelease. func mapToCFDictionary(m map[C.CFTypeRef]C.CFTypeRef) C.CFDictionaryRef { var keys, values []unsafe.Pointer for key, value := range m { keys = append(keys, unsafe.Pointer(key)) values = append(values, unsafe.Pointer(value)) } numValues := len(values) var keysPointer, valuesPointer *unsafe.Pointer if numValues > 0 { keysPointer = &keys[0] valuesPointer = &values[0] } return C.CFDictionaryCreate(nil, keysPointer, valuesPointer, C.CFIndex(numValues), &C.kCFTypeDictionaryKeyCallBacks, &C.kCFTypeDictionaryValueCallBacks) }
// StringToCFString will return a CFStringRef and if non-nil, must be released with // Release(ref). func StringToCFString(s string) (C.CFStringRef, error) { if !utf8.ValidString(s) { return nil, errors.New("Invalid UTF-8 string") } if uint64(len(s)) > math.MaxUint32 { return nil, errors.New("String is too large") } bytes := []byte(s) var p *C.UInt8 if len(bytes) > 0 { p = (*C.UInt8)(&bytes[0]) } return C.CFStringCreateWithBytes(nil, p, C.CFIndex(len(s)), C.kCFStringEncodingUTF8, C.false), nil }
// ===== CFString ===== // convertStringToCFString may return nil if the input string is not a valid UTF-8 string func convertStringToCFString(str string) C.CFStringRef { var bytes *C.UInt8 var byteCount C.CFIndex if len(str) > 0 { // check the string for invalid encodings // We could use unicode.ValidString() but we also want to count the desired buffer size // and there's no sense in iterating the string more than we have to var errorCount int for i, r := range str { if r == utf8.RuneError { // This may be a valid value in the string. Re-decode it _, size := utf8.DecodeRuneInString(str[i:]) if size == 1 { errorCount++ } } } if errorCount == 0 { // go through unsafe to get the string bytes directly without the copy header := (*reflect.StringHeader)(unsafe.Pointer(&str)) bytes = (*C.UInt8)(unsafe.Pointer(header.Data)) byteCount = C.CFIndex(header.Len) } else { // our desired buffer is the length of s, minus the invalid bytes, plus the // replacement bytes. buf := make([]byte, len(str)+(errorCount*(runeErrorLen-1))) i := 0 for _, r := range str { i += utf8.EncodeRune(buf[i:], r) } bytes = (*C.UInt8)(unsafe.Pointer(&buf[0])) byteCount = C.CFIndex(len(buf)) } } return C.CFStringCreateWithBytes(nil, bytes, byteCount, C.kCFStringEncodingUTF8, C.false) }
func fromCFString(cstr C.CFStringRef) string { defer C.CFRelease(C.CFTypeRef(cstr)) var ( buf []C.char ok C.Boolean size uint = 1024 ) for ok == C.FALSE { buf = make([]C.char, size) ok = C.CFStringGetCString(cstr, &buf[0], C.CFIndex(len(buf)), C.kCFStringEncodingUTF8) size *= 2 } return C.GoString(&buf[0]) }
// MapToCFDictionary will return a CFDictionaryRef and if non-nil, must be // released with Release(ref). func MapToCFDictionary(m map[C.CFTypeRef]C.CFTypeRef) (C.CFDictionaryRef, error) { var keys, values []unsafe.Pointer for key, value := range m { keys = append(keys, unsafe.Pointer(key)) values = append(values, unsafe.Pointer(value)) } numValues := len(values) var keysPointer, valuesPointer *unsafe.Pointer if numValues > 0 { keysPointer = &keys[0] valuesPointer = &values[0] } cfDict := C.CFDictionaryCreate(nil, keysPointer, valuesPointer, C.CFIndex(numValues), &C.kCFTypeDictionaryKeyCallBacks, &C.kCFTypeDictionaryValueCallBacks) if cfDict == nil { return nil, fmt.Errorf("CFDictionaryCreate failed") } return cfDict, nil }
func (dev *osxDevice) setReport(typ C.IOHIDReportType, data []byte) error { var reportNo int32 = int32(data[0]) if reportNo == 0 { data = data[1:] } if !dev.disconnected { res := C.IOHIDDeviceSetReport(dev.osDevice, typ, C.CFIndex(reportNo), (*C.uint8_t)(&data[0]), C.CFIndex(len(data))) if res == C.kIOReturnSuccess { return nil } else { return ioReturnToErr(res) } } return errors.New("device disconnected") }
func NSStringToString(inString Object) string { cr := C.CFStringRef(unsafe.Pointer(inString)) var usedBufLen C.CFIndex rng := C.CFRange{C.CFIndex(0), C.CFStringGetLength(cr)} n := int(C.CFStringGetBytes(cr, rng, C.kCFStringEncodingUTF8, 0, 0, nil, 0, &usedBufLen)) if n <= 0 { return "" } buf := make([]byte, int(usedBufLen)) C.CFStringGetBytes(cr, rng, C.kCFStringEncodingUTF8, 0, 0, (*C.UInt8)(unsafe.Pointer(&buf[0])), C.CFIndex(len(buf)), &usedBufLen) sh := &reflect.StringHeader{ Data: uintptr(unsafe.Pointer(&buf[0])), Len: int(usedBufLen), } return *(*string)(unsafe.Pointer(sh)) }
// Converts a CGStringRef object to a Go string. func CFStringToGoString(s C.CFStringRef) string { ptr := C.CFStringGetCStringPtr(s, C.kCFStringEncodingUTF8) if ptr != nil { return C.GoString(ptr) } n := C.CFStringGetLength(s) b := make([]byte, int(4*n)) C.CFStringGetBytes( s, C.CFRangeMake(0, n), C.kCFStringEncodingUTF8, '?', 0, (*C.UInt8)(unsafe.Pointer(&b[0])), C.CFIndex(len(b)), &n, ) return string(b[:n]) }
func NSString(inString string) Object { l := C.CFIndex(len(inString)) ret := C.CFStringCreateWithBytes(nil, *(**C.UInt8)(unsafe.Pointer(&inString)), l, C.kCFStringEncodingUTF8, 0) return Object(unsafe.Pointer(ret)) }
// The function reads the content of the text reader given as argument and // produces a C.CFAttributedStringRef object that most closely represtns the // input. // The program needs to call C.CFRelease on the returned C.CFAttributedStringRef // when it doesn't need it anymore to avoid any memory leak. func CFAttributedStringCreateWithTextReader(r text.Reader) C.CFAttributedStringRef { style := text.Style{} b := &bytes.Buffer{} b.Grow(1000) styles := make([]C.Style__, 0, 10) offset := 0 for { c, err := r.ReadChar() if err != nil { break } s := text.StyleOf(c) s.Offset = 0 if s != style { style = s s.Offset = offset font := C.CTFontRef(nil) switch f := s.Face.(type) { case *face: font = f.ref } styles = append(styles, C.Style__{ _range: C.CFRange{location: C.CFIndex(offset)}, font: font, foreground: makeRGBA__(imageColor(s.Foreground, color.Black)), background: makeRGBA__(imageColor(s.Background, color.Transparent)), }) } b.WriteRune(c.Rune) offset++ } if n := len(styles); n != 0 { n-- styles[n]._range.length = C.CFIndex(offset) - styles[n]._range.location for i := range styles[:n] { s0 := &styles[i] s1 := &styles[i+1] s0._range.length = s1._range.location - s0._range.location } } d := b.Bytes() s := C.CFStringCreateWithBytesNoCopy( nil, (*C.UInt8)(&d[0]), C.CFIndex(len(d)), C.kCFStringEncodingUTF8, 0, C.kCFAllocatorNull, ) defer C.CFRelease(C.CFTypeRef(s)) return C.CFAttributedStringCreateWithStyles__(nil, s, &styles[0], C.size_t(len(styles))) }
func cfstring(s string) C.CFStringRef { n := C.CFIndex(len(s)) return C.CFStringCreateWithBytes(nil, *(**C.UInt8)(unsafe.Pointer(&s)), n, C.kCFStringEncodingUTF8, 0) }
// FindIdentity ... // IMPORTANT: you have to C.CFRelease the returned items (one-by-one)!! // you can use the ReleaseIdentityWithRefList method to do that func FindIdentity(identityLabel string, isFullLabelMatch bool) ([]IdentityWithRefModel, error) { queryDict := C.CFDictionaryCreateMutable(nil, 0, nil, nil) defer C.CFRelease(C.CFTypeRef(queryDict)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecClass), unsafe.Pointer(C.kSecClassIdentity)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecMatchLimit), unsafe.Pointer(C.kSecMatchLimitAll)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecReturnAttributes), unsafe.Pointer(C.kCFBooleanTrue)) C.CFDictionaryAddValue(queryDict, unsafe.Pointer(C.kSecReturnRef), unsafe.Pointer(C.kCFBooleanTrue)) var resultRefs C.CFTypeRef osStatusCode := C.SecItemCopyMatching(queryDict, &resultRefs) if osStatusCode != C.errSecSuccess { return nil, fmt.Errorf("Failed to call SecItemCopyMatch - OSStatus: %d", osStatusCode) } defer C.CFRelease(C.CFTypeRef(resultRefs)) identitiesArrRef := C.CFArrayRef(resultRefs) identitiesCount := C.CFArrayGetCount(identitiesArrRef) if identitiesCount < 1 { return nil, fmt.Errorf("No Identity (certificate + related private key) found in your Keychain!") } log.Debugf("identitiesCount: %d", identitiesCount) // filter the identities, by label retIdentityRefs := []IdentityWithRefModel{} for i := C.CFIndex(0); i < identitiesCount; i++ { aIdentityRef := C.CFArrayGetValueAtIndex(identitiesArrRef, i) log.Debugf("aIdentityRef: %#v", aIdentityRef) aIdentityDictRef := C.CFDictionaryRef(aIdentityRef) log.Debugf("aIdentityDictRef: %#v", aIdentityDictRef) lablCSting := C.CString("labl") defer C.free(unsafe.Pointer(lablCSting)) vrefCSting := C.CString("v_Ref") defer C.free(unsafe.Pointer(vrefCSting)) labl, err := getCFDictValueUTF8String(aIdentityDictRef, C.CFTypeRef(convertCStringToCFString(lablCSting))) if err != nil { return nil, fmt.Errorf("FindIdentity: failed to get 'labl' property: %s", err) } log.Debugf("labl: %#v", labl) if isFullLabelMatch { if labl != identityLabel { continue } } else { if !strings.Contains(labl, identityLabel) { continue } } log.Debugf("Found identity with label: %s", labl) vrefRef, err := getCFDictValueRef(aIdentityDictRef, C.CFTypeRef(convertCStringToCFString(vrefCSting))) if err != nil { return nil, fmt.Errorf("FindIdentity: failed to get 'v_Ref' property: %s", err) } log.Debugf("vrefRef: %#v", vrefRef) // retain the pointer vrefRef = C.CFRetain(vrefRef) // store it retIdentityRefs = append(retIdentityRefs, IdentityWithRefModel{ KeychainRef: vrefRef, Label: labl, }) } return retIdentityRefs, nil }