]> git.saurik.com Git - apple/cf.git/blob - CFPreferences.c
CF-1152.14.tar.gz
[apple/cf.git] / CFPreferences.c
1 /*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /* CFPreferences.c
25 Copyright (c) 1998-2014, Apple Inc. All rights reserved.
26 Responsibility: David Smith
27 */
28
29 #include <CoreFoundation/CFPreferences.h>
30 #include <CoreFoundation/CFURLAccess.h>
31 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
32 #include <CoreFoundation/CFUserNotification.h>
33 #endif
34 #include <CoreFoundation/CFPropertyList.h>
35 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
36 #include <CoreFoundation/CFBundle.h>
37 #endif
38 #include <CoreFoundation/CFNumber.h>
39 #include <CoreFoundation/CFPriv.h>
40 #include "CFInternal.h"
41 #include <sys/stat.h>
42 #if DEPLOYMENT_TARGET_MACOSX
43 #include <unistd.h>
44 #include <CoreFoundation/CFUUID.h>
45 #endif
46
47 #if DEBUG_PREFERENCES_MEMORY
48 #include "../Tests/CFCountingAllocator.c"
49 #endif
50
51 static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel);
52
53 struct __CFPreferencesDomain {
54 CFRuntimeBase _base;
55 /* WARNING - not copying the callbacks; we know they are always static structs */
56 const _CFPreferencesDomainCallBacks *_callBacks;
57 CFTypeRef _context;
58 void *_domain;
59 };
60
61 CONST_STRING_DECL(kCFPreferencesAnyApplication, "kCFPreferencesAnyApplication")
62 CONST_STRING_DECL(kCFPreferencesAnyHost, "kCFPreferencesAnyHost")
63 CONST_STRING_DECL(kCFPreferencesAnyUser, "kCFPreferencesAnyUser")
64 CONST_STRING_DECL(kCFPreferencesCurrentApplication, "kCFPreferencesCurrentApplication")
65 CONST_STRING_DECL(kCFPreferencesCurrentHost, "kCFPreferencesCurrentHost")
66 CONST_STRING_DECL(kCFPreferencesCurrentUser, "kCFPreferencesCurrentUser")
67
68
69 static CFAllocatorRef _preferencesAllocator = NULL;
70 CF_PRIVATE CFAllocatorRef __CFPreferencesAllocator(void) {
71 if (!_preferencesAllocator) {
72 #if DEBUG_PREFERENCES_MEMORY
73 _preferencesAllocator = CFCountingAllocatorCreate(NULL);
74 #else
75 _preferencesAllocator = __CFGetDefaultAllocator();
76 CFRetain(_preferencesAllocator);
77 #endif
78 }
79 return _preferencesAllocator;
80 }
81
82 // declaration for telling the
83 void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef);
84
85 #if DEBUG_PREFERENCES_MEMORY
86 #warning Preferences debugging on
87 CF_EXPORT void CFPreferencesDumpMem(void) {
88 if (_preferencesAllocator) {
89 // CFCountingAllocatorPrintSummary(_preferencesAllocator);
90 CFCountingAllocatorPrintPointers(_preferencesAllocator);
91 }
92 // CFCountingAllocatorReset(_preferencesAllocator);
93 }
94 #endif
95
96 #if DEPLOYMENT_TARGET_MACOSX
97 #pragma mark -
98 #pragma mark Determining host UUID
99 #endif
100
101 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
102 // The entry point is in libSystem.B.dylib, but not actually declared
103 // If this becomes available in a header (<rdar://problem/4943036>), I need to pull this out
104 int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp);
105
106 CF_PRIVATE CFStringRef _CFGetHostUUIDString(void) {
107 static CFStringRef __hostUUIDString = NULL;
108
109 if (!__hostUUIDString) {
110 CFUUIDBytes uuidBytes;
111 int getuuidErr = 0;
112 struct timespec timeout = {0, 0}; // Infinite timeout for gethostuuid()
113
114 getuuidErr = gethostuuid((unsigned char *)&uuidBytes, &timeout);
115 if (getuuidErr == -1) {
116 // An error has occurred trying to get the host UUID string. There's nothing we can do here, so we should just return NULL.
117 CFLog(kCFLogLevelWarning, CFSTR("_CFGetHostUUIDString: unable to determine UUID for host. Error: %d"), errno);
118 return NULL;
119 }
120
121 CFUUIDRef uuidRef = CFUUIDCreateFromUUIDBytes(kCFAllocatorSystemDefault, uuidBytes);
122 CFStringRef uuidAsString = CFUUIDCreateString(kCFAllocatorSystemDefault, uuidRef);
123
124 if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)uuidAsString, (void *)&__hostUUIDString)) {
125 CFRelease(uuidAsString); // someone else made the assignment, so just release the extra string.
126 }
127
128 CFRelease(uuidRef);
129 }
130
131 return __hostUUIDString;
132 }
133
134 CF_PRIVATE CFStringRef _CFPreferencesGetByHostIdentifierString(void) {
135 static CFStringRef __byHostIdentifierString = NULL;
136
137 if (!__byHostIdentifierString) {
138 CFStringRef hostID = _CFGetHostUUIDString();
139 if (hostID) {
140 if (CFStringHasPrefix(hostID, CFSTR("00000000-0000-1000-8000-"))) {
141 // If the host UUID is prefixed by "00000000-0000-1000-8000-" then the UUID returned is the "compatible" type. The last field of the string will be the MAC address of the primary ethernet interface of the computer. We use this for compatibility with existing by-host preferences.
142 CFStringRef lastField = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, hostID, CFRangeMake(24, 12));
143 CFMutableStringRef tmpstr = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, lastField);
144 CFStringLowercase(tmpstr, NULL);
145 CFStringRef downcasedField = CFStringCreateCopy(kCFAllocatorSystemDefault, tmpstr);
146
147 if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)downcasedField, (void *)&__byHostIdentifierString)) {
148 CFRelease(downcasedField);
149 }
150
151 CFRelease(tmpstr);
152 CFRelease(lastField);
153 } else {
154 // The host UUID is a full UUID, and we should just use that. This doesn't involve any additional string creation, so we should just be able to do the assignment.
155 __byHostIdentifierString = hostID;
156 }
157 } else {
158 __byHostIdentifierString = CFSTR("UnknownHostID");
159 }
160 }
161
162 return __byHostIdentifierString;
163 }
164
165 #else
166
167 CF_PRIVATE CFStringRef _CFPreferencesGetByHostIdentifierString(void) {
168 return CFSTR("");
169 }
170
171 #endif
172
173
174 static unsigned long __CFSafeLaunchLevel = 0;
175
176 #if DEPLOYMENT_TARGET_WINDOWS
177 #include <shfolder.h>
178
179 #endif
180
181 static CFURLRef _preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) {
182 CFAllocatorRef alloc = __CFPreferencesAllocator();
183 #if DEPLOYMENT_TARGET_WINDOWS
184
185 CFURLRef url = NULL;
186
187 CFMutableStringRef completePath = _CFCreateApplicationRepositoryPath(alloc, CSIDL_APPDATA);
188 if (completePath) {
189 // append "Preferences\" and make the CFURL
190 CFStringAppend(completePath, CFSTR("Preferences\\"));
191 url = CFURLCreateWithFileSystemPath(alloc, completePath, kCFURLWindowsPathStyle, true);
192 CFRelease(completePath);
193 }
194
195
196 // Can't find a better place? Home directory then?
197 if (url == NULL)
198 url = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName);
199
200 return url;
201
202 #else
203 CFURLRef home = NULL;
204 CFURLRef url;
205 int levels = 0;
206 // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
207 if (userName == kCFPreferencesAnyUser) {
208 if (!home) home = CFURLCreateWithFileSystemPath(alloc, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle, true);
209 levels = 1;
210 if (hostName == kCFPreferencesCurrentHost) url = home;
211 else {
212 url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Network/"), kCFURLPOSIXPathStyle, true, home);
213 levels ++;
214 CFRelease(home);
215 }
216 } else {
217 home = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName);
218 if (home) {
219 url = (safeLevel > 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle, true, home) :
220 CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle, true, home);
221 levels = 2;
222 CFRelease(home);
223 if (hostName != kCFPreferencesAnyHost) {
224 home = url;
225 url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("ByHost/"), kCFURLPOSIXPathStyle, true, home);
226 levels ++;
227 CFRelease(home);
228 }
229 } else {
230 url = NULL;
231 }
232 }
233 return url;
234 #endif
235 }
236
237 static CFURLRef _preferencesDirectoryForUserHost(CFStringRef userName, CFStringRef hostName) {
238 return _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, __CFSafeLaunchLevel);
239 }
240
241 static Boolean __CFPreferencesWritesXML = true;
242
243 Boolean __CFPreferencesShouldWriteXML(void) {
244 return __CFPreferencesWritesXML;
245 }
246
247 static CFLock_t domainCacheLock = CFLockInit;
248 static CFMutableDictionaryRef domainCache = NULL; // mutable
249
250 // Public API
251
252 CFTypeRef CFPreferencesCopyValue(CFStringRef key, CFStringRef appName, CFStringRef user, CFStringRef host) {
253 CFPreferencesDomainRef domain;
254 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
255 CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
256
257 domain = _CFPreferencesStandardDomain(appName, user, host);
258 if (domain) {
259 return _CFPreferencesDomainCreateValueForKey(domain, key);
260 } else {
261 return NULL;
262 }
263 }
264
265 CFDictionaryRef CFPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef appName, CFStringRef user, CFStringRef host) {
266 CFPreferencesDomainRef domain;
267 CFMutableDictionaryRef result;
268 CFIndex idx, count;
269
270 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
271 __CFGenericValidateType(appName, CFStringGetTypeID());
272 __CFGenericValidateType(user, CFStringGetTypeID());
273 __CFGenericValidateType(host, CFStringGetTypeID());
274
275 domain = _CFPreferencesStandardDomain(appName, user, host);
276 if (!domain) return NULL;
277 if (!keysToFetch) {
278 return _CFPreferencesDomainDeepCopyDictionary(domain);
279 } else {
280 __CFGenericValidateType(keysToFetch, CFArrayGetTypeID());
281 count = CFArrayGetCount(keysToFetch);
282 result = CFDictionaryCreateMutable(CFGetAllocator(domain), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
283 if (!result) return NULL;
284 for (idx = 0; idx < count; idx ++) {
285 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keysToFetch, idx);
286 CFPropertyListRef value;
287 __CFGenericValidateType(key, CFStringGetTypeID());
288 value = _CFPreferencesDomainCreateValueForKey(domain, key);
289 if (value) {
290 CFDictionarySetValue(result, key, value);
291 CFRelease(value);
292 }
293 }
294 }
295 return result;
296 }
297
298 void CFPreferencesSetValue(CFStringRef key, CFTypeRef value, CFStringRef appName, CFStringRef user, CFStringRef host) {
299 CFPreferencesDomainRef domain;
300 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
301 CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
302
303 domain = _CFPreferencesStandardDomain(appName, user, host);
304 if (domain) {
305 _CFPreferencesDomainSet(domain, key, value);
306 _CFApplicationPreferencesDomainHasChanged(domain);
307 }
308 }
309
310
311 void CFPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef appName, CFStringRef user, CFStringRef host) {
312 CFPreferencesDomainRef domain;
313 CFIndex idx, count;
314 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
315 if (keysToSet) __CFGenericValidateType(keysToSet, CFDictionaryGetTypeID());
316 if (keysToRemove) __CFGenericValidateType(keysToRemove, CFArrayGetTypeID());
317 __CFGenericValidateType(appName, CFStringGetTypeID());
318 __CFGenericValidateType(user, CFStringGetTypeID());
319 __CFGenericValidateType(host, CFStringGetTypeID());
320
321 CFTypeRef *keys = NULL;
322 CFTypeRef *values;
323 CFIndex numOfKeysToSet = 0;
324
325 domain = _CFPreferencesStandardDomain(appName, user, host);
326 if (!domain) return;
327
328 CFAllocatorRef alloc = CFGetAllocator(domain);
329
330 if (keysToSet && (count = CFDictionaryGetCount(keysToSet))) {
331 numOfKeysToSet = count;
332 keys = (CFTypeRef *)CFAllocatorAllocate(alloc, 2*count*sizeof(CFTypeRef), 0);
333 if (keys) {
334 values = &(keys[count]);
335 CFDictionaryGetKeysAndValues(keysToSet, keys, values);
336 for (idx = 0; idx < count; idx ++) {
337 _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], values[idx]);
338 }
339 }
340 }
341 if (keysToRemove && (count = CFArrayGetCount(keysToRemove))) {
342 for (idx = 0; idx < count; idx ++) {
343 CFStringRef removedKey = (CFStringRef)CFArrayGetValueAtIndex(keysToRemove, idx);
344 _CFPreferencesDomainSet(domain, removedKey, NULL);
345 }
346 }
347
348
349 _CFApplicationPreferencesDomainHasChanged(domain);
350
351 if(keys) CFAllocatorDeallocate(alloc, keys);
352 }
353
354 Boolean CFPreferencesSynchronize(CFStringRef appName, CFStringRef user, CFStringRef host) {
355 CFPreferencesDomainRef domain;
356 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
357
358 domain = _CFPreferencesStandardDomain(appName, user, host);
359 if(domain) _CFApplicationPreferencesDomainHasChanged(domain);
360
361 return domain ? _CFPreferencesDomainSynchronize(domain) : false;
362 }
363
364 CFArrayRef CFPreferencesCopyApplicationList(CFStringRef user, CFStringRef host) {
365 CFArrayRef array;
366 CFAssert1(user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__);
367 array = _CFPreferencesCreateDomainList(user, host);
368 return array;
369 }
370
371 CFArrayRef CFPreferencesCopyKeyList(CFStringRef appName, CFStringRef user, CFStringRef host) {
372 CFPreferencesDomainRef domain;
373 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
374
375 domain = _CFPreferencesStandardDomain(appName, user, host);
376 if (!domain) {
377 return NULL;
378 } else {
379 CFArrayRef result;
380
381 CFAllocatorRef alloc = __CFPreferencesAllocator();
382 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
383 CFIndex count = d ? CFDictionaryGetCount(d) : 0;
384 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0);
385 if (d) CFDictionaryGetKeysAndValues(d, keys, NULL);
386 if (count == 0) {
387 result = NULL;
388 } else {
389 result = CFArrayCreate(alloc, keys, count, &kCFTypeArrayCallBacks);
390 }
391 CFAllocatorDeallocate(alloc, keys);
392 if (d) CFRelease(d);
393 return result;
394 }
395 }
396
397
398 /****************************/
399 /* CFPreferencesDomain */
400 /****************************/
401
402 static CFStringRef __CFPreferencesDomainCopyDescription(CFTypeRef cf) {
403 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("<Private CFType %p>\n"), cf);
404 }
405
406 static void __CFPreferencesDomainDeallocate(CFTypeRef cf) {
407 const struct __CFPreferencesDomain *domain = (struct __CFPreferencesDomain *)cf;
408 CFAllocatorRef alloc = __CFPreferencesAllocator();
409 domain->_callBacks->freeDomain(alloc, domain->_context, domain->_domain);
410 if (domain->_context) CFRelease(domain->_context);
411 }
412
413 static CFTypeID __kCFPreferencesDomainTypeID = _kCFRuntimeNotATypeID;
414
415 static const CFRuntimeClass __CFPreferencesDomainClass = {
416 0,
417 "CFPreferencesDomain",
418 NULL, // init
419 NULL, // copy
420 __CFPreferencesDomainDeallocate,
421 NULL,
422 NULL,
423 NULL, //
424 __CFPreferencesDomainCopyDescription
425 };
426
427 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
428 static CFStringRef _CFPreferencesCachePrefixForUserHost(CFStringRef userName, CFStringRef hostName) {
429 if (userName == kCFPreferencesAnyUser && hostName == kCFPreferencesAnyHost) {
430 return (CFStringRef)CFRetain(CFSTR("*/*/"));
431 }
432 CFMutableStringRef result = CFStringCreateMutable(__CFPreferencesAllocator(), 0);
433 if (userName == kCFPreferencesCurrentUser) {
434 userName = CFCopyUserName();
435 CFStringAppend(result, userName);
436 CFRelease(userName);
437 CFStringAppend(result, CFSTR("/"));
438 } else if (userName == kCFPreferencesAnyUser) {
439 CFStringAppend(result, CFSTR("*/"));
440 }
441 if (hostName == kCFPreferencesCurrentHost) {
442 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
443 CFStringAppend(result, hostID);
444 CFStringAppend(result, CFSTR("/"));
445 } else if (hostName == kCFPreferencesAnyHost) {
446 CFStringAppend(result, CFSTR("*/"));
447 }
448 return result;
449 }
450
451 // It would be nice if we could remember the key for "well-known" combinations, so we're not constantly allocing more strings.... - REW 2/3/99
452 static CFStringRef _CFPreferencesStandardDomainCacheKey(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
453 CFStringRef prefix = _CFPreferencesCachePrefixForUserHost(userName, hostName);
454 CFStringRef result = NULL;
455
456 if (prefix) {
457 result = CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("%@%@"), prefix, domainName);
458 CFRelease(prefix);
459 }
460 return result;
461 }
462
463 static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) {
464 CFURLRef theURL = NULL;
465 CFAllocatorRef prefAlloc = __CFPreferencesAllocator();
466 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
467 CFURLRef prefDir = _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, safeLevel);
468 CFStringRef appName;
469 CFStringRef fileName;
470 Boolean mustFreeAppName = false;
471
472 if (!prefDir) return NULL;
473 if (domainName == kCFPreferencesAnyApplication) {
474 appName = CFSTR(".GlobalPreferences");
475 } else if (domainName == kCFPreferencesCurrentApplication) {
476 CFBundleRef mainBundle = CFBundleGetMainBundle();
477 appName = mainBundle ? CFBundleGetIdentifier(mainBundle) : NULL;
478 if (!appName || CFStringGetLength(appName) == 0) {
479 appName = _CFProcessNameString();
480 }
481 } else {
482 appName = domainName;
483 }
484 if (userName != kCFPreferencesAnyUser) {
485 if (hostName == kCFPreferencesAnyHost) {
486 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName);
487 } else if (hostName == kCFPreferencesCurrentHost) {
488 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
489 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostID);
490 } else {
491 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostName); // sketchy - this allows someone to set an arbitrary hostname.
492 }
493 } else {
494 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName);
495 }
496 if (mustFreeAppName) {
497 CFRelease(appName);
498 }
499 if (fileName) {
500 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
501 theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLPOSIXPathStyle, false, prefDir);
502 #elif DEPLOYMENT_TARGET_WINDOWS
503 theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLWindowsPathStyle, false, prefDir);
504 #endif
505 if (prefDir) CFRelease(prefDir);
506 CFRelease(fileName);
507 }
508 #else
509 //#error Do not know where to store NSUserDefaults on this platform
510 #endif
511 return theURL;
512 }
513
514 static CFURLRef _CFPreferencesURLForStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
515 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName, userName, hostName, __CFSafeLaunchLevel);
516 }
517
518 CFPreferencesDomainRef _CFPreferencesStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
519 CFPreferencesDomainRef domain;
520 CFStringRef domainKey;
521 Boolean shouldReleaseDomain = true;
522 domainKey = _CFPreferencesStandardDomainCacheKey(domainName, userName, hostName);
523 __CFLock(&domainCacheLock);
524 if (!domainCache) {
525 CFAllocatorRef alloc = __CFPreferencesAllocator();
526 domainCache = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
527 }
528 domain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey);
529 __CFUnlock(&domainCacheLock);
530 if (!domain) {
531 // Domain's not in the cache; load from permanent storage
532 CFURLRef theURL = _CFPreferencesURLForStandardDomain(domainName, userName, hostName);
533 if (theURL) {
534 domain = _CFPreferencesDomainCreate(theURL, &__kCFXMLPropertyListDomainCallBacks);
535
536 if (userName == kCFPreferencesAnyUser) {
537 _CFPreferencesDomainSetIsWorldReadable(domain, true);
538 }
539 CFRelease(theURL);
540 }
541 __CFLock(&domainCacheLock);
542 if (domain && domainCache) {
543 // We've just synthesized a domain & we're about to throw it in the domain cache. The problem is that someone else might have gotten in here behind our backs, so we can't just blindly set the domain (3021920). We'll need to check to see if this happened, and compensate.
544 CFPreferencesDomainRef checkDomain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey);
545 if(checkDomain) {
546 // Someone got in here ahead of us, so we shouldn't smash the domain we're given. checkDomain is the current version, we should use that.
547 // checkDomain was retrieved with a Get, so we don't want to over-release.
548 shouldReleaseDomain = false;
549 CFRelease(domain); // release the domain we synthesized earlier.
550 domain = checkDomain; // repoint it at the domain picked up out of the cache.
551 } else {
552 // We must not have found the domain in the cache, so it's ok for us to put this in.
553 CFDictionarySetValue(domainCache, domainKey, domain);
554 }
555 if(shouldReleaseDomain) CFRelease(domain);
556 }
557 __CFUnlock(&domainCacheLock);
558 }
559 CFRelease(domainKey);
560 return domain;
561 }
562
563 static void __CFPreferencesPerformSynchronize(const void *key, const void *value, void *context) {
564 CFPreferencesDomainRef domain = (CFPreferencesDomainRef)value;
565 Boolean *cumulativeResult = (Boolean *)context;
566 if (!_CFPreferencesDomainSynchronize(domain)) *cumulativeResult = false;
567 }
568
569 CF_PRIVATE Boolean _CFSynchronizeDomainCache(void) {
570 Boolean result = true;
571 __CFLock(&domainCacheLock);
572 if (domainCache) {
573 CFDictionaryApplyFunction(domainCache, __CFPreferencesPerformSynchronize, &result);
574 }
575 __CFUnlock(&domainCacheLock);
576 return result;
577 }
578
579 CF_PRIVATE void _CFPreferencesPurgeDomainCache(void) {
580 _CFSynchronizeDomainCache();
581 __CFLock(&domainCacheLock);
582 if (domainCache) {
583 CFRelease(domainCache);
584 domainCache = NULL;
585 }
586 __CFUnlock(&domainCacheLock);
587 }
588
589 CF_PRIVATE CFArrayRef _CFPreferencesCreateDomainList(CFStringRef userName, CFStringRef hostName) {
590 CFAllocatorRef prefAlloc = __CFPreferencesAllocator();
591 CFArrayRef domains;
592 CFMutableArrayRef marray;
593 CFStringRef *cachedDomainKeys;
594 CFPreferencesDomainRef *cachedDomains;
595 SInt32 idx, cnt;
596 CFStringRef suffix;
597 UInt32 suffixLen;
598 CFURLRef prefDir = _preferencesDirectoryForUserHost(userName, hostName);
599
600 if (!prefDir) {
601 return NULL;
602 }
603 if (hostName == kCFPreferencesAnyHost) {
604 suffix = CFStringCreateWithCString(prefAlloc, ".plist", kCFStringEncodingASCII);
605 } else if (hostName == kCFPreferencesCurrentHost) {
606 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
607 suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostID);
608 } else {
609 suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostName); // sketchy - this allows someone to create a domain list for an arbitrary hostname.
610 }
611 suffixLen = CFStringGetLength(suffix);
612
613 domains = (CFArrayRef)CFURLCreatePropertyFromResource(prefAlloc, prefDir, kCFURLFileDirectoryContents, NULL);
614 CFRelease(prefDir);
615 if (domains){
616 marray = CFArrayCreateMutableCopy(prefAlloc, 0, domains);
617 CFRelease(domains);
618 } else {
619 marray = CFArrayCreateMutable(prefAlloc, 0, & kCFTypeArrayCallBacks);
620 }
621 for (idx = CFArrayGetCount(marray)-1; idx >= 0; idx --) {
622 CFURLRef url = (CFURLRef)CFArrayGetValueAtIndex(marray, idx);
623 CFStringRef string = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
624 if (!CFStringHasSuffix(string, suffix)) {
625 CFArrayRemoveValueAtIndex(marray, idx);
626 } else {
627 CFStringRef dom = CFStringCreateWithSubstring(prefAlloc, string, CFRangeMake(0, CFStringGetLength(string) - suffixLen));
628 if (CFEqual(dom, CFSTR(".GlobalPreferences"))) {
629 CFArraySetValueAtIndex(marray, idx, kCFPreferencesAnyApplication);
630 } else {
631 CFArraySetValueAtIndex(marray, idx, dom);
632 }
633 CFRelease(dom);
634 }
635 CFRelease(string);
636 }
637 CFRelease(suffix);
638
639 // Now add any domains added in the cache; delete any that have been deleted in the cache
640 __CFLock(&domainCacheLock);
641 if (!domainCache) {
642 __CFUnlock(&domainCacheLock);
643 return marray;
644 }
645 cnt = CFDictionaryGetCount(domainCache);
646 cachedDomainKeys = (CFStringRef *)CFAllocatorAllocate(prefAlloc, 2 * cnt * sizeof(CFStringRef), 0);
647 cachedDomains = (CFPreferencesDomainRef *)(cachedDomainKeys + cnt);
648 CFDictionaryGetKeysAndValues(domainCache, (const void **)cachedDomainKeys, (const void **)cachedDomains);
649 __CFUnlock(&domainCacheLock);
650 suffix = _CFPreferencesCachePrefixForUserHost(userName, hostName);
651 suffixLen = CFStringGetLength(suffix);
652
653 for (idx = 0; idx < cnt; idx ++) {
654 CFStringRef domainKey = cachedDomainKeys[idx];
655 CFPreferencesDomainRef domain = cachedDomains[idx];
656 CFStringRef domainName;
657 CFIndex keyCount = 0;
658
659 if (!CFStringHasPrefix(domainKey, suffix)) continue;
660 domainName = CFStringCreateWithSubstring(prefAlloc, domainKey, CFRangeMake(suffixLen, CFStringGetLength(domainKey) - suffixLen));
661 if (CFEqual(domainName, CFSTR("*"))) {
662 CFRelease(domainName);
663 domainName = (CFStringRef)CFRetain(kCFPreferencesAnyApplication);
664 } else if (CFEqual(domainName, kCFPreferencesCurrentApplication)) {
665 CFRelease(domainName);
666 domainName = (CFStringRef)CFRetain(_CFProcessNameString());
667 }
668 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
669 keyCount = d ? CFDictionaryGetCount(d) : 0;
670 if (keyCount) CFRelease(d);
671 if (keyCount == 0) {
672 // Domain was deleted
673 SInt32 firstIndexOfValue = CFArrayGetFirstIndexOfValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName);
674 if (0 <= firstIndexOfValue) {
675 CFArrayRemoveValueAtIndex(marray, firstIndexOfValue);
676 }
677 } else if (!CFArrayContainsValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName)) {
678 CFArrayAppendValue(marray, domainName);
679 }
680 CFRelease(domainName);
681 }
682 CFRelease(suffix);
683 CFAllocatorDeallocate(prefAlloc, cachedDomainKeys);
684 return marray;
685 }
686
687 //
688 // CFPreferencesDomain functions
689 //
690
691 CFPreferencesDomainRef _CFPreferencesDomainCreate(CFTypeRef context, const _CFPreferencesDomainCallBacks *callBacks) {
692 static dispatch_once_t initOnce;
693 dispatch_once(&initOnce, ^{ __kCFPreferencesDomainTypeID = _CFRuntimeRegisterClass(&__CFPreferencesDomainClass); });
694 CFAllocatorRef alloc = __CFPreferencesAllocator();
695 CFPreferencesDomainRef newDomain;
696 CFAssert(callBacks != NULL && callBacks->createDomain != NULL && callBacks->freeDomain != NULL && callBacks->fetchValue != NULL && callBacks->writeValue != NULL, __kCFLogAssertion, "Cannot create a domain with NULL callbacks");
697 newDomain = (CFPreferencesDomainRef)_CFRuntimeCreateInstance(alloc, __kCFPreferencesDomainTypeID, sizeof(struct __CFPreferencesDomain) - sizeof(CFRuntimeBase), NULL);
698 if (newDomain) {
699 newDomain->_callBacks = callBacks;
700 if (context) CFRetain(context);
701 newDomain->_context = context;
702 newDomain->_domain = callBacks->createDomain(alloc, context);
703 }
704 return newDomain;
705 }
706
707 CFTypeRef _CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain, CFStringRef key) {
708 return domain->_callBacks->fetchValue(domain->_context, domain->_domain, key);
709 }
710
711 void _CFPreferencesDomainSet(CFPreferencesDomainRef domain, CFStringRef key, CFTypeRef value) {
712 domain->_callBacks->writeValue(domain->_context, domain->_domain, key, value);
713 }
714
715 CF_PRIVATE Boolean _CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain) {
716 return domain->_callBacks->synchronize(domain->_context, domain->_domain);
717 }
718
719 CF_PRIVATE void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain, Boolean isWorldReadable) {
720 if (domain->_callBacks->setIsWorldReadable) {
721 domain->_callBacks->setIsWorldReadable(domain->_context, domain->_domain, isWorldReadable);
722 }
723 }
724
725 CF_PRIVATE void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain) {
726 return domain->_callBacks->copyDomainDictionary;
727 }
728
729 void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain, CFDictionaryRef dict) {
730 CFAllocatorRef alloc = __CFPreferencesAllocator();
731 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
732 CFIndex idx, count = d ? CFDictionaryGetCount(d) : 0;
733
734 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0);
735 if (d) CFDictionaryGetKeysAndValues(d, keys, NULL);
736 for (idx = 0; idx < count; idx ++) {
737 _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], NULL);
738 }
739 CFAllocatorDeallocate(alloc, keys);
740 if (d) CFRelease(d);
741
742 if (dict && (count = CFDictionaryGetCount(dict)) != 0) {
743 CFStringRef *newKeys = (CFStringRef *)CFAllocatorAllocate(alloc, count * sizeof(CFStringRef), 0);
744 CFDictionaryGetKeysAndValues(dict, (const void **)newKeys, NULL);
745 for (idx = 0; idx < count; idx ++) {
746 CFStringRef key = newKeys[idx];
747 _CFPreferencesDomainSet(domain, key, (CFTypeRef)CFDictionaryGetValue(dict, key));
748 }
749 CFAllocatorDeallocate(alloc, newKeys);
750 }
751 }
752
753 CFDictionaryRef _CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain) {
754 CFDictionaryRef result = domain->_callBacks->copyDomainDictionary(domain->_context, domain->_domain);
755 if(result && CFDictionaryGetCount(result) == 0) {
756 CFRelease(result);
757 result = NULL;
758 }
759 return result;
760 }
761
762 Boolean _CFPreferencesDomainExists(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
763 CFPreferencesDomainRef domain;
764 domain = _CFPreferencesStandardDomain(domainName, userName, hostName);
765 if (domain) {
766 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
767 if (d) CFRelease(d);
768 return d != NULL;
769 } else {
770 return false;
771 }
772 }
773
774 /* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
775 static void *createVolatileDomain(CFAllocatorRef allocator, CFTypeRef context) {
776 return CFDictionaryCreateMutable(allocator, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
777 }
778
779 static void freeVolatileDomain(CFAllocatorRef allocator, CFTypeRef context, void *domain) {
780 CFRelease((CFTypeRef)domain);
781 }
782
783 static CFTypeRef fetchVolatileValue(CFTypeRef context, void *domain, CFStringRef key) {
784 CFTypeRef result = CFDictionaryGetValue((CFMutableDictionaryRef )domain, key);
785 if (result) CFRetain(result);
786 return result;
787 }
788
789 static void writeVolatileValue(CFTypeRef context, void *domain, CFStringRef key, CFTypeRef value) {
790 if (value)
791 CFDictionarySetValue((CFMutableDictionaryRef )domain, key, value);
792 else
793 CFDictionaryRemoveValue((CFMutableDictionaryRef )domain, key);
794 }
795
796 static Boolean synchronizeVolatileDomain(CFTypeRef context, void *domain) {
797 return true;
798 }
799
800 static void getVolatileKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *domain, void **buf[], CFIndex *numKeyValuePairs) {
801 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)domain;
802 CFIndex count = CFDictionaryGetCount(dict);
803
804 if (buf) {
805 void **values;
806 if ( count < *numKeyValuePairs ) {
807 values = *buf + count;
808 CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values);
809 } else if (alloc != kCFAllocatorNull) {
810 if (*buf) {
811 *buf = (void **)CFAllocatorReallocate(alloc, *buf, count * 2 * sizeof(void *), 0);
812 } else {
813 *buf = (void **)CFAllocatorAllocate(alloc, count*2*sizeof(void *), 0);
814 }
815 if (*buf) {
816 values = *buf + count;
817 CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values);
818 }
819 }
820 }
821 *numKeyValuePairs = count;
822 }
823
824 static CFDictionaryRef copyVolatileDomainDictionary(CFTypeRef context, void *volatileDomain) {
825 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)volatileDomain;
826
827 CFDictionaryRef result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict, kCFPropertyListImmutable);
828 return result;
829 }
830
831 const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks = {createVolatileDomain, freeVolatileDomain, fetchVolatileValue, writeVolatileValue, synchronizeVolatileDomain, getVolatileKeysAndValues, copyVolatileDomainDictionary, NULL};