2 * Copyright (c) 2015 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-2014, 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 CF_PRIVATE 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 CF_PRIVATE 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 CF_PRIVATE 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 CF_PRIVATE 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 CFLock_t domainCacheLock
= CFLockInit
;
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 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
428 static CFStringRef
_CFPreferencesCachePrefixForUserHost(CFStringRef userName
, CFStringRef hostName
) {
429 if (userName
== kCFPreferencesAnyUser
&& hostName
== kCFPreferencesAnyHost
) {
430 return (CFStringRef
)CFRetain(CFSTR("*/*/"));
432 CFMutableStringRef result
= CFStringCreateMutable(__CFPreferencesAllocator(), 0);
433 if (userName
== kCFPreferencesCurrentUser
) {
434 userName
= CFCopyUserName();
435 CFStringAppend(result
, userName
);
437 CFStringAppend(result
, CFSTR("/"));
438 } else if (userName
== kCFPreferencesAnyUser
) {
439 CFStringAppend(result
, CFSTR("*/"));
441 if (hostName
== kCFPreferencesCurrentHost
) {
442 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
443 CFStringAppend(result
, hostID
);
444 CFStringAppend(result
, CFSTR("/"));
445 } else if (hostName
== kCFPreferencesAnyHost
) {
446 CFStringAppend(result
, CFSTR("*/"));
451 // It would be nice if we could remember the key for "well-known" combinations, so we're not constantly allocing more strings.... - REW 2/3/99
452 static CFStringRef
_CFPreferencesStandardDomainCacheKey(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
453 CFStringRef prefix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
454 CFStringRef result
= NULL
;
457 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@%@"), prefix
, domainName
);
463 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
464 CFURLRef theURL
= NULL
;
465 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
466 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
467 CFURLRef prefDir
= _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, safeLevel
);
469 CFStringRef fileName
;
470 Boolean mustFreeAppName
= false;
472 if (!prefDir
) return NULL
;
473 if (domainName
== kCFPreferencesAnyApplication
) {
474 appName
= CFSTR(".GlobalPreferences");
475 } else if (domainName
== kCFPreferencesCurrentApplication
) {
476 CFBundleRef mainBundle
= CFBundleGetMainBundle();
477 appName
= mainBundle
? CFBundleGetIdentifier(mainBundle
) : NULL
;
478 if (!appName
|| CFStringGetLength(appName
) == 0) {
479 appName
= _CFProcessNameString();
482 appName
= domainName
;
484 if (userName
!= kCFPreferencesAnyUser
) {
485 if (hostName
== kCFPreferencesAnyHost
) {
486 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
487 } else if (hostName
== kCFPreferencesCurrentHost
) {
488 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
489 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostID
);
491 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostName
); // sketchy - this allows someone to set an arbitrary hostname.
494 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
496 if (mustFreeAppName
) {
500 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
501 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLPOSIXPathStyle
, false, prefDir
);
502 #elif DEPLOYMENT_TARGET_WINDOWS
503 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLWindowsPathStyle
, false, prefDir
);
505 if (prefDir
) CFRelease(prefDir
);
509 //#error Do not know where to store NSUserDefaults on this platform
514 static CFURLRef
_CFPreferencesURLForStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
515 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName
, userName
, hostName
, __CFSafeLaunchLevel
);
518 CFPreferencesDomainRef
_CFPreferencesStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
519 CFPreferencesDomainRef domain
;
520 CFStringRef domainKey
;
521 Boolean shouldReleaseDomain
= true;
522 domainKey
= _CFPreferencesStandardDomainCacheKey(domainName
, userName
, hostName
);
523 __CFLock(&domainCacheLock
);
525 CFAllocatorRef alloc
= __CFPreferencesAllocator();
526 domainCache
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
528 domain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
529 __CFUnlock(&domainCacheLock
);
531 // Domain's not in the cache; load from permanent storage
532 CFURLRef theURL
= _CFPreferencesURLForStandardDomain(domainName
, userName
, hostName
);
534 domain
= _CFPreferencesDomainCreate(theURL
, &__kCFXMLPropertyListDomainCallBacks
);
536 if (userName
== kCFPreferencesAnyUser
) {
537 _CFPreferencesDomainSetIsWorldReadable(domain
, true);
541 __CFLock(&domainCacheLock
);
542 if (domain
&& domainCache
) {
543 // We've just synthesized a domain & we're about to throw it in the domain cache. The problem is that someone else might have gotten in here behind our backs, so we can't just blindly set the domain (3021920). We'll need to check to see if this happened, and compensate.
544 CFPreferencesDomainRef checkDomain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
546 // Someone got in here ahead of us, so we shouldn't smash the domain we're given. checkDomain is the current version, we should use that.
547 // checkDomain was retrieved with a Get, so we don't want to over-release.
548 shouldReleaseDomain
= false;
549 CFRelease(domain
); // release the domain we synthesized earlier.
550 domain
= checkDomain
; // repoint it at the domain picked up out of the cache.
552 // We must not have found the domain in the cache, so it's ok for us to put this in.
553 CFDictionarySetValue(domainCache
, domainKey
, domain
);
555 if(shouldReleaseDomain
) CFRelease(domain
);
557 __CFUnlock(&domainCacheLock
);
559 CFRelease(domainKey
);
563 static void __CFPreferencesPerformSynchronize(const void *key
, const void *value
, void *context
) {
564 CFPreferencesDomainRef domain
= (CFPreferencesDomainRef
)value
;
565 Boolean
*cumulativeResult
= (Boolean
*)context
;
566 if (!_CFPreferencesDomainSynchronize(domain
)) *cumulativeResult
= false;
569 CF_PRIVATE Boolean
_CFSynchronizeDomainCache(void) {
570 Boolean result
= true;
571 __CFLock(&domainCacheLock
);
573 CFDictionaryApplyFunction(domainCache
, __CFPreferencesPerformSynchronize
, &result
);
575 __CFUnlock(&domainCacheLock
);
579 CF_PRIVATE
void _CFPreferencesPurgeDomainCache(void) {
580 _CFSynchronizeDomainCache();
581 __CFLock(&domainCacheLock
);
583 CFRelease(domainCache
);
586 __CFUnlock(&domainCacheLock
);
589 CF_PRIVATE CFArrayRef
_CFPreferencesCreateDomainList(CFStringRef userName
, CFStringRef hostName
) {
590 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
592 CFMutableArrayRef marray
;
593 CFStringRef
*cachedDomainKeys
;
594 CFPreferencesDomainRef
*cachedDomains
;
598 CFURLRef prefDir
= _preferencesDirectoryForUserHost(userName
, hostName
);
603 if (hostName
== kCFPreferencesAnyHost
) {
604 suffix
= CFStringCreateWithCString(prefAlloc
, ".plist", kCFStringEncodingASCII
);
605 } else if (hostName
== kCFPreferencesCurrentHost
) {
606 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
607 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostID
);
609 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostName
); // sketchy - this allows someone to create a domain list for an arbitrary hostname.
611 suffixLen
= CFStringGetLength(suffix
);
613 domains
= (CFArrayRef
)CFURLCreatePropertyFromResource(prefAlloc
, prefDir
, kCFURLFileDirectoryContents
, NULL
);
616 marray
= CFArrayCreateMutableCopy(prefAlloc
, 0, domains
);
619 marray
= CFArrayCreateMutable(prefAlloc
, 0, & kCFTypeArrayCallBacks
);
621 for (idx
= CFArrayGetCount(marray
)-1; idx
>= 0; idx
--) {
622 CFURLRef url
= (CFURLRef
)CFArrayGetValueAtIndex(marray
, idx
);
623 CFStringRef string
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
624 if (!CFStringHasSuffix(string
, suffix
)) {
625 CFArrayRemoveValueAtIndex(marray
, idx
);
627 CFStringRef dom
= CFStringCreateWithSubstring(prefAlloc
, string
, CFRangeMake(0, CFStringGetLength(string
) - suffixLen
));
628 if (CFEqual(dom
, CFSTR(".GlobalPreferences"))) {
629 CFArraySetValueAtIndex(marray
, idx
, kCFPreferencesAnyApplication
);
631 CFArraySetValueAtIndex(marray
, idx
, dom
);
639 // Now add any domains added in the cache; delete any that have been deleted in the cache
640 __CFLock(&domainCacheLock
);
642 __CFUnlock(&domainCacheLock
);
645 cnt
= CFDictionaryGetCount(domainCache
);
646 cachedDomainKeys
= (CFStringRef
*)CFAllocatorAllocate(prefAlloc
, 2 * cnt
* sizeof(CFStringRef
), 0);
647 cachedDomains
= (CFPreferencesDomainRef
*)(cachedDomainKeys
+ cnt
);
648 CFDictionaryGetKeysAndValues(domainCache
, (const void **)cachedDomainKeys
, (const void **)cachedDomains
);
649 __CFUnlock(&domainCacheLock
);
650 suffix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
651 suffixLen
= CFStringGetLength(suffix
);
653 for (idx
= 0; idx
< cnt
; idx
++) {
654 CFStringRef domainKey
= cachedDomainKeys
[idx
];
655 CFPreferencesDomainRef domain
= cachedDomains
[idx
];
656 CFStringRef domainName
;
657 CFIndex keyCount
= 0;
659 if (!CFStringHasPrefix(domainKey
, suffix
)) continue;
660 domainName
= CFStringCreateWithSubstring(prefAlloc
, domainKey
, CFRangeMake(suffixLen
, CFStringGetLength(domainKey
) - suffixLen
));
661 if (CFEqual(domainName
, CFSTR("*"))) {
662 CFRelease(domainName
);
663 domainName
= (CFStringRef
)CFRetain(kCFPreferencesAnyApplication
);
664 } else if (CFEqual(domainName
, kCFPreferencesCurrentApplication
)) {
665 CFRelease(domainName
);
666 domainName
= (CFStringRef
)CFRetain(_CFProcessNameString());
668 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
669 keyCount
= d
? CFDictionaryGetCount(d
) : 0;
670 if (keyCount
) CFRelease(d
);
672 // Domain was deleted
673 SInt32 firstIndexOfValue
= CFArrayGetFirstIndexOfValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
);
674 if (0 <= firstIndexOfValue
) {
675 CFArrayRemoveValueAtIndex(marray
, firstIndexOfValue
);
677 } else if (!CFArrayContainsValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
)) {
678 CFArrayAppendValue(marray
, domainName
);
680 CFRelease(domainName
);
683 CFAllocatorDeallocate(prefAlloc
, cachedDomainKeys
);
688 // CFPreferencesDomain functions
691 CFPreferencesDomainRef
_CFPreferencesDomainCreate(CFTypeRef context
, const _CFPreferencesDomainCallBacks
*callBacks
) {
692 static dispatch_once_t initOnce
;
693 dispatch_once(&initOnce
, ^{ __kCFPreferencesDomainTypeID
= _CFRuntimeRegisterClass(&__CFPreferencesDomainClass
); });
694 CFAllocatorRef alloc
= __CFPreferencesAllocator();
695 CFPreferencesDomainRef newDomain
;
696 CFAssert(callBacks
!= NULL
&& callBacks
->createDomain
!= NULL
&& callBacks
->freeDomain
!= NULL
&& callBacks
->fetchValue
!= NULL
&& callBacks
->writeValue
!= NULL
, __kCFLogAssertion
, "Cannot create a domain with NULL callbacks");
697 newDomain
= (CFPreferencesDomainRef
)_CFRuntimeCreateInstance(alloc
, __kCFPreferencesDomainTypeID
, sizeof(struct __CFPreferencesDomain
) - sizeof(CFRuntimeBase
), NULL
);
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 CF_PRIVATE Boolean
_CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain
) {
716 return domain
->_callBacks
->synchronize(domain
->_context
, domain
->_domain
);
719 CF_PRIVATE
void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain
, Boolean isWorldReadable
) {
720 if (domain
->_callBacks
->setIsWorldReadable
) {
721 domain
->_callBacks
->setIsWorldReadable(domain
->_context
, domain
->_domain
, isWorldReadable
);
725 CF_PRIVATE
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
};