2 * Copyright (c) 2010 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-2009, Apple Inc. All rights reserved.
26 Responsibility: Chris Parker
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 #include <CoreFoundation/CFBundle.h>
36 #include <CoreFoundation/CFNumber.h>
37 #include <CoreFoundation/CFPriv.h>
38 #include <CoreFoundation/CFPriv.h>
39 #include "CFInternal.h"
41 #if DEPLOYMENT_TARGET_MACOSX
43 #include <CoreFoundation/CFUUID.h>
46 #if DEBUG_PREFERENCES_MEMORY
47 #include "../Tests/CFCountingAllocator.c"
50 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
);
52 struct __CFPreferencesDomain
{
54 /* WARNING - not copying the callbacks; we know they are always static structs */
55 const _CFPreferencesDomainCallBacks
*_callBacks
;
60 CONST_STRING_DECL(kCFPreferencesAnyApplication
, "kCFPreferencesAnyApplication")
61 CONST_STRING_DECL(kCFPreferencesAnyHost
, "kCFPreferencesAnyHost")
62 CONST_STRING_DECL(kCFPreferencesAnyUser
, "kCFPreferencesAnyUser")
63 CONST_STRING_DECL(kCFPreferencesCurrentApplication
, "kCFPreferencesCurrentApplication")
64 CONST_STRING_DECL(kCFPreferencesCurrentHost
, "kCFPreferencesCurrentHost")
65 CONST_STRING_DECL(kCFPreferencesCurrentUser
, "kCFPreferencesCurrentUser")
68 static CFAllocatorRef _preferencesAllocator
= NULL
;
69 __private_extern__ CFAllocatorRef
__CFPreferencesAllocator(void) {
70 if (!_preferencesAllocator
) {
71 #if DEBUG_PREFERENCES_MEMORY
72 _preferencesAllocator
= CFCountingAllocatorCreate(NULL
);
74 _preferencesAllocator
= __CFGetDefaultAllocator();
75 CFRetain(_preferencesAllocator
);
78 return _preferencesAllocator
;
81 // declaration for telling the
82 void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef
);
84 #if DEBUG_PREFERENCES_MEMORY
85 #warning Preferences debugging on
86 CF_EXPORT
void CFPreferencesDumpMem(void) {
87 if (_preferencesAllocator
) {
88 // CFCountingAllocatorPrintSummary(_preferencesAllocator);
89 CFCountingAllocatorPrintPointers(_preferencesAllocator
);
91 // CFCountingAllocatorReset(_preferencesAllocator);
95 #if DEPLOYMENT_TARGET_MACOSX
97 #pragma mark Determining host UUID
100 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
101 // The entry point is in libSystem.B.dylib, but not actually declared
102 // If this becomes available in a header (<rdar://problem/4943036>), I need to pull this out
103 int gethostuuid(unsigned char *uuid_buf
, const struct timespec
*timeoutp
);
105 __private_extern__ CFStringRef
_CFGetHostUUIDString(void) {
106 static CFStringRef __hostUUIDString
= NULL
;
108 if (!__hostUUIDString
) {
109 CFUUIDBytes uuidBytes
;
111 struct timespec timeout
= {0, 0}; // Infinite timeout for gethostuuid()
113 getuuidErr
= gethostuuid((unsigned char *)&uuidBytes
, &timeout
);
114 if (getuuidErr
== -1) {
115 // An error has occurred trying to get the host UUID string. There's nothing we can do here, so we should just return NULL.
116 CFLog(kCFLogLevelWarning
, CFSTR("_CFGetHostUUIDString: unable to determine UUID for host. Error: %d"), errno
);
120 CFUUIDRef uuidRef
= CFUUIDCreateFromUUIDBytes(kCFAllocatorSystemDefault
, uuidBytes
);
121 CFStringRef uuidAsString
= CFUUIDCreateString(kCFAllocatorSystemDefault
, uuidRef
);
123 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)uuidAsString
, (void *)&__hostUUIDString
)) {
124 CFRelease(uuidAsString
); // someone else made the assignment, so just release the extra string.
130 return __hostUUIDString
;
133 __private_extern__ CFStringRef
_CFPreferencesGetByHostIdentifierString(void) {
134 static CFStringRef __byHostIdentifierString
= NULL
;
136 if (!__byHostIdentifierString
) {
137 CFStringRef hostID
= _CFGetHostUUIDString();
139 if (CFStringHasPrefix(hostID
, CFSTR("00000000-0000-1000-8000-"))) {
140 // 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.
141 CFStringRef lastField
= CFStringCreateWithSubstring(kCFAllocatorSystemDefault
, hostID
, CFRangeMake(24, 12));
142 CFMutableStringRef tmpstr
= CFStringCreateMutableCopy(kCFAllocatorSystemDefault
, 0, lastField
);
143 CFStringLowercase(tmpstr
, NULL
);
144 CFStringRef downcasedField
= CFStringCreateCopy(kCFAllocatorSystemDefault
, tmpstr
);
146 if (!OSAtomicCompareAndSwapPtrBarrier(NULL
, (void *)downcasedField
, (void *)&__byHostIdentifierString
)) {
147 CFRelease(downcasedField
);
151 CFRelease(lastField
);
153 // 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.
154 __byHostIdentifierString
= hostID
;
157 __byHostIdentifierString
= CFSTR("UnknownHostID");
161 return __byHostIdentifierString
;
166 __private_extern__ CFStringRef
_CFPreferencesGetByHostIdentifierString(void) {
173 static unsigned long __CFSafeLaunchLevel
= 0;
175 #if DEPLOYMENT_TARGET_WINDOWS
176 #include <shfolder.h>
180 static CFURLRef
_preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
181 CFAllocatorRef alloc
= __CFPreferencesAllocator();
182 #if DEPLOYMENT_TARGET_WINDOWS
186 CFMutableStringRef completePath
= _CFCreateApplicationRepositoryPath(alloc
, CSIDL_APPDATA
);
188 // append "Preferences\" and make the CFURL
189 CFStringAppend(completePath
, CFSTR("Preferences\\"));
190 url
= CFURLCreateWithFileSystemPath(alloc
, completePath
, kCFURLWindowsPathStyle
, true);
191 CFRelease(completePath
);
195 // Can't find a better place? Home directory then?
197 url
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
202 CFURLRef home
= NULL
;
205 // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
206 if (userName
== kCFPreferencesAnyUser
) {
207 if (!home
) home
= CFURLCreateWithFileSystemPath(alloc
, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle
, true);
209 if (hostName
== kCFPreferencesCurrentHost
) url
= home
;
211 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Network/"), kCFURLPOSIXPathStyle
, true, home
);
216 home
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
218 url
= (safeLevel
> 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle
, true, home
) :
219 CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle
, true, home
);
222 if (hostName
!= kCFPreferencesAnyHost
) {
224 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("ByHost/"), kCFURLPOSIXPathStyle
, true, home
);
236 static CFURLRef
_preferencesDirectoryForUserHost(CFStringRef userName
, CFStringRef hostName
) {
237 return _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, __CFSafeLaunchLevel
);
240 static Boolean __CFPreferencesWritesXML
= true;
242 Boolean
__CFPreferencesShouldWriteXML(void) {
243 return __CFPreferencesWritesXML
;
246 static CFSpinLock_t domainCacheLock
= CFSpinLockInit
;
247 static CFMutableDictionaryRef domainCache
= NULL
; // mutable
251 CFTypeRef
CFPreferencesCopyValue(CFStringRef key
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
252 CFPreferencesDomainRef domain
;
253 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
254 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
256 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
258 return _CFPreferencesDomainCreateValueForKey(domain
, key
);
264 CFDictionaryRef
CFPreferencesCopyMultiple(CFArrayRef keysToFetch
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
265 CFPreferencesDomainRef domain
;
266 CFMutableDictionaryRef result
;
269 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
270 __CFGenericValidateType(appName
, CFStringGetTypeID());
271 __CFGenericValidateType(user
, CFStringGetTypeID());
272 __CFGenericValidateType(host
, CFStringGetTypeID());
274 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
275 if (!domain
) return NULL
;
277 return _CFPreferencesDomainDeepCopyDictionary(domain
);
279 __CFGenericValidateType(keysToFetch
, CFArrayGetTypeID());
280 count
= CFArrayGetCount(keysToFetch
);
281 result
= CFDictionaryCreateMutable(CFGetAllocator(domain
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
282 if (!result
) return NULL
;
283 for (idx
= 0; idx
< count
; idx
++) {
284 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keysToFetch
, idx
);
285 CFPropertyListRef value
;
286 __CFGenericValidateType(key
, CFStringGetTypeID());
287 value
= _CFPreferencesDomainCreateValueForKey(domain
, key
);
289 CFDictionarySetValue(result
, key
, value
);
297 void CFPreferencesSetValue(CFStringRef key
, CFTypeRef value
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
298 CFPreferencesDomainRef domain
;
299 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
300 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
302 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
304 _CFPreferencesDomainSet(domain
, key
, value
);
305 _CFApplicationPreferencesDomainHasChanged(domain
);
310 void CFPreferencesSetMultiple(CFDictionaryRef keysToSet
, CFArrayRef keysToRemove
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
311 CFPreferencesDomainRef domain
;
313 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
314 if (keysToSet
) __CFGenericValidateType(keysToSet
, CFDictionaryGetTypeID());
315 if (keysToRemove
) __CFGenericValidateType(keysToRemove
, CFArrayGetTypeID());
316 __CFGenericValidateType(appName
, CFStringGetTypeID());
317 __CFGenericValidateType(user
, CFStringGetTypeID());
318 __CFGenericValidateType(host
, CFStringGetTypeID());
320 CFTypeRef
*keys
= NULL
;
322 CFIndex numOfKeysToSet
= 0;
324 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
327 CFAllocatorRef alloc
= CFGetAllocator(domain
);
329 if (keysToSet
&& (count
= CFDictionaryGetCount(keysToSet
))) {
330 numOfKeysToSet
= count
;
331 keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, 2*count
*sizeof(CFTypeRef
), 0);
333 values
= &(keys
[count
]);
334 CFDictionaryGetKeysAndValues(keysToSet
, keys
, values
);
335 for (idx
= 0; idx
< count
; idx
++) {
336 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], values
[idx
]);
340 if (keysToRemove
&& (count
= CFArrayGetCount(keysToRemove
))) {
341 for (idx
= 0; idx
< count
; idx
++) {
342 CFStringRef removedKey
= (CFStringRef
)CFArrayGetValueAtIndex(keysToRemove
, idx
);
343 _CFPreferencesDomainSet(domain
, removedKey
, NULL
);
348 _CFApplicationPreferencesDomainHasChanged(domain
);
350 if(keys
) CFAllocatorDeallocate(alloc
, keys
);
353 Boolean
CFPreferencesSynchronize(CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
354 CFPreferencesDomainRef domain
;
355 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
357 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
358 if(domain
) _CFApplicationPreferencesDomainHasChanged(domain
);
360 return domain
? _CFPreferencesDomainSynchronize(domain
) : false;
363 CFArrayRef
CFPreferencesCopyApplicationList(CFStringRef user
, CFStringRef host
) {
365 CFAssert1(user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__
);
366 array
= _CFPreferencesCreateDomainList(user
, host
);
370 CFArrayRef
CFPreferencesCopyKeyList(CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
371 CFPreferencesDomainRef domain
;
372 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
374 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
380 CFAllocatorRef alloc
= __CFPreferencesAllocator();
381 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
382 CFIndex count
= d
? CFDictionaryGetCount(d
) : 0;
383 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFTypeRef
), 0);
384 if (d
) CFDictionaryGetKeysAndValues(d
, keys
, NULL
);
388 result
= CFArrayCreate(alloc
, keys
, count
, &kCFTypeArrayCallBacks
);
390 CFAllocatorDeallocate(alloc
, keys
);
397 /****************************/
398 /* CFPreferencesDomain */
399 /****************************/
401 static CFStringRef
__CFPreferencesDomainCopyDescription(CFTypeRef cf
) {
402 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("<Private CFType %p>\n"), cf
);
405 static void __CFPreferencesDomainDeallocate(CFTypeRef cf
) {
406 const struct __CFPreferencesDomain
*domain
= (struct __CFPreferencesDomain
*)cf
;
407 CFAllocatorRef alloc
= __CFPreferencesAllocator();
408 domain
->_callBacks
->freeDomain(alloc
, domain
->_context
, domain
->_domain
);
409 if (domain
->_context
) CFRelease(domain
->_context
);
412 static CFTypeID __kCFPreferencesDomainTypeID
= _kCFRuntimeNotATypeID
;
414 static const CFRuntimeClass __CFPreferencesDomainClass
= {
416 "CFPreferencesDomain",
419 __CFPreferencesDomainDeallocate
,
423 __CFPreferencesDomainCopyDescription
426 /* This is called once at CFInitialize() time. */
427 __private_extern__
void __CFPreferencesDomainInitialize(void) {
428 __kCFPreferencesDomainTypeID
= _CFRuntimeRegisterClass(&__CFPreferencesDomainClass
);
431 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
432 static CFStringRef
_CFPreferencesCachePrefixForUserHost(CFStringRef userName
, CFStringRef hostName
) {
433 if (userName
== kCFPreferencesAnyUser
&& hostName
== kCFPreferencesAnyHost
) {
434 return (CFStringRef
)CFRetain(CFSTR("*/*/"));
436 CFMutableStringRef result
= CFStringCreateMutable(__CFPreferencesAllocator(), 0);
437 if (userName
== kCFPreferencesCurrentUser
) {
438 userName
= CFGetUserName();
439 CFStringAppend(result
, userName
);
440 CFStringAppend(result
, CFSTR("/"));
441 } else if (userName
== kCFPreferencesAnyUser
) {
442 CFStringAppend(result
, CFSTR("*/"));
444 if (hostName
== kCFPreferencesCurrentHost
) {
445 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
446 CFStringAppend(result
, hostID
);
447 CFStringAppend(result
, CFSTR("/"));
448 } else if (hostName
== kCFPreferencesAnyHost
) {
449 CFStringAppend(result
, CFSTR("*/"));
454 // 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
455 static CFStringRef
_CFPreferencesStandardDomainCacheKey(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
456 CFStringRef prefix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
457 CFStringRef result
= NULL
;
460 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@%@"), prefix
, domainName
);
466 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
467 CFURLRef theURL
= NULL
;
468 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
469 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
470 CFURLRef prefDir
= _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, safeLevel
);
472 CFStringRef fileName
;
473 Boolean mustFreeAppName
= false;
475 if (!prefDir
) return NULL
;
476 if (domainName
== kCFPreferencesAnyApplication
) {
477 appName
= CFSTR(".GlobalPreferences");
478 } else if (domainName
== kCFPreferencesCurrentApplication
) {
479 CFBundleRef mainBundle
= CFBundleGetMainBundle();
480 appName
= mainBundle
? CFBundleGetIdentifier(mainBundle
) : NULL
;
481 if (!appName
|| CFStringGetLength(appName
) == 0) {
482 appName
= _CFProcessNameString();
485 appName
= domainName
;
487 if (userName
!= kCFPreferencesAnyUser
) {
488 if (hostName
== kCFPreferencesAnyHost
) {
489 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
490 } else if (hostName
== kCFPreferencesCurrentHost
) {
491 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
492 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostID
);
494 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostName
); // sketchy - this allows someone to set an arbitrary hostname.
497 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
499 if (mustFreeAppName
) {
503 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
504 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLPOSIXPathStyle
, false, prefDir
);
505 #elif DEPLOYMENT_TARGET_WINDOWS
506 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLWindowsPathStyle
, false, prefDir
);
508 if (prefDir
) CFRelease(prefDir
);
512 //#error Do not know where to store NSUserDefaults on this platform
517 static CFURLRef
_CFPreferencesURLForStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
518 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName
, userName
, hostName
, __CFSafeLaunchLevel
);
521 CFPreferencesDomainRef
_CFPreferencesStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
522 CFPreferencesDomainRef domain
;
523 CFStringRef domainKey
;
524 Boolean shouldReleaseDomain
= true;
525 domainKey
= _CFPreferencesStandardDomainCacheKey(domainName
, userName
, hostName
);
526 __CFSpinLock(&domainCacheLock
);
528 CFAllocatorRef alloc
= __CFPreferencesAllocator();
529 domainCache
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
531 domain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
532 __CFSpinUnlock(&domainCacheLock
);
534 // Domain's not in the cache; load from permanent storage
535 CFURLRef theURL
= _CFPreferencesURLForStandardDomain(domainName
, userName
, hostName
);
537 domain
= _CFPreferencesDomainCreate(theURL
, &__kCFXMLPropertyListDomainCallBacks
);
539 if (userName
== kCFPreferencesAnyUser
) {
540 _CFPreferencesDomainSetIsWorldReadable(domain
, true);
544 __CFSpinLock(&domainCacheLock
);
545 if (domain
&& domainCache
) {
546 // 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.
547 CFPreferencesDomainRef checkDomain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
549 // 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.
550 // checkDomain was retrieved with a Get, so we don't want to over-release.
551 shouldReleaseDomain
= false;
552 CFRelease(domain
); // release the domain we synthesized earlier.
553 domain
= checkDomain
; // repoint it at the domain picked up out of the cache.
555 // We must not have found the domain in the cache, so it's ok for us to put this in.
556 CFDictionarySetValue(domainCache
, domainKey
, domain
);
558 if(shouldReleaseDomain
) CFRelease(domain
);
560 __CFSpinUnlock(&domainCacheLock
);
562 CFRelease(domainKey
);
566 static void __CFPreferencesPerformSynchronize(const void *key
, const void *value
, void *context
) {
567 CFPreferencesDomainRef domain
= (CFPreferencesDomainRef
)value
;
568 Boolean
*cumulativeResult
= (Boolean
*)context
;
569 if (!_CFPreferencesDomainSynchronize(domain
)) *cumulativeResult
= false;
572 __private_extern__ Boolean
_CFSynchronizeDomainCache(void) {
573 Boolean result
= true;
574 __CFSpinLock(&domainCacheLock
);
576 CFDictionaryApplyFunction(domainCache
, __CFPreferencesPerformSynchronize
, &result
);
578 __CFSpinUnlock(&domainCacheLock
);
582 __private_extern__
void _CFPreferencesPurgeDomainCache(void) {
583 _CFSynchronizeDomainCache();
584 __CFSpinLock(&domainCacheLock
);
586 CFRelease(domainCache
);
589 __CFSpinUnlock(&domainCacheLock
);
592 __private_extern__ CFArrayRef
_CFPreferencesCreateDomainList(CFStringRef userName
, CFStringRef hostName
) {
593 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
595 CFMutableArrayRef marray
;
596 CFStringRef
*cachedDomainKeys
;
597 CFPreferencesDomainRef
*cachedDomains
;
601 CFURLRef prefDir
= _preferencesDirectoryForUserHost(userName
, hostName
);
606 if (hostName
== kCFPreferencesAnyHost
) {
607 suffix
= CFStringCreateWithCString(prefAlloc
, ".plist", kCFStringEncodingASCII
);
608 } else if (hostName
== kCFPreferencesCurrentHost
) {
609 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
610 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostID
);
612 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostName
); // sketchy - this allows someone to create a domain list for an arbitrary hostname.
614 suffixLen
= CFStringGetLength(suffix
);
616 domains
= (CFArrayRef
)CFURLCreatePropertyFromResource(prefAlloc
, prefDir
, kCFURLFileDirectoryContents
, NULL
);
619 marray
= CFArrayCreateMutableCopy(prefAlloc
, 0, domains
);
622 marray
= CFArrayCreateMutable(prefAlloc
, 0, & kCFTypeArrayCallBacks
);
624 for (idx
= CFArrayGetCount(marray
)-1; idx
>= 0; idx
--) {
625 CFURLRef url
= (CFURLRef
)CFArrayGetValueAtIndex(marray
, idx
);
626 CFStringRef string
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
627 if (!CFStringHasSuffix(string
, suffix
)) {
628 CFArrayRemoveValueAtIndex(marray
, idx
);
630 CFStringRef dom
= CFStringCreateWithSubstring(prefAlloc
, string
, CFRangeMake(0, CFStringGetLength(string
) - suffixLen
));
631 if (CFEqual(dom
, CFSTR(".GlobalPreferences"))) {
632 CFArraySetValueAtIndex(marray
, idx
, kCFPreferencesAnyApplication
);
634 CFArraySetValueAtIndex(marray
, idx
, dom
);
642 // Now add any domains added in the cache; delete any that have been deleted in the cache
643 __CFSpinLock(&domainCacheLock
);
645 __CFSpinUnlock(&domainCacheLock
);
648 cnt
= CFDictionaryGetCount(domainCache
);
649 cachedDomainKeys
= (CFStringRef
*)CFAllocatorAllocate(prefAlloc
, 2 * cnt
* sizeof(CFStringRef
), 0);
650 cachedDomains
= (CFPreferencesDomainRef
*)(cachedDomainKeys
+ cnt
);
651 CFDictionaryGetKeysAndValues(domainCache
, (const void **)cachedDomainKeys
, (const void **)cachedDomains
);
652 __CFSpinUnlock(&domainCacheLock
);
653 suffix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
654 suffixLen
= CFStringGetLength(suffix
);
656 for (idx
= 0; idx
< cnt
; idx
++) {
657 CFStringRef domainKey
= cachedDomainKeys
[idx
];
658 CFPreferencesDomainRef domain
= cachedDomains
[idx
];
659 CFStringRef domainName
;
660 CFIndex keyCount
= 0;
662 if (!CFStringHasPrefix(domainKey
, suffix
)) continue;
663 domainName
= CFStringCreateWithSubstring(prefAlloc
, domainKey
, CFRangeMake(suffixLen
, CFStringGetLength(domainKey
) - suffixLen
));
664 if (CFEqual(domainName
, CFSTR("*"))) {
665 CFRelease(domainName
);
666 domainName
= (CFStringRef
)CFRetain(kCFPreferencesAnyApplication
);
667 } else if (CFEqual(domainName
, kCFPreferencesCurrentApplication
)) {
668 CFRelease(domainName
);
669 domainName
= (CFStringRef
)CFRetain(_CFProcessNameString());
671 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
672 keyCount
= d
? CFDictionaryGetCount(d
) : 0;
673 if (keyCount
) CFRelease(d
);
675 // Domain was deleted
676 SInt32 firstIndexOfValue
= CFArrayGetFirstIndexOfValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
);
677 if (0 <= firstIndexOfValue
) {
678 CFArrayRemoveValueAtIndex(marray
, firstIndexOfValue
);
680 } else if (!CFArrayContainsValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
)) {
681 CFArrayAppendValue(marray
, domainName
);
683 CFRelease(domainName
);
686 CFAllocatorDeallocate(prefAlloc
, cachedDomainKeys
);
691 // CFPreferencesDomain functions
694 CFPreferencesDomainRef
_CFPreferencesDomainCreate(CFTypeRef context
, const _CFPreferencesDomainCallBacks
*callBacks
) {
695 CFAllocatorRef alloc
= __CFPreferencesAllocator();
696 CFPreferencesDomainRef newDomain
;
697 CFAssert(callBacks
!= NULL
&& callBacks
->createDomain
!= NULL
&& callBacks
->freeDomain
!= NULL
&& callBacks
->fetchValue
!= NULL
&& callBacks
->writeValue
!= NULL
, __kCFLogAssertion
, "Cannot create a domain with NULL callbacks");
698 newDomain
= (CFPreferencesDomainRef
)_CFRuntimeCreateInstance(alloc
, __kCFPreferencesDomainTypeID
, sizeof(struct __CFPreferencesDomain
) - sizeof(CFRuntimeBase
), NULL
);
700 newDomain
->_callBacks
= callBacks
;
701 if (context
) CFRetain(context
);
702 newDomain
->_context
= context
;
703 newDomain
->_domain
= callBacks
->createDomain(alloc
, context
);
708 CFTypeRef
_CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain
, CFStringRef key
) {
709 return domain
->_callBacks
->fetchValue(domain
->_context
, domain
->_domain
, key
);
712 void _CFPreferencesDomainSet(CFPreferencesDomainRef domain
, CFStringRef key
, CFTypeRef value
) {
713 domain
->_callBacks
->writeValue(domain
->_context
, domain
->_domain
, key
, value
);
716 __private_extern__ Boolean
_CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain
) {
717 return domain
->_callBacks
->synchronize(domain
->_context
, domain
->_domain
);
720 __private_extern__
void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain
, Boolean isWorldReadable
) {
721 if (domain
->_callBacks
->setIsWorldReadable
) {
722 domain
->_callBacks
->setIsWorldReadable(domain
->_context
, domain
->_domain
, isWorldReadable
);
726 __private_extern__
void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain
) {
727 return domain
->_callBacks
->copyDomainDictionary
;
730 void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain
, CFDictionaryRef dict
) {
731 CFAllocatorRef alloc
= __CFPreferencesAllocator();
732 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
733 CFIndex idx
, count
= d
? CFDictionaryGetCount(d
) : 0;
735 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFTypeRef
), 0);
736 if (d
) CFDictionaryGetKeysAndValues(d
, keys
, NULL
);
737 for (idx
= 0; idx
< count
; idx
++) {
738 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], NULL
);
740 CFAllocatorDeallocate(alloc
, keys
);
743 if (dict
&& (count
= CFDictionaryGetCount(dict
)) != 0) {
744 CFStringRef
*newKeys
= (CFStringRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFStringRef
), 0);
745 CFDictionaryGetKeysAndValues(dict
, (const void **)newKeys
, NULL
);
746 for (idx
= 0; idx
< count
; idx
++) {
747 CFStringRef key
= newKeys
[idx
];
748 _CFPreferencesDomainSet(domain
, key
, (CFTypeRef
)CFDictionaryGetValue(dict
, key
));
750 CFAllocatorDeallocate(alloc
, newKeys
);
754 CFDictionaryRef
_CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain
) {
755 CFDictionaryRef result
= domain
->_callBacks
->copyDomainDictionary(domain
->_context
, domain
->_domain
);
756 if(result
&& CFDictionaryGetCount(result
) == 0) {
763 Boolean
_CFPreferencesDomainExists(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
764 CFPreferencesDomainRef domain
;
765 domain
= _CFPreferencesStandardDomain(domainName
, userName
, hostName
);
767 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
775 /* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
776 static void *createVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
) {
777 return CFDictionaryCreateMutable(allocator
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
780 static void freeVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *domain
) {
781 CFRelease((CFTypeRef
)domain
);
784 static CFTypeRef
fetchVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
) {
785 CFTypeRef result
= CFDictionaryGetValue((CFMutableDictionaryRef
)domain
, key
);
786 if (result
) CFRetain(result
);
790 static void writeVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
, CFTypeRef value
) {
792 CFDictionarySetValue((CFMutableDictionaryRef
)domain
, key
, value
);
794 CFDictionaryRemoveValue((CFMutableDictionaryRef
)domain
, key
);
797 static Boolean
synchronizeVolatileDomain(CFTypeRef context
, void *domain
) {
801 static void getVolatileKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
802 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)domain
;
803 CFIndex count
= CFDictionaryGetCount(dict
);
807 if ( count
< *numKeyValuePairs
) {
808 values
= *buf
+ count
;
809 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
810 } else if (alloc
!= kCFAllocatorNull
) {
812 *buf
= (void **)CFAllocatorReallocate(alloc
, *buf
, count
* 2 * sizeof(void *), 0);
814 *buf
= (void **)CFAllocatorAllocate(alloc
, count
*2*sizeof(void *), 0);
817 values
= *buf
+ count
;
818 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
822 *numKeyValuePairs
= count
;
825 static CFDictionaryRef
copyVolatileDomainDictionary(CFTypeRef context
, void *volatileDomain
) {
826 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)volatileDomain
;
828 CFDictionaryRef result
= (CFDictionaryRef
)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict
, kCFPropertyListImmutable
);
832 const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks
= {createVolatileDomain
, freeVolatileDomain
, fetchVolatileValue
, writeVolatileValue
, synchronizeVolatileDomain
, getVolatileKeysAndValues
, copyVolatileDomainDictionary
, NULL
};