/ CFXPCBridge.c
CFXPCBridge.c
  1  /*
  2  This file is part of Darling.
  3  
  4  Copyright (C) 2016 Lubos Dolezel
  5  
  6  Darling is free software: you can redistribute it and/or modify
  7  it under the terms of the GNU General Public License as published by
  8  the Free Software Foundation, either version 3 of the License, or
  9  (at your option) any later version.
 10  
 11  Darling is distributed in the hope that it will be useful,
 12  but WITHOUT ANY WARRANTY; without even the implied warranty of
 13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 14  GNU General Public License for more details.
 15  
 16  You should have received a copy of the GNU General Public License
 17  along with Darling.  If not, see <http://www.gnu.org/licenses/>.
 18  */
 19  
 20  #include <CoreFoundation/CFXPCBridge.h>
 21  #include <CoreFoundation/CFString.h>
 22  #include <CoreFoundation/CFDictionary.h>
 23  #include <CoreFoundation/CFNumber.h>
 24  #include <CoreFoundation/CFArray.h>
 25  #include <CoreFoundation/CFData.h>
 26  #include <CoreFoundation/CFNumber.h>
 27  
 28  #include <xpc/xpc.h>
 29  
 30  CFTypeRef _CFXPCCreateCFObjectFromXPCObject(xpc_object_t xo) {
 31  	xpc_type_t type = xpc_get_type(xo);
 32  
 33  	if (type == XPC_TYPE_DICTIONARY) {
 34  		size_t count = xpc_dictionary_get_count(xo);
 35  		CFStringRef* keys = malloc(count * sizeof(CFStringRef));
 36  		CFTypeRef* values = malloc(count * sizeof(CFTypeRef));
 37  		__block size_t idx = 0;
 38  
 39  		xpc_dictionary_apply(xo, ^bool(const char* key, xpc_object_t value) {
 40  			keys[idx] = CFStringCreateWithCString(NULL, key, kCFStringEncodingUTF8);
 41  			values[idx] = _CFXPCCreateCFObjectFromXPCObject(value);
 42  			++idx;
 43  			return true;
 44  		});
 45  
 46  		CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values, count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
 47  
 48  		for (size_t i = 0; i < count; ++i) {
 49  			CFRelease(keys[i]);
 50  			CFRelease(values[i]);
 51  		}
 52  		free(keys);
 53  		free(values);
 54  
 55  		return dict;
 56  	}
 57  
 58  	if (type == XPC_TYPE_ARRAY) {
 59  		size_t count = xpc_array_get_count(xo);
 60  		CFTypeRef* entries = malloc(count * sizeof(CFTypeRef));
 61  
 62  		xpc_array_apply(xo, ^bool(size_t idx, xpc_object_t entry) {
 63  			entries[idx] = _CFXPCCreateCFObjectFromXPCObject(entry);
 64  			return true;
 65  		});
 66  
 67  		CFArrayRef array = CFArrayCreate(NULL, entries, count, &kCFTypeArrayCallBacks);
 68  
 69  		for (size_t i = 0; i < count; ++i)
 70  			CFRelease(entries[i]);
 71  		free(entries);
 72  
 73  		return array;
 74  	}
 75  
 76  	if (type == XPC_TYPE_STRING) {
 77  		return CFStringCreateWithCString(NULL, xpc_string_get_string_ptr(xo), kCFStringEncodingUTF8);
 78  	}
 79  
 80  	if (type == XPC_TYPE_BOOL) {
 81  		return CFRetain(xpc_bool_get_value(xo) == true ? kCFBooleanTrue : kCFBooleanFalse);
 82  	}
 83  
 84  	if (type == XPC_TYPE_DATA) {
 85  		return CFDataCreate(NULL, xpc_data_get_bytes_ptr(xo), xpc_data_get_length(xo));
 86  	}
 87  
 88  	if (type == XPC_TYPE_INT64) {
 89  		int64_t val = xpc_int64_get_value(xo);
 90  		return CFNumberCreate(NULL, kCFNumberSInt64Type, &val);
 91  	}
 92  
 93  	if (type == XPC_TYPE_UINT64) {
 94  		uint64_t val = xpc_uint64_get_value(xo);
 95  		// CFNumber doesn't provide an unsigned 64-bit integer type
 96  		// i guess kCFNumberSInt64Type is the next best thing
 97  		return CFNumberCreate(NULL, kCFNumberSInt64Type, &val);
 98  	}
 99  
100  	if (type == XPC_TYPE_NULL) {
101  		return CFRetain(kCFNull);
102  	}
103  
104  	// TODO: double, date, and uuid
105  
106  	return NULL;
107  };
108  
109  static void CFXPCDictionaryApplier(const void* raw_key, const void* raw_value, void* context) {
110  	struct {
111  		char** keys;
112  		xpc_object_t* objs;
113  		size_t i;
114  	}* ctx = context;
115  
116  	// NOTE(@facekapow):
117  	// we're assuming that the dictionary passed in contains only CFStrings as keys.
118  	// is this a bad assumption? probably maybe kinda sorta possibly definitely.
119  	// however, xpc dictionaries can only contain strings as keys so...
120  	//
121  	// here's to hoping that whoever uses `_CFXPCCreateXPCObjectFromCFObject` knows this
122  	// and only uses CFString keys for their dictionaries :/
123  
124  	CFStringRef key = raw_key;
125  
126  	CFIndex length = CFStringGetLength(key);
127  	CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
128  	ctx->keys[ctx->i] = malloc(maxSize);
129  	CFStringGetCString(key, ctx->keys[ctx->i], maxSize, kCFStringEncodingUTF8);
130  
131  	// we're also assuming that the values in the dictionary are all CF objects
132  
133  	ctx->objs[ctx->i] = _CFXPCCreateXPCObjectFromCFObject(raw_value);
134  
135  	++ctx->i;
136  };
137  
138  xpc_object_t _CFXPCCreateXPCObjectFromCFObject(CFTypeRef attrs) {
139  	CFTypeID id = CFGetTypeID(attrs);
140  
141  	if (id == CFStringGetTypeID()) {
142  		CFStringRef str = attrs;
143  		CFIndex length = CFStringGetLength(str);
144  		CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
145  		char* tmp = malloc(maxSize);
146  		if (!CFStringGetCString(str, tmp, maxSize, kCFStringEncodingUTF8)) {
147  			free(tmp);
148  			return NULL;
149  		}
150  		xpc_object_t xo = xpc_string_create(tmp);
151  		free(tmp);
152  		return xo;
153  	}
154  
155  	if (id == CFDictionaryGetTypeID()) {
156  		CFDictionaryRef dict = attrs;
157  		CFIndex length = CFDictionaryGetCount(dict);
158  		char** keys = malloc(sizeof(char*) * length);
159  		xpc_object_t* objs = malloc(sizeof(xpc_object_t) * length);
160  		struct {
161  			char** keys;
162  			xpc_object_t* objs;
163  			size_t i;
164  		} ctx = {
165  			.keys = keys,
166  			.objs = objs,
167  			.i = 0,
168  		};
169  		CFDictionaryApplyFunction(dict, CFXPCDictionaryApplier, &ctx);
170  
171  		xpc_object_t xdict = xpc_dictionary_create(keys, objs, length);
172  
173  		for (CFIndex i = 0; i < length; ++i) {
174  			free(keys[i]);
175  			xpc_release(objs[i]);
176  		}
177  		free(keys);
178  		free(objs);
179  
180  		return xdict;
181  	}
182  
183  	if (id == CFBooleanGetTypeID()) {
184  		CFBooleanRef boolean = attrs;
185  		return xpc_bool_create(CFBooleanGetValue(boolean));
186  	}
187  
188  	if (id == CFArrayGetTypeID()) {
189  		CFArrayRef array = attrs;
190  		CFIndex length = CFArrayGetCount(array);
191  		xpc_object_t* objs = malloc(sizeof(xpc_object_t) * length);
192  
193  		for (CFIndex i = 0; i < length; ++i)
194  			objs[i] = _CFXPCCreateXPCObjectFromCFObject(CFArrayGetValueAtIndex(array, i));
195  
196  		xpc_object_t xarray = xpc_array_create(objs, length);
197  
198  		for (CFIndex i = 0; i < length; ++i)
199  			xpc_release(objs[i]);
200  		free(objs);
201  
202  		return xarray;
203  	}
204  
205  	if (id == CFDataGetTypeID()) {
206  		CFDataRef data = attrs;
207  		return xpc_data_create(CFDataGetBytePtr(data), CFDataGetLength(data));
208  	}
209  
210  	if (id == CFNumberGetTypeID()) {
211  		CFNumberRef num = attrs;
212  		int64_t tmp = 0;
213  		CFNumberGetValue(num, kCFNumberSInt64Type, &tmp);
214  		return xpc_int64_create(tmp);
215  	}
216  
217  	if (id == CFNullGetTypeID()) {
218  		return xpc_null_create();
219  	}
220  
221  	return NULL;
222  }
223  
224  xpc_object_t _CFXPCCreateXPCMessageWithCFObject(CFTypeRef obj) {
225  	return NULL;
226  }
227  
228  CFTypeRef _CFXPCCreateCFObjectFromXPCMessage(xpc_object_t obj) {
229  	return NULL;
230  }
231  
232  // TODO: _CFXPCCreateCFObjectFromXPCMessage
233  // This function takes the "ECF19A18-7AA6-4141-B4DC-A2E5123B2B5C" data value
234  // from the dictionary and parses it as a binary plist.