2 * Copyright (c) 2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 Copyright (c) 1998-2011, Apple Inc. All rights reserved.
26 Responsibility: David Smith
29 #include <CoreFoundation/CFPreferences.h>
30 #include <CoreFoundation/CFURLAccess.h>
31 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
32 #include <CoreFoundation/CFUserNotification.h>
34 #include <CoreFoundation/CFPropertyList.h>
35 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
36 #include <CoreFoundation/CFBundle.h>
38 #include <CoreFoundation/CFNumber.h>
39 #include <CoreFoundation/CFPriv.h>
40 #include "CFInternal.h"
42 #if DEPLOYMENT_TARGET_MACOSX
44 #include <CoreFoundation/CFUUID.h>
47 #if DEBUG_PREFERENCES_MEMORY
48 #include "../Tests/CFCountingAllocator.c"
51 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
);
53 struct __CFPreferencesDomain
{
55 /* WARNING - not copying the callbacks; we know they are always static structs */
56 const _CFPreferencesDomainCallBacks
*_callBacks
;
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")
69 static CFAllocatorRef _preferencesAllocator
= NULL
;
70 __private_extern__ CFAllocatorRef
__CFPreferencesAllocator(void) {
71 if (!_preferencesAllocator
) {
72 #if DEBUG_PREFERENCES_MEMORY
73 _preferencesAllocator
= CFCountingAllocatorCreate(NULL
);
75 _preferencesAllocator
= __CFGetDefaultAllocator();
76 CFRetain(_preferencesAllocator
);
79 return _preferencesAllocator
;
82 // declaration for telling the
83 void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef
);
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
);
92 // CFCountingAllocatorReset(_preferencesAllocator);
96 #if DEPLOYMENT_TARGET_MACOSX
98 #pragma mark Determining host UUID
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
);
106 __private_extern__ CFStringRef
_CFGetHostUUIDString(void) {
107 static CFStringRef __hostUUIDString
= NULL
;
109 if (!__hostUUIDString
) {
110 CFUUIDBytes uuidBytes
;
112 struct timespec timeout
= {0, 0}; // Infinite timeout for gethostuuid()
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
);
121 CFUUIDRef uuidRef
= CFUUIDCreateFromUUIDBytes(kCFAllocatorSystemDefault
, uuidBytes
);
122 CFStringRef uuidAsString
= CFUUIDCreateString(kCFAllocatorSystemDefault
, uuidRef
);
124 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)uuidAsString
, (void *)&__hostUUIDString
)) {
125 CFRelease(uuidAsString
); // someone else made the assignment, so just release the extra string.
131 return __hostUUIDString
;
134 __private_extern__ CFStringRef
_CFPreferencesGetByHostIdentifierString(void) {
135 static CFStringRef __byHostIdentifierString
= NULL
;
137 if (!__byHostIdentifierString
) {
138 CFStringRef hostID
= _CFGetHostUUIDString();
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
);
147 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)downcasedField
, (void *)&__byHostIdentifierString
)) {
148 CFRelease(downcasedField
);
152 CFRelease(lastField
);
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
;
158 __byHostIdentifierString
= CFSTR("UnknownHostID");
162 return __byHostIdentifierString
;
167 __private_extern__ CFStringRef
_CFPreferencesGetByHostIdentifierString(void) {
174 static unsigned long __CFSafeLaunchLevel
= 0;
176 #if DEPLOYMENT_TARGET_WINDOWS
177 #include <shfolder.h>
181 static CFURLRef
_preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
182 CFAllocatorRef alloc
= __CFPreferencesAllocator();
183 #if DEPLOYMENT_TARGET_WINDOWS
187 CFMutableStringRef completePath
= _CFCreateApplicationRepositoryPath(alloc
, CSIDL_APPDATA
);
189 // append "Preferences\" and make the CFURL
190 CFStringAppend(completePath
, CFSTR("Preferences\\"));
191 url
= CFURLCreateWithFileSystemPath(alloc
, completePath
, kCFURLWindowsPathStyle
, true);
192 CFRelease(completePath
);
196 // Can't find a better place? Home directory then?
198 url
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
203 CFURLRef home
= NULL
;
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);
210 if (hostName
== kCFPreferencesCurrentHost
) url
= home
;
212 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Network/"), kCFURLPOSIXPathStyle
, true, home
);
217 home
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
219 url
= (safeLevel
> 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle
, true, home
) :
220 CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle
, true, home
);
223 if (hostName
!= kCFPreferencesAnyHost
) {
225 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("ByHost/"), kCFURLPOSIXPathStyle
, true, home
);
237 static CFURLRef
_preferencesDirectoryForUserHost(CFStringRef userName
, CFStringRef hostName
) {
238 return _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, __CFSafeLaunchLevel
);
241 static Boolean __CFPreferencesWritesXML
= true;
243 Boolean
__CFPreferencesShouldWriteXML(void) {
244 return __CFPreferencesWritesXML
;
247 static CFSpinLock_t domainCacheLock
= CFSpinLockInit
;
248 static CFMutableDictionaryRef domainCache
= NULL
; // mutable
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__
);
257 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
259 return _CFPreferencesDomainCreateValueForKey(domain
, key
);
265 CFDictionaryRef
CFPreferencesCopyMultiple(CFArrayRef keysToFetch
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
266 CFPreferencesDomainRef domain
;
267 CFMutableDictionaryRef result
;
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());
275 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
276 if (!domain
) return NULL
;
278 return _CFPreferencesDomainDeepCopyDictionary(domain
);
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
);
290 CFDictionarySetValue(result
, key
, value
);
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__
);
303 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
305 _CFPreferencesDomainSet(domain
, key
, value
);
306 _CFApplicationPreferencesDomainHasChanged(domain
);
311 void CFPreferencesSetMultiple(CFDictionaryRef keysToSet
, CFArrayRef keysToRemove
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
312 CFPreferencesDomainRef domain
;
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());
321 CFTypeRef
*keys
= NULL
;
323 CFIndex numOfKeysToSet
= 0;
325 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
328 CFAllocatorRef alloc
= CFGetAllocator(domain
);
330 if (keysToSet
&& (count
= CFDictionaryGetCount(keysToSet
))) {
331 numOfKeysToSet
= count
;
332 keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, 2*count
*sizeof(CFTypeRef
), 0);
334 values
= &(keys
[count
]);
335 CFDictionaryGetKeysAndValues(keysToSet
, keys
, values
);
336 for (idx
= 0; idx
< count
; idx
++) {
337 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], values
[idx
]);
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
);
349 _CFApplicationPreferencesDomainHasChanged(domain
);
351 if(keys
) CFAllocatorDeallocate(alloc
, keys
);
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__
);
358 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
359 if(domain
) _CFApplicationPreferencesDomainHasChanged(domain
);
361 return domain
? _CFPreferencesDomainSynchronize(domain
) : false;
364 CFArrayRef
CFPreferencesCopyApplicationList(CFStringRef user
, CFStringRef host
) {
366 CFAssert1(user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__
);
367 array
= _CFPreferencesCreateDomainList(user
, host
);
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__
);
375 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
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
);
389 result
= CFArrayCreate(alloc
, keys
, count
, &kCFTypeArrayCallBacks
);
391 CFAllocatorDeallocate(alloc
, keys
);
398 /****************************/
399 /* CFPreferencesDomain */
400 /****************************/
402 static CFStringRef
__CFPreferencesDomainCopyDescription(CFTypeRef cf
) {
403 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("<Private CFType %p>\n"), cf
);
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
);
413 static CFTypeID __kCFPreferencesDomainTypeID
= _kCFRuntimeNotATypeID
;
415 static const CFRuntimeClass __CFPreferencesDomainClass
= {
417 "CFPreferencesDomain",
420 __CFPreferencesDomainDeallocate
,
424 __CFPreferencesDomainCopyDescription
427 /* This is called once at CFInitialize() time. */
428 __private_extern__
void __CFPreferencesDomainInitialize(void) {
429 __kCFPreferencesDomainTypeID
= _CFRuntimeRegisterClass(&__CFPreferencesDomainClass
);
432 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
433 static CFStringRef
_CFPreferencesCachePrefixForUserHost(CFStringRef userName
, CFStringRef hostName
) {
434 if (userName
== kCFPreferencesAnyUser
&& hostName
== kCFPreferencesAnyHost
) {
435 return (CFStringRef
)CFRetain(CFSTR("*/*/"));
437 CFMutableStringRef result
= CFStringCreateMutable(__CFPreferencesAllocator(), 0);
438 if (userName
== kCFPreferencesCurrentUser
) {
439 userName
= CFCopyUserName();
440 CFStringAppend(result
, userName
);
442 CFStringAppend(result
, CFSTR("/"));
443 } else if (userName
== kCFPreferencesAnyUser
) {
444 CFStringAppend(result
, CFSTR("*/"));
446 if (hostName
== kCFPreferencesCurrentHost
) {
447 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
448 CFStringAppend(result
, hostID
);
449 CFStringAppend(result
, CFSTR("/"));
450 } else if (hostName
== kCFPreferencesAnyHost
) {
451 CFStringAppend(result
, CFSTR("*/"));
456 // 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
457 static CFStringRef
_CFPreferencesStandardDomainCacheKey(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
458 CFStringRef prefix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
459 CFStringRef result
= NULL
;
462 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@%@"), prefix
, domainName
);
468 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
469 CFURLRef theURL
= NULL
;
470 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
471 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
472 CFURLRef prefDir
= _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, safeLevel
);
474 CFStringRef fileName
;
475 Boolean mustFreeAppName
= false;
477 if (!prefDir
) return NULL
;
478 if (domainName
== kCFPreferencesAnyApplication
) {
479 appName
= CFSTR(".GlobalPreferences");
480 } else if (domainName
== kCFPreferencesCurrentApplication
) {
481 CFBundleRef mainBundle
= CFBundleGetMainBundle();
482 appName
= mainBundle
? CFBundleGetIdentifier(mainBundle
) : NULL
;
483 if (!appName
|| CFStringGetLength(appName
) == 0) {
484 appName
= _CFProcessNameString();
487 appName
= domainName
;
489 if (userName
!= kCFPreferencesAnyUser
) {
490 if (hostName
== kCFPreferencesAnyHost
) {
491 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
492 } else if (hostName
== kCFPreferencesCurrentHost
) {
493 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
494 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostID
);
496 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostName
); // sketchy - this allows someone to set an arbitrary hostname.
499 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
501 if (mustFreeAppName
) {
505 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
506 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLPOSIXPathStyle
, false, prefDir
);
507 #elif DEPLOYMENT_TARGET_WINDOWS
508 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLWindowsPathStyle
, false, prefDir
);
510 if (prefDir
) CFRelease(prefDir
);
514 //#error Do not know where to store NSUserDefaults on this platform
519 static CFURLRef
_CFPreferencesURLForStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
520 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName
, userName
, hostName
, __CFSafeLaunchLevel
);
523 CFPreferencesDomainRef
_CFPreferencesStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
524 CFPreferencesDomainRef domain
;
525 CFStringRef domainKey
;
526 Boolean shouldReleaseDomain
= true;
527 domainKey
= _CFPreferencesStandardDomainCacheKey(domainName
, userName
, hostName
);
528 __CFSpinLock(&domainCacheLock
);
530 CFAllocatorRef alloc
= __CFPreferencesAllocator();
531 domainCache
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
533 domain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
534 __CFSpinUnlock(&domainCacheLock
);
536 // Domain's not in the cache; load from permanent storage
537 CFURLRef theURL
= _CFPreferencesURLForStandardDomain(domainName
, userName
, hostName
);
539 domain
= _CFPreferencesDomainCreate(theURL
, &__kCFXMLPropertyListDomainCallBacks
);
541 if (userName
== kCFPreferencesAnyUser
) {
542 _CFPreferencesDomainSetIsWorldReadable(domain
, true);
546 __CFSpinLock(&domainCacheLock
);
547 if (domain
&& domainCache
) {
548 // 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.
549 CFPreferencesDomainRef checkDomain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
551 // 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.
552 // checkDomain was retrieved with a Get, so we don't want to over-release.
553 shouldReleaseDomain
= false;
554 CFRelease(domain
); // release the domain we synthesized earlier.
555 domain
= checkDomain
; // repoint it at the domain picked up out of the cache.
557 // We must not have found the domain in the cache, so it's ok for us to put this in.
558 CFDictionarySetValue(domainCache
, domainKey
, domain
);
560 if(shouldReleaseDomain
) CFRelease(domain
);
562 __CFSpinUnlock(&domainCacheLock
);
564 CFRelease(domainKey
);
568 static void __CFPreferencesPerformSynchronize(const void *key
, const void *value
, void *context
) {
569 CFPreferencesDomainRef domain
= (CFPreferencesDomainRef
)value
;
570 Boolean
*cumulativeResult
= (Boolean
*)context
;
571 if (!_CFPreferencesDomainSynchronize(domain
)) *cumulativeResult
= false;
574 __private_extern__ Boolean
_CFSynchronizeDomainCache(void) {
575 Boolean result
= true;
576 __CFSpinLock(&domainCacheLock
);
578 CFDictionaryApplyFunction(domainCache
, __CFPreferencesPerformSynchronize
, &result
);
580 __CFSpinUnlock(&domainCacheLock
);
584 __private_extern__
void _CFPreferencesPurgeDomainCache(void) {
585 _CFSynchronizeDomainCache();
586 __CFSpinLock(&domainCacheLock
);
588 CFRelease(domainCache
);
591 __CFSpinUnlock(&domainCacheLock
);
594 __private_extern__ CFArrayRef
_CFPreferencesCreateDomainList(CFStringRef userName
, CFStringRef hostName
) {
595 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
597 CFMutableArrayRef marray
;
598 CFStringRef
*cachedDomainKeys
;
599 CFPreferencesDomainRef
*cachedDomains
;
603 CFURLRef prefDir
= _preferencesDirectoryForUserHost(userName
, hostName
);
608 if (hostName
== kCFPreferencesAnyHost
) {
609 suffix
= CFStringCreateWithCString(prefAlloc
, ".plist", kCFStringEncodingASCII
);
610 } else if (hostName
== kCFPreferencesCurrentHost
) {
611 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
612 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostID
);
614 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostName
); // sketchy - this allows someone to create a domain list for an arbitrary hostname.
616 suffixLen
= CFStringGetLength(suffix
);
618 domains
= (CFArrayRef
)CFURLCreatePropertyFromResource(prefAlloc
, prefDir
, kCFURLFileDirectoryContents
, NULL
);
621 marray
= CFArrayCreateMutableCopy(prefAlloc
, 0, domains
);
624 marray
= CFArrayCreateMutable(prefAlloc
, 0, & kCFTypeArrayCallBacks
);
626 for (idx
= CFArrayGetCount(marray
)-1; idx
>= 0; idx
--) {
627 CFURLRef url
= (CFURLRef
)CFArrayGetValueAtIndex(marray
, idx
);
628 CFStringRef string
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
629 if (!CFStringHasSuffix(string
, suffix
)) {
630 CFArrayRemoveValueAtIndex(marray
, idx
);
632 CFStringRef dom
= CFStringCreateWithSubstring(prefAlloc
, string
, CFRangeMake(0, CFStringGetLength(string
) - suffixLen
));
633 if (CFEqual(dom
, CFSTR(".GlobalPreferences"))) {
634 CFArraySetValueAtIndex(marray
, idx
, kCFPreferencesAnyApplication
);
636 CFArraySetValueAtIndex(marray
, idx
, dom
);
644 // Now add any domains added in the cache; delete any that have been deleted in the cache
645 __CFSpinLock(&domainCacheLock
);
647 __CFSpinUnlock(&domainCacheLock
);
650 cnt
= CFDictionaryGetCount(domainCache
);
651 cachedDomainKeys
= (CFStringRef
*)CFAllocatorAllocate(prefAlloc
, 2 * cnt
* sizeof(CFStringRef
), 0);
652 cachedDomains
= (CFPreferencesDomainRef
*)(cachedDomainKeys
+ cnt
);
653 CFDictionaryGetKeysAndValues(domainCache
, (const void **)cachedDomainKeys
, (const void **)cachedDomains
);
654 __CFSpinUnlock(&domainCacheLock
);
655 suffix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
656 suffixLen
= CFStringGetLength(suffix
);
658 for (idx
= 0; idx
< cnt
; idx
++) {
659 CFStringRef domainKey
= cachedDomainKeys
[idx
];
660 CFPreferencesDomainRef domain
= cachedDomains
[idx
];
661 CFStringRef domainName
;
662 CFIndex keyCount
= 0;
664 if (!CFStringHasPrefix(domainKey
, suffix
)) continue;
665 domainName
= CFStringCreateWithSubstring(prefAlloc
, domainKey
, CFRangeMake(suffixLen
, CFStringGetLength(domainKey
) - suffixLen
));
666 if (CFEqual(domainName
, CFSTR("*"))) {
667 CFRelease(domainName
);
668 domainName
= (CFStringRef
)CFRetain(kCFPreferencesAnyApplication
);
669 } else if (CFEqual(domainName
, kCFPreferencesCurrentApplication
)) {
670 CFRelease(domainName
);
671 domainName
= (CFStringRef
)CFRetain(_CFProcessNameString());
673 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
674 keyCount
= d
? CFDictionaryGetCount(d
) : 0;
675 if (keyCount
) CFRelease(d
);
677 // Domain was deleted
678 SInt32 firstIndexOfValue
= CFArrayGetFirstIndexOfValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
);
679 if (0 <= firstIndexOfValue
) {
680 CFArrayRemoveValueAtIndex(marray
, firstIndexOfValue
);
682 } else if (!CFArrayContainsValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
)) {
683 CFArrayAppendValue(marray
, domainName
);
685 CFRelease(domainName
);
688 CFAllocatorDeallocate(prefAlloc
, cachedDomainKeys
);
693 // CFPreferencesDomain functions
696 CFPreferencesDomainRef
_CFPreferencesDomainCreate(CFTypeRef context
, const _CFPreferencesDomainCallBacks
*callBacks
) {
697 CFAllocatorRef alloc
= __CFPreferencesAllocator();
698 CFPreferencesDomainRef newDomain
;
699 CFAssert(callBacks
!= NULL
&& callBacks
->createDomain
!= NULL
&& callBacks
->freeDomain
!= NULL
&& callBacks
->fetchValue
!= NULL
&& callBacks
->writeValue
!= NULL
, __kCFLogAssertion
, "Cannot create a domain with NULL callbacks");
700 newDomain
= (CFPreferencesDomainRef
)_CFRuntimeCreateInstance(alloc
, __kCFPreferencesDomainTypeID
, sizeof(struct __CFPreferencesDomain
) - sizeof(CFRuntimeBase
), NULL
);
702 newDomain
->_callBacks
= callBacks
;
703 if (context
) CFRetain(context
);
704 newDomain
->_context
= context
;
705 newDomain
->_domain
= callBacks
->createDomain(alloc
, context
);
710 CFTypeRef
_CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain
, CFStringRef key
) {
711 return domain
->_callBacks
->fetchValue(domain
->_context
, domain
->_domain
, key
);
714 void _CFPreferencesDomainSet(CFPreferencesDomainRef domain
, CFStringRef key
, CFTypeRef value
) {
715 domain
->_callBacks
->writeValue(domain
->_context
, domain
->_domain
, key
, value
);
718 __private_extern__ Boolean
_CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain
) {
719 return domain
->_callBacks
->synchronize(domain
->_context
, domain
->_domain
);
722 __private_extern__
void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain
, Boolean isWorldReadable
) {
723 if (domain
->_callBacks
->setIsWorldReadable
) {
724 domain
->_callBacks
->setIsWorldReadable(domain
->_context
, domain
->_domain
, isWorldReadable
);
728 __private_extern__
void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain
) {
729 return domain
->_callBacks
->copyDomainDictionary
;
732 void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain
, CFDictionaryRef dict
) {
733 CFAllocatorRef alloc
= __CFPreferencesAllocator();
734 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
735 CFIndex idx
, count
= d
? CFDictionaryGetCount(d
) : 0;
737 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFTypeRef
), 0);
738 if (d
) CFDictionaryGetKeysAndValues(d
, keys
, NULL
);
739 for (idx
= 0; idx
< count
; idx
++) {
740 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], NULL
);
742 CFAllocatorDeallocate(alloc
, keys
);
745 if (dict
&& (count
= CFDictionaryGetCount(dict
)) != 0) {
746 CFStringRef
*newKeys
= (CFStringRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFStringRef
), 0);
747 CFDictionaryGetKeysAndValues(dict
, (const void **)newKeys
, NULL
);
748 for (idx
= 0; idx
< count
; idx
++) {
749 CFStringRef key
= newKeys
[idx
];
750 _CFPreferencesDomainSet(domain
, key
, (CFTypeRef
)CFDictionaryGetValue(dict
, key
));
752 CFAllocatorDeallocate(alloc
, newKeys
);
756 CFDictionaryRef
_CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain
) {
757 CFDictionaryRef result
= domain
->_callBacks
->copyDomainDictionary(domain
->_context
, domain
->_domain
);
758 if(result
&& CFDictionaryGetCount(result
) == 0) {
765 Boolean
_CFPreferencesDomainExists(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
766 CFPreferencesDomainRef domain
;
767 domain
= _CFPreferencesStandardDomain(domainName
, userName
, hostName
);
769 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
777 /* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
778 static void *createVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
) {
779 return CFDictionaryCreateMutable(allocator
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
782 static void freeVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *domain
) {
783 CFRelease((CFTypeRef
)domain
);
786 static CFTypeRef
fetchVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
) {
787 CFTypeRef result
= CFDictionaryGetValue((CFMutableDictionaryRef
)domain
, key
);
788 if (result
) CFRetain(result
);
792 static void writeVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
, CFTypeRef value
) {
794 CFDictionarySetValue((CFMutableDictionaryRef
)domain
, key
, value
);
796 CFDictionaryRemoveValue((CFMutableDictionaryRef
)domain
, key
);
799 static Boolean
synchronizeVolatileDomain(CFTypeRef context
, void *domain
) {
803 static void getVolatileKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
804 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)domain
;
805 CFIndex count
= CFDictionaryGetCount(dict
);
809 if ( count
< *numKeyValuePairs
) {
810 values
= *buf
+ count
;
811 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
812 } else if (alloc
!= kCFAllocatorNull
) {
814 *buf
= (void **)CFAllocatorReallocate(alloc
, *buf
, count
* 2 * sizeof(void *), 0);
816 *buf
= (void **)CFAllocatorAllocate(alloc
, count
*2*sizeof(void *), 0);
819 values
= *buf
+ count
;
820 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
824 *numKeyValuePairs
= count
;
827 static CFDictionaryRef
copyVolatileDomainDictionary(CFTypeRef context
, void *volatileDomain
) {
828 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)volatileDomain
;
830 CFDictionaryRef result
= (CFDictionaryRef
)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict
, kCFPropertyListImmutable
);
834 const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks
= {createVolatileDomain
, freeVolatileDomain
, fetchVolatileValue
, writeVolatileValue
, synchronizeVolatileDomain
, getVolatileKeysAndValues
, copyVolatileDomainDictionary
, NULL
};