2 * Copyright (c) 2008 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 1998-2002, Apple, Inc. All rights reserved.
25 Responsibility: Chris Parker
28 #include <CoreFoundation/CFPreferences.h>
29 #include <CoreFoundation/CFURLAccess.h>
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>
38 #include "CFInternal.h"
40 #if DEPLOYMENT_TARGET_MACOSX
44 #if DEBUG_PREFERENCES_MEMORY
45 #include "../Tests/CFCountingAllocator.c"
48 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
);
50 struct __CFPreferencesDomain
{
52 /* WARNING - not copying the callbacks; we know they are always static structs */
53 const _CFPreferencesDomainCallBacks
*_callBacks
;
58 CONST_STRING_DECL(kCFPreferencesAnyApplication
, "kCFPreferencesAnyApplication")
59 CONST_STRING_DECL(kCFPreferencesAnyHost
, "kCFPreferencesAnyHost")
60 CONST_STRING_DECL(kCFPreferencesAnyUser
, "kCFPreferencesAnyUser")
61 CONST_STRING_DECL(kCFPreferencesCurrentApplication
, "kCFPreferencesCurrentApplication")
62 CONST_STRING_DECL(kCFPreferencesCurrentHost
, "kCFPreferencesCurrentHost")
63 CONST_STRING_DECL(kCFPreferencesCurrentUser
, "kCFPreferencesCurrentUser")
66 static CFAllocatorRef _preferencesAllocator
= NULL
;
67 __private_extern__ CFAllocatorRef
__CFPreferencesAllocator(void) {
68 if (!_preferencesAllocator
) {
69 #if DEBUG_PREFERENCES_MEMORY
70 _preferencesAllocator
= CFCountingAllocatorCreate(NULL
);
72 _preferencesAllocator
= __CFGetDefaultAllocator();
73 CFRetain(_preferencesAllocator
);
76 return _preferencesAllocator
;
79 // declaration for telling the
80 void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef
);
82 #if DEBUG_PREFERENCES_MEMORY
83 #warning Preferences debugging on
84 CF_EXPORT
void CFPreferencesDumpMem(void) {
85 if (_preferencesAllocator
) {
86 // CFCountingAllocatorPrintSummary(_preferencesAllocator);
87 CFCountingAllocatorPrintPointers(_preferencesAllocator
);
89 // CFCountingAllocatorReset(_preferencesAllocator);
93 #if DEPLOYMENT_TARGET_MACOSX
95 #pragma mark Determining host UUID
99 __private_extern__ CFStringRef
_CFPreferencesGetByHostIdentifierString(void) {
105 static unsigned long __CFSafeLaunchLevel
= 0;
108 #include <shfolder.h>
110 CF_INLINE CFIndex
strlen_UniChar(const UniChar
* p
) {
119 static CFURLRef
_preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
120 CFAllocatorRef alloc
= __CFPreferencesAllocator();
125 UniChar szPath
[MAX_PATH
];
126 if (S_OK
== SHGetFolderPathW(NULL
, CSIDL_LOCAL_APPDATA
, NULL
, 0, (LPWSTR
) szPath
)) {
127 CFStringRef directoryPath
= CFStringCreateWithCharacters(alloc
, szPath
, strlen_UniChar(szPath
));
129 CFStringRef completePath
= CFStringCreateWithFormat(alloc
, NULL
, CFSTR("%@\\Apple\\"), directoryPath
);
131 url
= CFURLCreateWithFileSystemPath(alloc
, completePath
, kCFURLWindowsPathStyle
, true);
132 CFRelease(completePath
);
134 CFRelease(directoryPath
);
138 // Can't find a better place? Home directory then?
140 url
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
145 CFURLRef home
= NULL
;
148 // if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
149 if (userName
== kCFPreferencesAnyUser
) {
150 if (!home
) home
= CFURLCreateWithFileSystemPath(alloc
, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle
, true);
152 if (hostName
== kCFPreferencesCurrentHost
) url
= home
;
154 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Network/"), kCFURLPOSIXPathStyle
, true, home
);
159 home
= CFCopyHomeDirectoryURLForUser((userName
== kCFPreferencesCurrentUser
) ? NULL
: userName
);
161 url
= (safeLevel
> 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle
, true, home
) :
162 CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle
, true, home
);
165 if (hostName
!= kCFPreferencesAnyHost
) {
167 url
= CFURLCreateWithFileSystemPathRelativeToBase(alloc
, CFSTR("ByHost/"), kCFURLPOSIXPathStyle
, true, home
);
179 static CFURLRef
_preferencesDirectoryForUserHost(CFStringRef userName
, CFStringRef hostName
) {
180 return _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, __CFSafeLaunchLevel
);
183 static Boolean __CFPreferencesWritesXML
= true;
185 Boolean
__CFPreferencesShouldWriteXML(void) {
186 return __CFPreferencesWritesXML
;
189 static CFSpinLock_t domainCacheLock
= CFSpinLockInit
;
190 static CFMutableDictionaryRef domainCache
= NULL
; // mutable
194 CFTypeRef
CFPreferencesCopyValue(CFStringRef key
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
195 CFPreferencesDomainRef domain
;
196 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
197 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
199 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
201 return _CFPreferencesDomainCreateValueForKey(domain
, key
);
207 CFDictionaryRef
CFPreferencesCopyMultiple(CFArrayRef keysToFetch
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
208 CFPreferencesDomainRef domain
;
209 CFMutableDictionaryRef result
;
212 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
213 __CFGenericValidateType(appName
, CFStringGetTypeID());
214 __CFGenericValidateType(user
, CFStringGetTypeID());
215 __CFGenericValidateType(host
, CFStringGetTypeID());
217 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
218 if (!domain
) return NULL
;
220 return _CFPreferencesDomainDeepCopyDictionary(domain
);
222 __CFGenericValidateType(keysToFetch
, CFArrayGetTypeID());
223 count
= CFArrayGetCount(keysToFetch
);
224 result
= CFDictionaryCreateMutable(CFGetAllocator(domain
), count
, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
225 if (!result
) return NULL
;
226 for (idx
= 0; idx
< count
; idx
++) {
227 CFStringRef key
= (CFStringRef
)CFArrayGetValueAtIndex(keysToFetch
, idx
);
228 CFPropertyListRef value
;
229 __CFGenericValidateType(key
, CFStringGetTypeID());
230 value
= _CFPreferencesDomainCreateValueForKey(domain
, key
);
232 CFDictionarySetValue(result
, key
, value
);
240 void CFPreferencesSetValue(CFStringRef key
, CFTypeRef value
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
241 CFPreferencesDomainRef domain
;
242 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
243 CFAssert1(key
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__
);
245 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
247 _CFPreferencesDomainSet(domain
, key
, value
);
248 _CFApplicationPreferencesDomainHasChanged(domain
);
253 void CFPreferencesSetMultiple(CFDictionaryRef keysToSet
, CFArrayRef keysToRemove
, CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
254 CFPreferencesDomainRef domain
;
256 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
257 if (keysToSet
) __CFGenericValidateType(keysToSet
, CFDictionaryGetTypeID());
258 if (keysToRemove
) __CFGenericValidateType(keysToRemove
, CFArrayGetTypeID());
259 __CFGenericValidateType(appName
, CFStringGetTypeID());
260 __CFGenericValidateType(user
, CFStringGetTypeID());
261 __CFGenericValidateType(host
, CFStringGetTypeID());
263 CFTypeRef
*keys
= NULL
;
265 CFIndex numOfKeysToSet
= 0;
267 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
270 CFAllocatorRef alloc
= CFGetAllocator(domain
);
272 if (keysToSet
&& (count
= CFDictionaryGetCount(keysToSet
))) {
273 numOfKeysToSet
= count
;
274 keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, 2*count
*sizeof(CFTypeRef
), 0);
276 values
= &(keys
[count
]);
277 CFDictionaryGetKeysAndValues(keysToSet
, keys
, values
);
278 for (idx
= 0; idx
< count
; idx
++) {
279 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], values
[idx
]);
283 if (keysToRemove
&& (count
= CFArrayGetCount(keysToRemove
))) {
284 for (idx
= 0; idx
< count
; idx
++) {
285 CFStringRef removedKey
= (CFStringRef
)CFArrayGetValueAtIndex(keysToRemove
, idx
);
286 _CFPreferencesDomainSet(domain
, removedKey
, NULL
);
291 _CFApplicationPreferencesDomainHasChanged(domain
);
293 if(keys
) CFAllocatorDeallocate(alloc
, keys
);
296 Boolean
CFPreferencesSynchronize(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__
);
300 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
301 if(domain
) _CFApplicationPreferencesDomainHasChanged(domain
);
303 return domain
? _CFPreferencesDomainSynchronize(domain
) : false;
306 CFArrayRef
CFPreferencesCopyApplicationList(CFStringRef user
, CFStringRef host
) {
308 CFAssert1(user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__
);
309 array
= _CFPreferencesCreateDomainList(user
, host
);
313 CFArrayRef
CFPreferencesCopyKeyList(CFStringRef appName
, CFStringRef user
, CFStringRef host
) {
314 CFPreferencesDomainRef domain
;
315 CFAssert1(appName
!= NULL
&& user
!= NULL
&& host
!= NULL
, __kCFLogAssertion
, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__
);
317 domain
= _CFPreferencesStandardDomain(appName
, user
, host
);
323 CFAllocatorRef alloc
= __CFPreferencesAllocator();
324 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
325 CFIndex count
= d
? CFDictionaryGetCount(d
) : 0;
326 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFTypeRef
), 0);
327 if (d
) CFDictionaryGetKeysAndValues(d
, keys
, NULL
);
331 result
= CFArrayCreate(alloc
, keys
, count
, &kCFTypeArrayCallBacks
);
333 CFAllocatorDeallocate(alloc
, keys
);
340 /****************************/
341 /* CFPreferencesDomain */
342 /****************************/
344 static CFStringRef
__CFPreferencesDomainCopyDescription(CFTypeRef cf
) {
345 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("<Private CFType %p>\n"), cf
);
348 static void __CFPreferencesDomainDeallocate(CFTypeRef cf
) {
349 const struct __CFPreferencesDomain
*domain
= (struct __CFPreferencesDomain
*)cf
;
350 CFAllocatorRef alloc
= __CFPreferencesAllocator();
351 domain
->_callBacks
->freeDomain(alloc
, domain
->_context
, domain
->_domain
);
352 if (domain
->_context
) CFRelease(domain
->_context
);
355 static CFTypeID __kCFPreferencesDomainTypeID
= _kCFRuntimeNotATypeID
;
357 static const CFRuntimeClass __CFPreferencesDomainClass
= {
359 "CFPreferencesDomain",
362 __CFPreferencesDomainDeallocate
,
366 __CFPreferencesDomainCopyDescription
369 /* This is called once at CFInitialize() time. */
370 __private_extern__
void __CFPreferencesDomainInitialize(void) {
371 __kCFPreferencesDomainTypeID
= _CFRuntimeRegisterClass(&__CFPreferencesDomainClass
);
374 /* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
375 static CFStringRef
_CFPreferencesCachePrefixForUserHost(CFStringRef userName
, CFStringRef hostName
) {
376 if (userName
== kCFPreferencesAnyUser
&& hostName
== kCFPreferencesAnyHost
) {
377 return (CFStringRef
)CFRetain(CFSTR("*/*/"));
379 CFMutableStringRef result
= CFStringCreateMutable(__CFPreferencesAllocator(), 0);
380 if (userName
== kCFPreferencesCurrentUser
) {
381 userName
= CFGetUserName();
382 CFStringAppend(result
, userName
);
383 CFStringAppend(result
, CFSTR("/"));
384 } else if (userName
== kCFPreferencesAnyUser
) {
385 CFStringAppend(result
, CFSTR("*/"));
387 if (hostName
== kCFPreferencesCurrentHost
) {
388 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
389 CFStringAppend(result
, hostID
);
390 CFStringAppend(result
, CFSTR("/"));
391 } else if (hostName
== kCFPreferencesAnyHost
) {
392 CFStringAppend(result
, CFSTR("*/"));
397 // 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
398 static CFStringRef
_CFPreferencesStandardDomainCacheKey(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
399 CFStringRef prefix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
400 CFStringRef result
= NULL
;
403 result
= CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL
, CFSTR("%@%@"), prefix
, domainName
);
409 static CFURLRef
_CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
, unsigned long safeLevel
) {
410 CFURLRef theURL
= NULL
;
411 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
412 #if (DEPLOYMENT_TARGET_MACOSX) || defined(__WIN32__)
413 CFURLRef prefDir
= _preferencesDirectoryForUserHostSafetyLevel(userName
, hostName
, safeLevel
);
415 CFStringRef fileName
;
416 Boolean mustFreeAppName
= false;
418 if (!prefDir
) return NULL
;
419 if (domainName
== kCFPreferencesAnyApplication
) {
420 appName
= CFSTR(".GlobalPreferences");
421 } else if (domainName
== kCFPreferencesCurrentApplication
) {
422 CFBundleRef mainBundle
= CFBundleGetMainBundle();
423 appName
= mainBundle
? CFBundleGetIdentifier(mainBundle
) : NULL
;
424 if (!appName
|| CFStringGetLength(appName
) == 0) {
425 appName
= _CFProcessNameString();
428 appName
= domainName
;
430 if (userName
!= kCFPreferencesAnyUser
) {
431 if (hostName
== kCFPreferencesAnyHost
) {
432 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
433 } else if (hostName
== kCFPreferencesCurrentHost
) {
434 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
435 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostID
);
437 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.%@.plist"), appName
, hostName
); // sketchy - this allows someone to set an arbitrary hostname.
440 fileName
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR("%@.plist"), appName
);
442 if (mustFreeAppName
) {
446 theURL
= CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc
, fileName
, kCFURLPOSIXPathStyle
, false, prefDir
);
447 if (prefDir
) CFRelease(prefDir
);
451 //#error Do not know where to store NSUserDefaults on this platform
456 static CFURLRef
_CFPreferencesURLForStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
457 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName
, userName
, hostName
, __CFSafeLaunchLevel
);
460 CFPreferencesDomainRef
_CFPreferencesStandardDomain(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
461 CFPreferencesDomainRef domain
;
462 CFStringRef domainKey
;
463 Boolean shouldReleaseDomain
= true;
464 domainKey
= _CFPreferencesStandardDomainCacheKey(domainName
, userName
, hostName
);
465 __CFSpinLock(&domainCacheLock
);
467 CFAllocatorRef alloc
= __CFPreferencesAllocator();
468 domainCache
= CFDictionaryCreateMutable(alloc
, 0, & kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
470 domain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
471 __CFSpinUnlock(&domainCacheLock
);
473 // Domain's not in the cache; load from permanent storage
474 CFURLRef theURL
= _CFPreferencesURLForStandardDomain(domainName
, userName
, hostName
);
476 domain
= _CFPreferencesDomainCreate(theURL
, &__kCFXMLPropertyListDomainCallBacks
);
478 if (userName
== kCFPreferencesAnyUser
) {
479 _CFPreferencesDomainSetIsWorldReadable(domain
, true);
483 __CFSpinLock(&domainCacheLock
);
484 if (domain
&& domainCache
) {
485 // 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.
486 CFPreferencesDomainRef checkDomain
= (CFPreferencesDomainRef
)CFDictionaryGetValue(domainCache
, domainKey
);
488 // 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.
489 // checkDomain was retrieved with a Get, so we don't want to over-release.
490 shouldReleaseDomain
= false;
491 CFRelease(domain
); // release the domain we synthesized earlier.
492 domain
= checkDomain
; // repoint it at the domain picked up out of the cache.
494 // We must not have found the domain in the cache, so it's ok for us to put this in.
495 CFDictionarySetValue(domainCache
, domainKey
, domain
);
497 if(shouldReleaseDomain
) CFRelease(domain
);
499 __CFSpinUnlock(&domainCacheLock
);
501 CFRelease(domainKey
);
505 static void __CFPreferencesPerformSynchronize(const void *key
, const void *value
, void *context
) {
506 CFPreferencesDomainRef domain
= (CFPreferencesDomainRef
)value
;
507 Boolean
*cumulativeResult
= (Boolean
*)context
;
508 if (!_CFPreferencesDomainSynchronize(domain
)) *cumulativeResult
= false;
511 __private_extern__ Boolean
_CFSynchronizeDomainCache(void) {
512 Boolean result
= true;
513 __CFSpinLock(&domainCacheLock
);
515 CFDictionaryApplyFunction(domainCache
, __CFPreferencesPerformSynchronize
, &result
);
517 __CFSpinUnlock(&domainCacheLock
);
521 __private_extern__
void _CFPreferencesPurgeDomainCache(void) {
522 _CFSynchronizeDomainCache();
523 __CFSpinLock(&domainCacheLock
);
525 CFRelease(domainCache
);
528 __CFSpinUnlock(&domainCacheLock
);
531 __private_extern__ CFArrayRef
_CFPreferencesCreateDomainList(CFStringRef userName
, CFStringRef hostName
) {
532 CFAllocatorRef prefAlloc
= __CFPreferencesAllocator();
534 CFMutableArrayRef marray
;
535 CFStringRef
*cachedDomainKeys
;
536 CFPreferencesDomainRef
*cachedDomains
;
540 CFURLRef prefDir
= _preferencesDirectoryForUserHost(userName
, hostName
);
545 if (hostName
== kCFPreferencesAnyHost
) {
546 suffix
= CFStringCreateWithCString(prefAlloc
, ".plist", kCFStringEncodingASCII
);
547 } else if (hostName
== kCFPreferencesCurrentHost
) {
548 CFStringRef hostID
= _CFPreferencesGetByHostIdentifierString();
549 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostID
);
551 suffix
= CFStringCreateWithFormat(prefAlloc
, NULL
, CFSTR(".%@.plist"), hostName
); // sketchy - this allows someone to create a domain list for an arbitrary hostname.
553 suffixLen
= CFStringGetLength(suffix
);
555 domains
= (CFArrayRef
)CFURLCreatePropertyFromResource(prefAlloc
, prefDir
, kCFURLFileDirectoryContents
, NULL
);
558 marray
= CFArrayCreateMutableCopy(prefAlloc
, 0, domains
);
561 marray
= CFArrayCreateMutable(prefAlloc
, 0, & kCFTypeArrayCallBacks
);
563 for (idx
= CFArrayGetCount(marray
)-1; idx
>= 0; idx
--) {
564 CFURLRef url
= (CFURLRef
)CFArrayGetValueAtIndex(marray
, idx
);
565 CFStringRef string
= CFURLCopyFileSystemPath(url
, kCFURLPOSIXPathStyle
);
566 if (!CFStringHasSuffix(string
, suffix
)) {
567 CFArrayRemoveValueAtIndex(marray
, idx
);
569 CFStringRef dom
= CFStringCreateWithSubstring(prefAlloc
, string
, CFRangeMake(0, CFStringGetLength(string
) - suffixLen
));
570 if (CFEqual(dom
, CFSTR(".GlobalPreferences"))) {
571 CFArraySetValueAtIndex(marray
, idx
, kCFPreferencesAnyApplication
);
573 CFArraySetValueAtIndex(marray
, idx
, dom
);
581 // Now add any domains added in the cache; delete any that have been deleted in the cache
582 __CFSpinLock(&domainCacheLock
);
584 __CFSpinUnlock(&domainCacheLock
);
587 cnt
= CFDictionaryGetCount(domainCache
);
588 cachedDomainKeys
= (CFStringRef
*)CFAllocatorAllocate(prefAlloc
, 2 * cnt
* sizeof(CFStringRef
), 0);
589 cachedDomains
= (CFPreferencesDomainRef
*)(cachedDomainKeys
+ cnt
);
590 CFDictionaryGetKeysAndValues(domainCache
, (const void **)cachedDomainKeys
, (const void **)cachedDomains
);
591 __CFSpinUnlock(&domainCacheLock
);
592 suffix
= _CFPreferencesCachePrefixForUserHost(userName
, hostName
);
593 suffixLen
= CFStringGetLength(suffix
);
595 for (idx
= 0; idx
< cnt
; idx
++) {
596 CFStringRef domainKey
= cachedDomainKeys
[idx
];
597 CFPreferencesDomainRef domain
= cachedDomains
[idx
];
598 CFStringRef domainName
;
599 CFIndex keyCount
= 0;
601 if (!CFStringHasPrefix(domainKey
, suffix
)) continue;
602 domainName
= CFStringCreateWithSubstring(prefAlloc
, domainKey
, CFRangeMake(suffixLen
, CFStringGetLength(domainKey
) - suffixLen
));
603 if (CFEqual(domainName
, CFSTR("*"))) {
604 CFRelease(domainName
);
605 domainName
= (CFStringRef
)CFRetain(kCFPreferencesAnyApplication
);
606 } else if (CFEqual(domainName
, kCFPreferencesCurrentApplication
)) {
607 CFRelease(domainName
);
608 domainName
= (CFStringRef
)CFRetain(_CFProcessNameString());
610 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
611 keyCount
= d
? CFDictionaryGetCount(d
) : 0;
612 if (keyCount
) CFRelease(d
);
614 // Domain was deleted
615 SInt32 firstIndexOfValue
= CFArrayGetFirstIndexOfValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
);
616 if (0 <= firstIndexOfValue
) {
617 CFArrayRemoveValueAtIndex(marray
, firstIndexOfValue
);
619 } else if (!CFArrayContainsValue(marray
, CFRangeMake(0, CFArrayGetCount(marray
)), domainName
)) {
620 CFArrayAppendValue(marray
, domainName
);
622 CFRelease(domainName
);
625 CFAllocatorDeallocate(prefAlloc
, cachedDomainKeys
);
630 // CFPreferencesDomain functions
633 CFPreferencesDomainRef
_CFPreferencesDomainCreate(CFTypeRef context
, const _CFPreferencesDomainCallBacks
*callBacks
) {
634 CFAllocatorRef alloc
= __CFPreferencesAllocator();
635 CFPreferencesDomainRef newDomain
;
636 CFAssert(callBacks
!= NULL
&& callBacks
->createDomain
!= NULL
&& callBacks
->freeDomain
!= NULL
&& callBacks
->fetchValue
!= NULL
&& callBacks
->writeValue
!= NULL
, __kCFLogAssertion
, "Cannot create a domain with NULL callbacks");
637 newDomain
= (CFPreferencesDomainRef
)_CFRuntimeCreateInstance(alloc
, __kCFPreferencesDomainTypeID
, sizeof(struct __CFPreferencesDomain
) - sizeof(CFRuntimeBase
), NULL
);
639 newDomain
->_callBacks
= callBacks
;
640 if (context
) CFRetain(context
);
641 newDomain
->_context
= context
;
642 newDomain
->_domain
= callBacks
->createDomain(alloc
, context
);
647 CFTypeRef
_CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain
, CFStringRef key
) {
648 return domain
->_callBacks
->fetchValue(domain
->_context
, domain
->_domain
, key
);
651 void _CFPreferencesDomainSet(CFPreferencesDomainRef domain
, CFStringRef key
, CFTypeRef value
) {
652 domain
->_callBacks
->writeValue(domain
->_context
, domain
->_domain
, key
, value
);
655 __private_extern__ Boolean
_CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain
) {
656 return domain
->_callBacks
->synchronize(domain
->_context
, domain
->_domain
);
659 __private_extern__
void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain
, Boolean isWorldReadable
) {
660 if (domain
->_callBacks
->setIsWorldReadable
) {
661 domain
->_callBacks
->setIsWorldReadable(domain
->_context
, domain
->_domain
, isWorldReadable
);
665 __private_extern__
void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain
) {
666 return domain
->_callBacks
->copyDomainDictionary
;
669 void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain
, CFDictionaryRef dict
) {
670 CFAllocatorRef alloc
= __CFPreferencesAllocator();
671 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
672 CFIndex idx
, count
= d
? CFDictionaryGetCount(d
) : 0;
674 CFTypeRef
*keys
= (CFTypeRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFTypeRef
), 0);
675 if (d
) CFDictionaryGetKeysAndValues(d
, keys
, NULL
);
676 for (idx
= 0; idx
< count
; idx
++) {
677 _CFPreferencesDomainSet(domain
, (CFStringRef
)keys
[idx
], NULL
);
679 CFAllocatorDeallocate(alloc
, keys
);
682 if (dict
&& (count
= CFDictionaryGetCount(dict
)) != 0) {
683 CFStringRef
*newKeys
= (CFStringRef
*)CFAllocatorAllocate(alloc
, count
* sizeof(CFStringRef
), 0);
684 CFDictionaryGetKeysAndValues(dict
, (const void **)newKeys
, NULL
);
685 for (idx
= 0; idx
< count
; idx
++) {
686 CFStringRef key
= newKeys
[idx
];
687 _CFPreferencesDomainSet(domain
, key
, (CFTypeRef
)CFDictionaryGetValue(dict
, key
));
689 CFAllocatorDeallocate(alloc
, newKeys
);
693 CFDictionaryRef
_CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain
) {
694 CFDictionaryRef result
= domain
->_callBacks
->copyDomainDictionary(domain
->_context
, domain
->_domain
);
695 if(result
&& CFDictionaryGetCount(result
) == 0) {
702 Boolean
_CFPreferencesDomainExists(CFStringRef domainName
, CFStringRef userName
, CFStringRef hostName
) {
703 CFPreferencesDomainRef domain
;
704 domain
= _CFPreferencesStandardDomain(domainName
, userName
, hostName
);
706 CFDictionaryRef d
= _CFPreferencesDomainDeepCopyDictionary(domain
);
714 /* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
715 static void *createVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
) {
716 return CFDictionaryCreateMutable(allocator
, 0, & kCFTypeDictionaryKeyCallBacks
, & kCFTypeDictionaryValueCallBacks
);
719 static void freeVolatileDomain(CFAllocatorRef allocator
, CFTypeRef context
, void *domain
) {
720 CFRelease((CFTypeRef
)domain
);
723 static CFTypeRef
fetchVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
) {
724 CFTypeRef result
= CFDictionaryGetValue((CFMutableDictionaryRef
)domain
, key
);
725 if (result
) CFRetain(result
);
729 static void writeVolatileValue(CFTypeRef context
, void *domain
, CFStringRef key
, CFTypeRef value
) {
731 CFDictionarySetValue((CFMutableDictionaryRef
)domain
, key
, value
);
733 CFDictionaryRemoveValue((CFMutableDictionaryRef
)domain
, key
);
736 static Boolean
synchronizeVolatileDomain(CFTypeRef context
, void *domain
) {
740 static void getVolatileKeysAndValues(CFAllocatorRef alloc
, CFTypeRef context
, void *domain
, void **buf
[], CFIndex
*numKeyValuePairs
) {
741 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)domain
;
742 CFIndex count
= CFDictionaryGetCount(dict
);
746 if ( count
< *numKeyValuePairs
) {
747 values
= *buf
+ count
;
748 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
749 } else if (alloc
!= kCFAllocatorNull
) {
751 *buf
= (void **)CFAllocatorReallocate(alloc
, *buf
, count
* 2 * sizeof(void *), 0);
753 *buf
= (void **)CFAllocatorAllocate(alloc
, count
*2*sizeof(void *), 0);
756 values
= *buf
+ count
;
757 CFDictionaryGetKeysAndValues(dict
, (const void **)*buf
, (const void **)values
);
761 *numKeyValuePairs
= count
;
764 static CFDictionaryRef
copyVolatileDomainDictionary(CFTypeRef context
, void *volatileDomain
) {
765 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)volatileDomain
;
767 CFDictionaryRef result
= (CFDictionaryRef
)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict
, kCFPropertyListImmutable
);
771 const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks
= {createVolatileDomain
, freeVolatileDomain
, fetchVolatileValue
, writeVolatileValue
, synchronizeVolatileDomain
, getVolatileKeysAndValues
, copyVolatileDomainDictionary
, NULL
};