1// Copyright 2020 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build darwin
6
7// Package macOS provides cgo-less wrappers for Core Foundation and
8// Security.framework, similarly to how package syscall provides access to
9// libSystem.dylib.
10package macOS
11
12import (
13	"bytes"
14	"errors"
15	"internal/abi"
16	"runtime"
17	"time"
18	"unsafe"
19)
20
21// Core Foundation linker flags for the external linker. See Issue 42459.
22//
23//go:cgo_ldflag "-framework"
24//go:cgo_ldflag "CoreFoundation"
25
26// CFRef is an opaque reference to a Core Foundation object. It is a pointer,
27// but to memory not owned by Go, so not an unsafe.Pointer.
28type CFRef uintptr
29
30// CFDataToSlice returns a copy of the contents of data as a bytes slice.
31func CFDataToSlice(data CFRef) []byte {
32	length := CFDataGetLength(data)
33	ptr := CFDataGetBytePtr(data)
34	src := unsafe.Slice((*byte)(unsafe.Pointer(ptr)), length)
35	return bytes.Clone(src)
36}
37
38// CFStringToString returns a Go string representation of the passed
39// in CFString, or an empty string if it's invalid.
40func CFStringToString(ref CFRef) string {
41	data, err := CFStringCreateExternalRepresentation(ref)
42	if err != nil {
43		return ""
44	}
45	b := CFDataToSlice(data)
46	CFRelease(data)
47	return string(b)
48}
49
50// TimeToCFDateRef converts a time.Time into an apple CFDateRef.
51func TimeToCFDateRef(t time.Time) CFRef {
52	secs := t.Sub(time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)).Seconds()
53	ref := CFDateCreate(secs)
54	return ref
55}
56
57type CFString CFRef
58
59const kCFAllocatorDefault = 0
60const kCFStringEncodingUTF8 = 0x08000100
61
62//go:cgo_import_dynamic x509_CFDataCreate CFDataCreate "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
63
64func BytesToCFData(b []byte) CFRef {
65	p := unsafe.Pointer(unsafe.SliceData(b))
66	ret := syscall(abi.FuncPCABI0(x509_CFDataCreate_trampoline), kCFAllocatorDefault, uintptr(p), uintptr(len(b)), 0, 0, 0)
67	runtime.KeepAlive(p)
68	return CFRef(ret)
69}
70func x509_CFDataCreate_trampoline()
71
72//go:cgo_import_dynamic x509_CFStringCreateWithBytes CFStringCreateWithBytes "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
73
74// StringToCFString returns a copy of the UTF-8 contents of s as a new CFString.
75func StringToCFString(s string) CFString {
76	p := unsafe.Pointer(unsafe.StringData(s))
77	ret := syscall(abi.FuncPCABI0(x509_CFStringCreateWithBytes_trampoline), kCFAllocatorDefault, uintptr(p),
78		uintptr(len(s)), uintptr(kCFStringEncodingUTF8), 0 /* isExternalRepresentation */, 0)
79	runtime.KeepAlive(p)
80	return CFString(ret)
81}
82func x509_CFStringCreateWithBytes_trampoline()
83
84//go:cgo_import_dynamic x509_CFDictionaryGetValueIfPresent CFDictionaryGetValueIfPresent "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
85
86func CFDictionaryGetValueIfPresent(dict CFRef, key CFString) (value CFRef, ok bool) {
87	ret := syscall(abi.FuncPCABI0(x509_CFDictionaryGetValueIfPresent_trampoline), uintptr(dict), uintptr(key),
88		uintptr(unsafe.Pointer(&value)), 0, 0, 0)
89	if ret == 0 {
90		return 0, false
91	}
92	return value, true
93}
94func x509_CFDictionaryGetValueIfPresent_trampoline()
95
96const kCFNumberSInt32Type = 3
97
98//go:cgo_import_dynamic x509_CFNumberGetValue CFNumberGetValue "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
99
100func CFNumberGetValue(num CFRef) (int32, error) {
101	var value int32
102	ret := syscall(abi.FuncPCABI0(x509_CFNumberGetValue_trampoline), uintptr(num), uintptr(kCFNumberSInt32Type),
103		uintptr(unsafe.Pointer(&value)), 0, 0, 0)
104	if ret == 0 {
105		return 0, errors.New("CFNumberGetValue call failed")
106	}
107	return value, nil
108}
109func x509_CFNumberGetValue_trampoline()
110
111//go:cgo_import_dynamic x509_CFDataGetLength CFDataGetLength "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
112
113func CFDataGetLength(data CFRef) int {
114	ret := syscall(abi.FuncPCABI0(x509_CFDataGetLength_trampoline), uintptr(data), 0, 0, 0, 0, 0)
115	return int(ret)
116}
117func x509_CFDataGetLength_trampoline()
118
119//go:cgo_import_dynamic x509_CFDataGetBytePtr CFDataGetBytePtr "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
120
121func CFDataGetBytePtr(data CFRef) uintptr {
122	ret := syscall(abi.FuncPCABI0(x509_CFDataGetBytePtr_trampoline), uintptr(data), 0, 0, 0, 0, 0)
123	return ret
124}
125func x509_CFDataGetBytePtr_trampoline()
126
127//go:cgo_import_dynamic x509_CFArrayGetCount CFArrayGetCount "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
128
129func CFArrayGetCount(array CFRef) int {
130	ret := syscall(abi.FuncPCABI0(x509_CFArrayGetCount_trampoline), uintptr(array), 0, 0, 0, 0, 0)
131	return int(ret)
132}
133func x509_CFArrayGetCount_trampoline()
134
135//go:cgo_import_dynamic x509_CFArrayGetValueAtIndex CFArrayGetValueAtIndex "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
136
137func CFArrayGetValueAtIndex(array CFRef, index int) CFRef {
138	ret := syscall(abi.FuncPCABI0(x509_CFArrayGetValueAtIndex_trampoline), uintptr(array), uintptr(index), 0, 0, 0, 0)
139	return CFRef(ret)
140}
141func x509_CFArrayGetValueAtIndex_trampoline()
142
143//go:cgo_import_dynamic x509_CFEqual CFEqual "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
144
145func CFEqual(a, b CFRef) bool {
146	ret := syscall(abi.FuncPCABI0(x509_CFEqual_trampoline), uintptr(a), uintptr(b), 0, 0, 0, 0)
147	return ret == 1
148}
149func x509_CFEqual_trampoline()
150
151//go:cgo_import_dynamic x509_CFRelease CFRelease "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
152
153func CFRelease(ref CFRef) {
154	syscall(abi.FuncPCABI0(x509_CFRelease_trampoline), uintptr(ref), 0, 0, 0, 0, 0)
155}
156func x509_CFRelease_trampoline()
157
158//go:cgo_import_dynamic x509_CFArrayCreateMutable CFArrayCreateMutable "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
159
160func CFArrayCreateMutable() CFRef {
161	ret := syscall(abi.FuncPCABI0(x509_CFArrayCreateMutable_trampoline), kCFAllocatorDefault, 0, 0 /* kCFTypeArrayCallBacks */, 0, 0, 0)
162	return CFRef(ret)
163}
164func x509_CFArrayCreateMutable_trampoline()
165
166//go:cgo_import_dynamic x509_CFArrayAppendValue CFArrayAppendValue "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
167
168func CFArrayAppendValue(array CFRef, val CFRef) {
169	syscall(abi.FuncPCABI0(x509_CFArrayAppendValue_trampoline), uintptr(array), uintptr(val), 0, 0, 0, 0)
170}
171func x509_CFArrayAppendValue_trampoline()
172
173//go:cgo_import_dynamic x509_CFDateCreate CFDateCreate "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
174
175func CFDateCreate(seconds float64) CFRef {
176	ret := syscall(abi.FuncPCABI0(x509_CFDateCreate_trampoline), kCFAllocatorDefault, 0, 0, 0, 0, seconds)
177	return CFRef(ret)
178}
179func x509_CFDateCreate_trampoline()
180
181//go:cgo_import_dynamic x509_CFErrorCopyDescription CFErrorCopyDescription "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
182
183func CFErrorCopyDescription(errRef CFRef) CFRef {
184	ret := syscall(abi.FuncPCABI0(x509_CFErrorCopyDescription_trampoline), uintptr(errRef), 0, 0, 0, 0, 0)
185	return CFRef(ret)
186}
187func x509_CFErrorCopyDescription_trampoline()
188
189//go:cgo_import_dynamic x509_CFErrorGetCode CFErrorGetCode "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
190
191func CFErrorGetCode(errRef CFRef) int {
192	return int(syscall(abi.FuncPCABI0(x509_CFErrorGetCode_trampoline), uintptr(errRef), 0, 0, 0, 0, 0))
193}
194func x509_CFErrorGetCode_trampoline()
195
196//go:cgo_import_dynamic x509_CFStringCreateExternalRepresentation CFStringCreateExternalRepresentation "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation"
197
198func CFStringCreateExternalRepresentation(strRef CFRef) (CFRef, error) {
199	ret := syscall(abi.FuncPCABI0(x509_CFStringCreateExternalRepresentation_trampoline), kCFAllocatorDefault, uintptr(strRef), kCFStringEncodingUTF8, 0, 0, 0)
200	if ret == 0 {
201		return 0, errors.New("string can't be represented as UTF-8")
202	}
203	return CFRef(ret), nil
204}
205func x509_CFStringCreateExternalRepresentation_trampoline()
206
207// syscall is implemented in the runtime package (runtime/sys_darwin.go)
208func syscall(fn, a1, a2, a3, a4, a5 uintptr, f1 float64) uintptr
209
210// ReleaseCFArray iterates through an array, releasing its contents, and then
211// releases the array itself. This is necessary because we cannot, easily, set the
212// CFArrayCallBacks argument when creating CFArrays.
213func ReleaseCFArray(array CFRef) {
214	for i := 0; i < CFArrayGetCount(array); i++ {
215		ref := CFArrayGetValueAtIndex(array, i)
216		CFRelease(ref)
217	}
218	CFRelease(array)
219}
220