2 * Copyright (c) 2009 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@
24 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
25 Responsibility: Chris Parker
28 #include <CoreFoundation/CFPreferences.h>
29 #include <CoreFoundation/CFURLAccess.h>
30 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
31 #include <CoreFoundation/CFUserNotification.h>
33 #include <CoreFoundation/CFPropertyList.h>
34 #include <CoreFoundation/CFBundle.h>
35 #include <CoreFoundation/CFNumber.h>
36 #include <CoreFoundation/CFPriv.h>
37 #include <CoreFoundation/CFPriv.h>
38 #include "CFInternal.h"
40 #if DEPLOYMENT_TARGET_MACOSX
42 #include <CoreFoundation/CFUUID.h>
45 #if DEBUG_PREFERENCES_MEMORY
46 #include "../Tests/CFCountingAllocator.c"
49 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
);
51 struct __CFPreferencesDomain
{
53 /* WARNING - not copying the callbacks; we know they are always static structs */
54 const _CFPreferencesDomainCallBacks
*_callBacks
;
59 CONST_STRING_DECL(kCFPreferencesAnyApplication
, "kCFPreferencesAnyApplication")
60 CONST_STRING_DECL(kCFPreferencesAnyHost
, "kCFPreferencesAnyHost")
61 CONST_STRING_DECL(kCFPreferencesAnyUser
, "kCFPreferencesAnyUser")
62 CONST_STRING_DECL(kCFPreferencesCurrentApplication
, "kCFPreferencesCurrentApplication")
63 CONST_STRING_DECL(kCFPreferencesCurrentHost
, "kCFPreferencesCurrentHost")
64 CONST_STRING_DECL(kCFPreferencesCurrentUser
, "kCFPreferencesCurrentUser")
67 static CFAllocatorRef _preferencesAllocator
= NULL
;
68 __private_extern__ CFAllocatorRef
__CFPreferencesAllocator(void) {
69 if (!_preferencesAllocator
) {
70 #if DEBUG_PREFERENCES_MEMORY
71 _preferencesAllocator
= CFCountingAllocatorCreate(NULL
);
73 _preferencesAllocator
= __CFGetDefaultAllocator();
74 CFRetain(_preferencesAllocator
);
77 return _preferencesAllocator
;
80 // declaration for telling the
81 void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef
);
83 #if DEBUG_PREFERENCES_MEMORY
84 #warning Preferences debugging on
85 CF_EXPORT
void CFPreferencesDumpMem(void) {
86 if (_preferencesAllocator
) {
87 // CFCountingAllocatorPrintSummary(_preferencesAllocator);
88 CFCountingAllocatorPrintPointers(_preferencesAllocator
);
90 // CFCountingAllocatorReset(_preferencesAllocator);
94 #if DEPLOYMENT_TARGET_MACOSX
96 #pragma mark Determining host UUID
99 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
100 // The entry point is in libSystem.B.dylib, but not actually declared
101 // If this becomes available in a header (<rdar://problem/4943036>), I need to pull this out
102 int gethostuuid(unsigned char *uuid_buf
, const struct timespec
*timeoutp
);
104 __private_extern__ CFStringRef
_CFGetHostUUIDString(void) {
105 static CFStringRef __hostUUIDString
= NULL
;
107 if (!__hostUUIDString
) {
108 CFUUIDBytes uuidBytes
;
110 struct timespec timeout
= {0, 0}; // Infinite timeout for gethostuuid()
112 getuuidErr
= gethostuuid((unsigned char *)&uuidBytes
, &timeout
);
113 if (getuuidErr
== -1) {
114 // An error has occurred trying to get the host UUID string. There's nothing we can do here, so we should just return NULL.
115 CFLog(kCFLogLevelWarning
, CFSTR("_CFGetHostUUIDString: unable to determine UUID for host. Error: %d"), errno
);
119 CFUUIDRef uuidRef
= CFUUIDCreateFromUUIDBytes(kCFAllocatorSystemDefault
, uuidBytes
);
120 CFStringRef uuidAsString
= CFUUIDCreateString(kCFAllocatorSystemDefault
, uuidRef
);
122 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)uuidAsString
, (void *)&__hostUUIDString
)) {
123 CFRelease(uuidAsString
); // someone else made the assignment, so just release the extra string.
129 return __hostUUIDString
;
132 __private_extern__ CFStringRef
_CFPreferencesGetByHostIdentifierString(void) {
133 static CFStringRef __byHostIdentifierString
= NULL
;
135 if (!__byHostIdentifierString
) {
136 CFStringRef hostID
= _CFGetHostUUIDString();
138 if (CFStringHasPrefix(hostID
, CFSTR("00000000-0000-1000-8000-"))) {
139 // 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.
140 CFStringRef lastField
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, hostID
, CFRangeMake(24, 12));
141 CFMutableStringRef tmpstr
= CFStringCreateMutableCopy(kCFAllocatorSystemDefault
, 0, lastField
);
142 CFStringLowercase(tmpstr
, NULL
);
143 CFStringRef downcasedField
= CFStringCreateCopy(kCFAllocatorSystemDefault
, tmpstr
);
145 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)downcasedField
, (void *)&__byHostIdentifierString
)) {
146 CFRelease(downcasedField
);
150 CFRelease(lastField
);
152 // 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.
153 __byHostIdentifierString
= hostID
;
156 __byHostIdentifierString
= CFSTR("UnknownHostID");
160 return __byHostIdentifierString
;
165 __private_extern__ CFStringRef
_CFPreferencesGetByHostIdentifierString(void) {
172 static unsigned long __CFSafeLaunchLevel
= 0;
174 #if DEPLOYMENT_TARGET_WINDOWS
175 #include <shfolder.h>
179 static CFURLRef
_preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
180 CFAllocatorRef alloc
= __CFPreferencesAllocator();
181 #if DEPLOYMENT_TARGET_WINDOWS
185 CFMutableStringRef completePath
= _CFCreateApplicationRepositoryPath(alloc
, CSIDL_APPDATA
);
187 // append "Preferences\" and make the CFURL
188 CFStringAppend(completePath
, CFSTR("Preferences\\"));
189 url
= CFURLCreateWithFileSystemPath(alloc
, completePath
, kCFURLWindowsPathStyle
, true);
190 CFRelease(completePath
);
194 // Can't find a better place? Home directory then?
196 url
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
201 CFURLRef home
= NULL
;
204 // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
205 if (userName
== kCFPreferencesAnyUser
) {
206 if (!home
) home
= CFURLCreateWithFileSystemPath(alloc
, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle
, true);
208 if (hostName
== kCFPreferencesCurrentHost
) url
= home
;
210 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Network/"), kCFURLPOSIXPathStyle
, true, home
);
215 home
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
217 url
= (safeLevel
> 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle
, true, home
) :
218 CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle
, true, home
);
221 if (hostName
!= kCFPreferencesAnyHost
) {
223 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("ByHost/"), kCFURLPOSIXPathStyle
, true, home
);
235 static CFURLRef
_preferencesDirectoryForUserHost(CFStringRef userName
, CFStringRef hostName
) {
236 return _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, __CFSafeLaunchLevel
);
239 static Boolean __CFPreferencesWritesXML
= true;
241 Boolean
__CFPreferencesShouldWriteXML(void) {
242 return __CFPreferencesWritesXML
;
245 static CFSpinLock_t domainCacheLock
= CFSpinLockInit
;
246 static CFMutableDictionaryRef domainCache
= NULL
; // mutable
250 CFTypeRef
CFPreferencesCopyValue(CFStringRef key
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
251 CFPreferencesDomainRef domain
;
252 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
253 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
255 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
257 return _CFPreferencesDomainCreateValueForKey(domain
, key
);
263 CFDictionaryRef
CFPreferencesCopyMultiple(CFArrayRef keysToFetch
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
264 CFPreferencesDomainRef domain
;
265 CFMutableDictionaryRef result
;
268 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
269 __CFGenericValidateType(appName
, CFStringGetTypeID());
270 __CFGenericValidateType(user
, CFStringGetTypeID());
271 __CFGenericValidateType(host
, CFStringGetTypeID());
273 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
274 if (!domain
) return NULL
;
276 return _CFPreferencesDomainDeepCopyDictionary(domain
);
278 __CFGenericValidateType(keysToFetch
, CFArrayGetTypeID());
279 count
= CFArrayGetCount(keysToFetch
);
280 result
= CFDictionaryCreateMutable(CFGetAllocator(domain
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
281 if (!result
) return NULL
;
282 for (idx
= 0; idx
< count
; idx
++) {
283 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keysToFetch
, idx
);
284 CFPropertyListRef value
;
285 __CFGenericValidateType(key
, CFStringGetTypeID());
286 value
= _CFPreferencesDomainCreateValueForKey(domain
, key
);
288 CFDictionarySetValue(result
, key
, value
);
296 void CFPreferencesSetValue(CFStringRef key
, CFTypeRef value
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
297 CFPreferencesDomainRef domain
;
298 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
299 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
301 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
303 _CFPreferencesDomainSet(domain
, key
, value
);
304 _CFApplicationPreferencesDomainHasChanged(domain
);
309 void CFPreferencesSetMultiple(CFDictionaryRef keysToSet
, CFArrayRef keysToRemove
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
310 CFPreferencesDomainRef domain
;
312 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
313 if (keysToSet
) __CFGenericValidateType(keysToSet
, CFDictionaryGetTypeID());
314 if (keysToRemove
) __CFGenericValidateType(keysToRemove
, CFArrayGetTypeID());
315 __CFGenericValidateType(appName
, CFStringGetTypeID());
316 __CFGenericValidateType(user
, CFStringGetTypeID());
317 __CFGenericValidateType(host
, CFStringGetTypeID());
319 CFTypeRef
*keys
= NULL
;
321 CFIndex numOfKeysToSet
= 0;
323 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
326 CFAllocatorRef alloc
= CFGetAllocator(domain
);
328 if (keysToSet
&& (count
= CFDictionaryGetCount(keysToSet
))) {
329 numOfKeysToSet
= count
;
330 keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, 2*count
*sizeof(CFTypeRef
), 0);
332 values
= &(keys
[count
]);
333 CFDictionaryGetKeysAndValues(keysToSet
, keys
, values
);
334 for (idx
= 0; idx
< count
; idx
++) {
335 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], values
[idx
]);
339 if (keysToRemove
&& (count
= CFArrayGetCount(keysToRemove
))) {
340 for (idx
= 0; idx
< count
; idx
++) {
341 CFStringRef removedKey
= (CFStringRef
)CFArrayGetValueAtIndex(keysToRemove
, idx
);
342 _CFPreferencesDomainSet(domain
, removedKey
, NULL
);
347 _CFApplicationPreferencesDomainHasChanged(domain
);
349 if(keys
) CFAllocatorDeallocate(alloc
, keys
);
352 Boolean
CFPreferencesSynchronize(CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
353 CFPreferencesDomainRef domain
;
354 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
356 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
357 if(domain
) _CFApplicationPreferencesDomainHasChanged(domain
);
359 return domain
? _CFPreferencesDomainSynchronize(domain
) : false;
362 CFArrayRef
CFPreferencesCopyApplicationList(CFStringRef user
, CFStringRef host
) {
364 CFAssert1(user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__
);
365 array
= _CFPreferencesCreateDomainList(user
, host
);
369 CFArrayRef
CFPreferencesCopyKeyList(CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
370 CFPreferencesDomainRef domain
;
371 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
373 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
379 CFAllocatorRef alloc
= __CFPreferencesAllocator();
380 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
381 CFIndex count
= d
? CFDictionaryGetCount(d
) : 0;
382 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFTypeRef
), 0);
383 if (d
) CFDictionaryGetKeysAndValues(d
, keys
, NULL
);
387 result
= CFArrayCreate(alloc
, keys
, count
, &kCFTypeArrayCallBacks
);
389 CFAllocatorDeallocate(alloc
, keys
);
396 /****************************/
397 /* CFPreferencesDomain */
398 /****************************/
400 static CFStringRef
__CFPreferencesDomainCopyDescription(CFTypeRef cf
) {
401 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("<Private CFType %p>\n"), cf
);
404 static void __CFPreferencesDomainDeallocate(CFTypeRef cf
) {
405 const struct __CFPreferencesDomain
*domain
= (struct __CFPreferencesDomain
*)cf
;
406 CFAllocatorRef alloc
= __CFPreferencesAllocator();
407 domain
->_callBacks
->freeDomain(alloc
, domain
->_context
, domain
->_domain
);
408 if (domain
->_context
) CFRelease(domain
->_context
);
411 static CFTypeID __kCFPreferencesDomainTypeID
= _kCFRuntimeNotATypeID
;
413 static const CFRuntimeClass __CFPreferencesDomainClass
= {
415 "CFPreferencesDomain",
418 __CFPreferencesDomainDeallocate
,
422 __CFPreferencesDomainCopyDescription
425 /* This is called once at CFInitialize() time. */
426 __private_extern__
void __CFPreferencesDomainInitialize(void) {
427 __kCFPreferencesDomainTypeID
= _CFRuntimeRegisterClass(&__CFPreferencesDomainClass
);
430 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
431 static CFStringRef
_CFPreferencesCachePrefixForUserHost(CFStringRef userName
, CFStringRef hostName
) {
432 if (userName
== kCFPreferencesAnyUser
&& hostName
== kCFPreferencesAnyHost
) {
433 return (CFStringRef
)CFRetain(CFSTR("*/*/"));
435 CFMutableStringRef result
= CFStringCreateMutable(__CFPreferencesAllocator(), 0);
436 if (userName
== kCFPreferencesCurrentUser
) {
437 userName
= CFGetUserName();
438 CFStringAppend(result
, userName
);
439 CFStringAppend(result
, CFSTR("/"));
440 } else if (userName
== kCFPreferencesAnyUser
) {
441 CFStringAppend(result
, CFSTR("*/"));
443 if (hostName
== kCFPreferencesCurrentHost
) {
444 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
445 CFStringAppend(result
, hostID
);
446 CFStringAppend(result
, CFSTR("/"));
447 } else if (hostName
== kCFPreferencesAnyHost
) {
448 CFStringAppend(result
, CFSTR("*/"));
453 // 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
454 static CFStringRef
_CFPreferencesStandardDomainCacheKey(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
455 CFStringRef prefix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
456 CFStringRef result
= NULL
;
459 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@%@"), prefix
, domainName
);
465 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
466 CFURLRef theURL
= NULL
;
467 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
468 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
469 CFURLRef prefDir
= _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, safeLevel
);
471 CFStringRef fileName
;
472 Boolean mustFreeAppName
= false;
474 if (!prefDir
) return NULL
;
475 if (domainName
== kCFPreferencesAnyApplication
) {
476 appName
= CFSTR(".GlobalPreferences");
477 } else if (domainName
== kCFPreferencesCurrentApplication
) {
478 CFBundleRef mainBundle
= CFBundleGetMainBundle();
479 appName
= mainBundle
? CFBundleGetIdentifier(mainBundle
) : NULL
;
480 if (!appName
|| CFStringGetLength(appName
) == 0) {
481 appName
= _CFProcessNameString();
484 appName
= domainName
;
486 if (userName
!= kCFPreferencesAnyUser
) {
487 if (hostName
== kCFPreferencesAnyHost
) {
488 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
489 } else if (hostName
== kCFPreferencesCurrentHost
) {
490 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
491 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostID
);
493 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostName
); // sketchy - this allows someone to set an arbitrary hostname.
496 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
498 if (mustFreeAppName
) {
502 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
503 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLPOSIXPathStyle
, false, prefDir
);
504 #elif DEPLOYMENT_TARGET_WINDOWS
505 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLWindowsPathStyle
, false, prefDir
);
507 if (prefDir
) CFRelease(prefDir
);
511 //#error Do not know where to store NSUserDefaults on this platform
516 static CFURLRef
_CFPreferencesURLForStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
517 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName
, userName
, hostName
, __CFSafeLaunchLevel
);
520 CFPreferencesDomainRef
_CFPreferencesStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
521 CFPreferencesDomainRef domain
;
522 CFStringRef domainKey
;
523 Boolean shouldReleaseDomain
= true;
524 domainKey
= _CFPreferencesStandardDomainCacheKey(domainName
, userName
, hostName
);
525 __CFSpinLock(&domainCacheLock
);
527 CFAllocatorRef alloc
= __CFPreferencesAllocator();
528 domainCache
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
530 domain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
531 __CFSpinUnlock(&domainCacheLock
);
533 // Domain's not in the cache; load from permanent storage
534 CFURLRef theURL
= _CFPreferencesURLForStandardDomain(domainName
, userName
, hostName
);
536 domain
= _CFPreferencesDomainCreate(theURL
, &__kCFXMLPropertyListDomainCallBacks
);
538 if (userName
== kCFPreferencesAnyUser
) {
539 _CFPreferencesDomainSetIsWorldReadable(domain
, true);
543 __CFSpinLock(&domainCacheLock
);
544 if (domain
&& domainCache
) {
545 // 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.
546 CFPreferencesDomainRef checkDomain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
548 // 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.
549 // checkDomain was retrieved with a Get, so we don't want to over-release.
550 shouldReleaseDomain
= false;
551 CFRelease(domain
); // release the domain we synthesized earlier.
552 domain
= checkDomain
; // repoint it at the domain picked up out of the cache.
554 // We must not have found the domain in the cache, so it's ok for us to put this in.
555 CFDictionarySetValue(domainCache
, domainKey
, domain
);
557 if(shouldReleaseDomain
) CFRelease(domain
);
559 __CFSpinUnlock(&domainCacheLock
);
561 CFRelease(domainKey
);
565 static void __CFPreferencesPerformSynchronize(const void *key
, const void *value
, void *context
) {
566 CFPreferencesDomainRef domain
= (CFPreferencesDomainRef
)value
;
567 Boolean
*cumulativeResult
= (Boolean
*)context
;
568 if (!_CFPreferencesDomainSynchronize(domain
)) *cumulativeResult
= false;
571 __private_extern__ Boolean
_CFSynchronizeDomainCache(void) {
572 Boolean result
= true;
573 __CFSpinLock(&domainCacheLock
);
575 CFDictionaryApplyFunction(domainCache
, __CFPreferencesPerformSynchronize
, &result
);
577 __CFSpinUnlock(&domainCacheLock
);
581 __private_extern__
void _CFPreferencesPurgeDomainCache(void) {
582 _CFSynchronizeDomainCache();
583 __CFSpinLock(&domainCacheLock
);
585 CFRelease(domainCache
);
588 __CFSpinUnlock(&domainCacheLock
);
591 __private_extern__ CFArrayRef
_CFPreferencesCreateDomainList(CFStringRef userName
, CFStringRef hostName
) {
592 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
594 CFMutableArrayRef marray
;
595 CFStringRef
*cachedDomainKeys
;
596 CFPreferencesDomainRef
*cachedDomains
;
600 CFURLRef prefDir
= _preferencesDirectoryForUserHost(userName
, hostName
);
605 if (hostName
== kCFPreferencesAnyHost
) {
606 suffix
= CFStringCreateWithCString(prefAlloc
, ".plist", kCFStringEncodingASCII
);
607 } else if (hostName
== kCFPreferencesCurrentHost
) {
608 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
609 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostID
);
611 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostName
); // sketchy - this allows someone to create a domain list for an arbitrary hostname.
613 suffixLen
= CFStringGetLength(suffix
);
615 domains
= (CFArrayRef
)CFURLCreatePropertyFromResource(prefAlloc
, prefDir
, kCFURLFileDirectoryContents
, NULL
);
618 marray
= CFArrayCreateMutableCopy(prefAlloc
, 0, domains
);
621 marray
= CFArrayCreateMutable(prefAlloc
, 0, & kCFTypeArrayCallBacks
);
623 for (idx
= CFArrayGetCount(marray
)-1; idx
>= 0; idx
--) {
624 CFURLRef url
= (CFURLRef
)CFArrayGetValueAtIndex(marray
, idx
);
625 CFStringRef string
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
626 if (!CFStringHasSuffix(string
, suffix
)) {
627 CFArrayRemoveValueAtIndex(marray
, idx
);
629 CFStringRef dom
= CFStringCreateWithSubstring(prefAlloc
, string
, CFRangeMake(0, CFStringGetLength(string
) - suffixLen
));
630 if (CFEqual(dom
, CFSTR(".GlobalPreferences"))) {
631 CFArraySetValueAtIndex(marray
, idx
, kCFPreferencesAnyApplication
);
633 CFArraySetValueAtIndex(marray
, idx
, dom
);
641 // Now add any domains added in the cache; delete any that have been deleted in the cache
642 __CFSpinLock(&domainCacheLock
);
644 __CFSpinUnlock(&domainCacheLock
);
647 cnt
= CFDictionaryGetCount(domainCache
);
648 cachedDomainKeys
= (CFStringRef
*)CFAllocatorAllocate(prefAlloc
, 2 * cnt
* sizeof(CFStringRef
), 0);
649 cachedDomains
= (CFPreferencesDomainRef
*)(cachedDomainKeys
+ cnt
);
650 CFDictionaryGetKeysAndValues(domainCache
, (const void **)cachedDomainKeys
, (const void **)cachedDomains
);
651 __CFSpinUnlock(&domainCacheLock
);
652 suffix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
653 suffixLen
= CFStringGetLength(suffix
);
655 for (idx
= 0; idx
< cnt
; idx
++) {
656 CFStringRef domainKey
= cachedDomainKeys
[idx
];
657 CFPreferencesDomainRef domain
= cachedDomains
[idx
];
658 CFStringRef domainName
;
659 CFIndex keyCount
= 0;
661 if (!CFStringHasPrefix(domainKey
, suffix
)) continue;
662 domainName
= CFStringCreateWithSubstring(prefAlloc
, domainKey
, CFRangeMake(suffixLen
, CFStringGetLength(domainKey
) - suffixLen
));
663 if (CFEqual(domainName
, CFSTR("*"))) {
664 CFRelease(domainName
);
665 domainName
= (CFStringRef
)CFRetain(kCFPreferencesAnyApplication
);
666 } else if (CFEqual(domainName
, kCFPreferencesCurrentApplication
)) {
667 CFRelease(domainName
);
668 domainName
= (CFStringRef
)CFRetain(_CFProcessNameString());
670 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
671 keyCount
= d
? CFDictionaryGetCount(d
) : 0;
672 if (keyCount
) CFRelease(d
);
674 // Domain was deleted
675 SInt32 firstIndexOfValue
= CFArrayGetFirstIndexOfValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
);
676 if (0 <= firstIndexOfValue
) {
677 CFArrayRemoveValueAtIndex(marray
, firstIndexOfValue
);
679 } else if (!CFArrayContainsValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
)) {
680 CFArrayAppendValue(marray
, domainName
);
682 CFRelease(domainName
);
685 CFAllocatorDeallocate(prefAlloc
, cachedDomainKeys
);
690 // CFPreferencesDomain functions
693 CFPreferencesDomainRef
_CFPreferencesDomainCreate(CFTypeRef context
, const _CFPreferencesDomainCallBacks
*callBacks
) {
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
);
699 newDomain
->_callBacks
= callBacks
;
700 if (context
) CFRetain(context
);
701 newDomain
->_context
= context
;
702 newDomain
->_domain
= callBacks
->createDomain(alloc
, context
);
707 CFTypeRef
_CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain
, CFStringRef key
) {
708 return domain
->_callBacks
->fetchValue(domain
->_context
, domain
->_domain
, key
);
711 void _CFPreferencesDomainSet(CFPreferencesDomainRef domain
, CFStringRef key
, CFTypeRef value
) {
712 domain
->_callBacks
->writeValue(domain
->_context
, domain
->_domain
, key
, value
);
715 __private_extern__ Boolean
_CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain
) {
716 return domain
->_callBacks
->synchronize(domain
->_context
, domain
->_domain
);
719 __private_extern__
void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain
, Boolean isWorldReadable
) {
720 if (domain
->_callBacks
->setIsWorldReadable
) {
721 domain
->_callBacks
->setIsWorldReadable(domain
->_context
, domain
->_domain
, isWorldReadable
);
725 __private_extern__
void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain
) {
726 return domain
->_callBacks
->copyDomainDictionary
;
729 void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain
, CFDictionaryRef dict
) {
730 CFAllocatorRef alloc
= __CFPreferencesAllocator();
731 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
732 CFIndex idx
, count
= d
? CFDictionaryGetCount(d
) : 0;
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
);
739 CFAllocatorDeallocate(alloc
, keys
);
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
));
749 CFAllocatorDeallocate(alloc
, newKeys
);
753 CFDictionaryRef
_CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain
) {
754 CFDictionaryRef result
= domain
->_callBacks
->copyDomainDictionary(domain
->_context
, domain
->_domain
);
755 if(result
&& CFDictionaryGetCount(result
) == 0) {
762 Boolean
_CFPreferencesDomainExists(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
763 CFPreferencesDomainRef domain
;
764 domain
= _CFPreferencesStandardDomain(domainName
, userName
, hostName
);
766 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
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
);
779 static void freeVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *domain
) {
780 CFRelease((CFTypeRef
)domain
);
783 static CFTypeRef
fetchVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
) {
784 CFTypeRef result
= CFDictionaryGetValue((CFMutableDictionaryRef
)domain
, key
);
785 if (result
) CFRetain(result
);
789 static void writeVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
, CFTypeRef value
) {
791 CFDictionarySetValue((CFMutableDictionaryRef
)domain
, key
, value
);
793 CFDictionaryRemoveValue((CFMutableDictionaryRef
)domain
, key
);
796 static Boolean
synchronizeVolatileDomain(CFTypeRef context
, void *domain
) {
800 static void getVolatileKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
801 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)domain
;
802 CFIndex count
= CFDictionaryGetCount(dict
);
806 if ( count
< *numKeyValuePairs
) {
807 values
= *buf
+ count
;
808 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
809 } else if (alloc
!= kCFAllocatorNull
) {
811 *buf
= (void **)CFAllocatorReallocate(alloc
, *buf
, count
* 2 * sizeof(void *), 0);
813 *buf
= (void **)CFAllocatorAllocate(alloc
, count
*2*sizeof(void *), 0);
816 values
= *buf
+ count
;
817 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
821 *numKeyValuePairs
= count
;
824 static CFDictionaryRef
copyVolatileDomainDictionary(CFTypeRef context
, void *volatileDomain
) {
825 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)volatileDomain
;
827 CFDictionaryRef result
= (CFDictionaryRef
)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict
, kCFPropertyListImmutable
);
831 const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks
= {createVolatileDomain
, freeVolatileDomain
, fetchVolatileValue
, writeVolatileValue
, synchronizeVolatileDomain
, getVolatileKeysAndValues
, copyVolatileDomainDictionary
, NULL
};