1/*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*	CFApplicationPreferences.c
25	Copyright (c) 1998-2013, Apple Inc. All rights reserved.
26	Responsibility: David Smith
27*/
28
29#include <CoreFoundation/CFPreferences.h>
30#include "CFInternal.h"
31#include <CoreFoundation/CFUniChar.h>
32#include <CoreFoundation/CFNumber.h>
33#include <CoreFoundation/CFString.h>
34#include <CoreFoundation/CFLocale.h>
35#include <CoreFoundation/CFNumberFormatter.h>
36#include <CoreFoundation/CFDateFormatter.h>
37#include <sys/types.h>
38#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
39#include <unistd.h>
40#endif
41
42static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self);
43void _CFPreferencesDomainSetMultiple(CFPreferencesDomainRef domain, CFDictionaryRef dict);
44static void updateDictRep(_CFApplicationPreferences *self);
45static void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList);
46Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain);
47static CFTypeRef _CFApplicationPreferencesCreateValueForKey2(_CFApplicationPreferences *self, CFStringRef defaultName);
48
49// Right now, nothing is getting destroyed pretty much ever.  We probably don't want this to be the case, but it's very tricky - domains live in the cache as well as a given application's preferences, and since they're not CFTypes, there's no reference count.  Also, it's not clear we ever want domains destroyed.  When they're synchronized, they clear their internal state (to force reading from the disk again), so they're not very big.... REW, 12/17/98
50
51CFPropertyListRef CFPreferencesCopyAppValue(CFStringRef key, CFStringRef appName) {
52    _CFApplicationPreferences *standardPrefs;
53    CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__);
54    CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
55
56    standardPrefs = _CFStandardApplicationPreferences(appName);
57    return standardPrefs ? _CFApplicationPreferencesCreateValueForKey2(standardPrefs, key) : NULL;
58}
59
60CF_EXPORT Boolean CFPreferencesAppBooleanValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) {
61    CFPropertyListRef value;
62    Boolean result, valid;
63    CFTypeID typeID = 0;
64    CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__);
65    CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
66
67    if (!keyExistsAndHasValidFormat) {
68        keyExistsAndHasValidFormat = &valid;
69    }
70    value = CFPreferencesCopyAppValue(key, appName);
71    if (!value) {
72        *keyExistsAndHasValidFormat = false;
73        return false;
74    }
75    typeID = CFGetTypeID(value);
76    if (typeID == CFStringGetTypeID()) {
77        if (CFStringCompare((CFStringRef)value, CFSTR("true"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare((CFStringRef)value, CFSTR("YES"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
78            *keyExistsAndHasValidFormat = true;
79            result = true;
80        } else if (CFStringCompare((CFStringRef)value, CFSTR("false"), kCFCompareCaseInsensitive) == kCFCompareEqualTo || CFStringCompare((CFStringRef)value, CFSTR("NO"), kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
81            *keyExistsAndHasValidFormat = true;
82            result = false;
83        } else {
84            *keyExistsAndHasValidFormat = false;
85            result = false;
86        }
87    } else if (typeID == CFNumberGetTypeID()) {
88        if (CFNumberIsFloatType((CFNumberRef)value)) {
89            *keyExistsAndHasValidFormat = false;
90            result = false;
91        } else {
92            int i;
93            *keyExistsAndHasValidFormat = true;
94            CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &i);
95            result = (i == 0) ? false : true;
96        }
97    } else if (typeID == CFBooleanGetTypeID()) {
98        result = (value == kCFBooleanTrue);
99        *keyExistsAndHasValidFormat = true;
100    } else {
101        // Unknown type
102        result = false;
103        *keyExistsAndHasValidFormat = false;
104    }
105    CFRelease(value);
106    return result;
107}
108
109CF_PRIVATE CFIndex CFPreferencesAppIntegerValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) {
110    CFPropertyListRef value;
111    CFIndex result;
112    CFTypeID typeID = 0;
113    Boolean valid;
114    CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__);
115    CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
116
117    value = CFPreferencesCopyAppValue(key, appName);
118    if (!keyExistsAndHasValidFormat) {
119        keyExistsAndHasValidFormat = &valid;
120    }
121    if (!value) {
122        *keyExistsAndHasValidFormat = false;
123        return 0;
124    }
125    typeID = CFGetTypeID(value);
126    if (typeID == CFStringGetTypeID()) {
127        SInt32 charIndex = 0;
128        SInt32 intVal;
129        CFStringInlineBuffer buf;
130        Boolean success;
131        CFStringInitInlineBuffer((CFStringRef)value, &buf, CFRangeMake(0, CFStringGetLength((CFStringRef)value)));
132        success = __CFStringScanInteger(&buf, NULL, &charIndex, false, &intVal);
133        *keyExistsAndHasValidFormat = (success && charIndex == CFStringGetLength((CFStringRef)value));
134        result = (*keyExistsAndHasValidFormat) ? intVal : 0;
135    } else if (typeID == CFNumberGetTypeID()) {
136        *keyExistsAndHasValidFormat = !CFNumberIsFloatType((CFNumberRef)value);
137        if (*keyExistsAndHasValidFormat) {
138            CFNumberGetValue((CFNumberRef)value, kCFNumberCFIndexType, &result);
139        } else {
140            result = 0;
141        }
142    } else {
143        // Unknown type
144        result = 0;
145        *keyExistsAndHasValidFormat = false;
146    }
147    CFRelease(value);
148    return result;
149}
150
151Boolean CFPreferencesGetAppBooleanValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) {
152    CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__);
153    CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
154    return CFPreferencesAppBooleanValue(key, appName, keyExistsAndHasValidFormat);
155}
156
157CFIndex CFPreferencesGetAppIntegerValue(CFStringRef key, CFStringRef appName, Boolean *keyExistsAndHasValidFormat) {
158    CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__);
159    CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
160    return CFPreferencesAppIntegerValue(key, appName, keyExistsAndHasValidFormat);
161}
162
163void CFPreferencesSetAppValue(CFStringRef key, CFTypeRef value, CFStringRef appName) {
164    _CFApplicationPreferences *standardPrefs;
165    CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__);
166    CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
167
168    standardPrefs = _CFStandardApplicationPreferences(appName);
169    if (standardPrefs) {
170        if (value) _CFApplicationPreferencesSet(standardPrefs, key, value);
171        else _CFApplicationPreferencesRemove(standardPrefs, key);
172    }
173}
174
175
176static CFSpinLock_t __CFApplicationPreferencesLock = CFSpinLockInit; // Locks access to __CFStandardUserPreferences
177static CFMutableDictionaryRef __CFStandardUserPreferences = NULL; // Mutable dictionary; keys are app names, values are _CFApplicationPreferences
178
179Boolean CFPreferencesAppSynchronize(CFStringRef appName) {
180    _CFApplicationPreferences *standardPrefs;
181    Boolean result;
182    CFAssert1(appName != NULL, __kCFLogAssertion, "%s(): Cannot access application preferences with a NULL application name", __PRETTY_FUNCTION__);
183
184    // Do not call _CFStandardApplicationPreferences(), as we do not want to create the preferences only to synchronize
185    __CFSpinLock(&__CFApplicationPreferencesLock);
186    if (__CFStandardUserPreferences)  {
187        standardPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName);
188    } else {
189        standardPrefs = NULL;
190    }
191
192    result = standardPrefs ? _CFApplicationPreferencesSynchronizeNoLock(standardPrefs) : _CFSynchronizeDomainCache();
193    __CFSpinUnlock(&__CFApplicationPreferencesLock);
194    return result;
195}
196
197void CFPreferencesFlushCaches(void) {
198    CFAllocatorRef alloc = __CFPreferencesAllocator();
199    __CFSpinLock(&__CFApplicationPreferencesLock);
200    if (__CFStandardUserPreferences)  {
201        _CFApplicationPreferences **prefsArray, *prefsBuf[32];
202        CFIndex idx, count = CFDictionaryGetCount(__CFStandardUserPreferences);
203        if (count < 32) {
204            prefsArray = prefsBuf;
205        } else {
206            prefsArray = (_CFApplicationPreferences **)CFAllocatorAllocate(alloc, count * sizeof(_CFApplicationPreferences *), 0);
207        }
208        CFDictionaryGetKeysAndValues(__CFStandardUserPreferences, NULL, (const void **)prefsArray);
209
210        __CFSpinUnlock(&__CFApplicationPreferencesLock);
211        // DeallocateApplicationPreferences needs the lock
212        for (idx = 0; idx < count; idx ++) {
213            _CFApplicationPreferences *appPrefs = prefsArray[idx];
214            _CFApplicationPreferencesSynchronize(appPrefs);
215            _CFDeallocateApplicationPreferences(appPrefs);
216        }
217        __CFSpinLock(&__CFApplicationPreferencesLock);
218
219        CFRelease(__CFStandardUserPreferences);
220        __CFStandardUserPreferences = NULL;
221        if(prefsArray != prefsBuf) CFAllocatorDeallocate(alloc, prefsArray);
222    }
223    __CFSpinUnlock(&__CFApplicationPreferencesLock);
224    _CFPreferencesPurgeDomainCache();
225}
226
227// quick message to indicate that the given domain has changed, and we should go through and invalidate any dictReps that involve this domain.
228void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef changedDomain) {
229    CFAllocatorRef alloc = __CFPreferencesAllocator();
230    __CFSpinLock(&__CFApplicationPreferencesLock);
231    if(__CFStandardUserPreferences) {  // only grovel over the prefs if there's something there to grovel
232        _CFApplicationPreferences **prefsArray, *prefsBuf[32];
233        CFIndex idx, count = CFDictionaryGetCount(__CFStandardUserPreferences);
234        if(count < 32) {
235            prefsArray = prefsBuf;
236        } else {
237            prefsArray = (_CFApplicationPreferences **)CFAllocatorAllocate(alloc, count * sizeof(_CFApplicationPreferences *), 0);
238        }
239        CFDictionaryGetKeysAndValues(__CFStandardUserPreferences, NULL, (const void **)prefsArray);
240        // For this operation, giving up the lock is the last thing we want to do, so use the modified flavor of _CFApplicationPreferencesContainsDomain
241        for(idx = 0; idx < count; idx++) {
242            _CFApplicationPreferences *appPrefs = prefsArray[idx];
243            if(_CFApplicationPreferencesContainsDomainNoLock(appPrefs, changedDomain)) {
244                updateDictRep(appPrefs);
245            }
246        }
247        if(prefsArray != prefsBuf) _CFAllocatorDeallocateGC(alloc, prefsArray);
248    }
249    __CFSpinUnlock(&__CFApplicationPreferencesLock);
250}
251
252
253// Begin ported code from NSUserDefaults.m
254
255
256static void updateDictRep(_CFApplicationPreferences *self) {
257    if (self->_dictRep) {
258        CFRelease(self->_dictRep);
259        self->_dictRep = NULL;
260    }
261}
262
263static void __addKeysAndValues(const void *key, const void *value, void *context) {
264    CFDictionarySetValue((CFMutableDictionaryRef)context, key, value);
265}
266
267static CFMutableDictionaryRef computeDictRep(_CFApplicationPreferences *self, Boolean skipC0C0A) {
268    CFAllocatorRef alloc = __CFPreferencesAllocator();
269    CFMutableArrayRef searchList = self->_search;
270    CFIndex idx;
271    CFIndex cnt = CFArrayGetCount(searchList);
272    CFDictionaryRef subdomainDict;
273    CFMutableDictionaryRef dictRep;
274
275    dictRep = CFDictionaryCreateMutable(alloc, 0, &kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
276    _CFDictionarySetCapacity(dictRep, 260);	// avoid lots of rehashing
277
278    // For each subdomain triplet in the domain, iterate over dictionaries, adding them if necessary to the dictRep
279    for (idx = cnt; idx--;) {
280        CFPreferencesDomainRef domain = (CFPreferencesDomainRef)CFArrayGetValueAtIndex(searchList, idx);
281
282        if (!domain) continue;
283
284        subdomainDict = _CFPreferencesDomainDeepCopyDictionary(domain);
285        if (subdomainDict) {
286            CFDictionaryApplyFunction(subdomainDict, __addKeysAndValues, dictRep);
287            CFRelease(subdomainDict);
288        }
289    }
290    return dictRep;
291}
292
293CFTypeRef _CFApplicationPreferencesSearchDownToDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef stopper, CFStringRef key) {
294    return NULL;
295}
296
297
298void _CFApplicationPreferencesUpdate(_CFApplicationPreferences *self) {
299    __CFSpinLock(&__CFApplicationPreferencesLock);
300    updateDictRep(self);
301    __CFSpinUnlock(&__CFApplicationPreferencesLock);
302}
303
304CF_EXPORT CFDictionaryRef _CFApplicationPreferencesCopyRepresentation(_CFApplicationPreferences *self);
305
306CF_PRIVATE CFDictionaryRef __CFApplicationPreferencesCopyCurrentState(void) {
307    _CFApplicationPreferences *self = _CFStandardApplicationPreferences(kCFPreferencesCurrentApplication);
308    CFDictionaryRef result = _CFApplicationPreferencesCopyRepresentation(self);
309    return result;
310}
311
312// CACHING here - we will only return a value as current as the last time computeDictRep() was called
313static CFTypeRef _CFApplicationPreferencesCreateValueForKey2(_CFApplicationPreferences *self, CFStringRef defaultName) {
314    CFTypeRef result;
315    __CFSpinLock(&__CFApplicationPreferencesLock);
316    if (!self->_dictRep) {
317        self->_dictRep = computeDictRep(self, true);
318    }
319    result = (self->_dictRep) ? (CFTypeRef )CFDictionaryGetValue(self->_dictRep, defaultName) : NULL;
320    if (result) {
321        CFRetain(result);
322    }
323    __CFSpinUnlock(&__CFApplicationPreferencesLock);
324    return result;
325}
326
327
328void _CFApplicationPreferencesSet(_CFApplicationPreferences *self, CFStringRef defaultName, CFTypeRef value) {
329    CFPreferencesDomainRef applicationDomain;
330
331    __CFSpinLock(&__CFApplicationPreferencesLock);
332    applicationDomain = _CFPreferencesStandardDomain(self->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
333    if(applicationDomain) {
334        _CFPreferencesDomainSet(applicationDomain, defaultName, value);
335        if (CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), applicationDomain)) {
336            // Expensive; can't we just check the relevant value throughout the appropriate sets of domains? -- REW, 7/19/99
337            updateDictRep(self);
338        }
339    }
340    __CFSpinUnlock(&__CFApplicationPreferencesLock);
341}
342
343void _CFApplicationPreferencesRemove(_CFApplicationPreferences *self, CFStringRef defaultName) {
344    CFPreferencesDomainRef appDomain;
345
346    __CFSpinLock(&__CFApplicationPreferencesLock);
347    appDomain = _CFPreferencesStandardDomain(self->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
348    if(appDomain) {
349        _CFPreferencesDomainSet(appDomain, defaultName, NULL);
350        if (CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), appDomain)) {
351            // If key exists, it will be in the _dictRep (but possibly overridden)
352            updateDictRep(self);
353        }
354    }
355    __CFSpinUnlock(&__CFApplicationPreferencesLock);
356}
357
358static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self) {
359    Boolean success = _CFSynchronizeDomainCache();
360    updateDictRep(self);
361    return success;
362}
363
364Boolean _CFApplicationPreferencesSynchronize(_CFApplicationPreferences *self) {
365    Boolean result;
366    __CFSpinLock(&__CFApplicationPreferencesLock);
367    result = _CFApplicationPreferencesSynchronizeNoLock(self);
368    __CFSpinUnlock(&__CFApplicationPreferencesLock);
369    return result;
370}
371
372// appName should not be kCFPreferencesCurrentApplication going in to this call
373_CFApplicationPreferences *_CFApplicationPreferencesCreateWithUser(CFStringRef userName, CFStringRef appName) {
374    CFAllocatorRef alloc = __CFPreferencesAllocator();
375    _CFApplicationPreferences *self = (_CFApplicationPreferences*)CFAllocatorAllocate(alloc, sizeof(_CFApplicationPreferences), 0);
376    if (self) {
377        self->_dictRep = NULL;
378        self->_appName = (CFStringRef)CFRetain(appName);
379        self->_search = CFArrayCreateMutable(alloc, 0, &kCFTypeArrayCallBacks);
380        if (!self->_search) {
381            CFAllocatorDeallocate(alloc, self);
382            CFRelease(appName);
383            self = NULL;
384        }
385    }
386    return self;
387}
388
389// Do NOT release the domain after adding it to the array; domain_expression should not return a retained object  -- REW, 8/26/99
390#define ADD_DOMAIN(domain_expression) domain = domain_expression; if (domain) {CFArrayAppendValue(search, domain);}
391void _CFApplicationPreferencesSetStandardSearchList(_CFApplicationPreferences *appPreferences) {
392    /* Here is how the domains end up in priority order in a search list.  Only a subset of these are setup by default.
393	argument domain
394	this app, this user, managed
395	this app, any user, managed
396        this app, this user, this host
397        this app, this user, any host (AppDomain)
398	suiteN, this user, this host
399	suiteN, this user, any host
400        ...
401	suite0, this user, this host
402	suite0, this user, any host
403        any app, this user, this host
404        any app, this user, any host (GlobalDomain)
405        NSUserDefaults backwards-compat ICU domain
406        this app, any user, this host
407        this app, any user, any host
408	suiteN, any user, this host
409	suiteN, any user, any host
410        ...
411	suite0, any user, this host
412	suite0, any user, any host
413        any app, any user, this host
414        any app, any user, any host
415	registration domain
416    */
417    CFPreferencesDomainRef domain;
418    CFMutableArrayRef search = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
419    if (!search) {
420        // couldn't allocate memory!
421        return;
422    }
423
424    ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost));
425    ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost));
426    ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost));
427    ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost));
428    ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost));
429    ADD_DOMAIN(_CFPreferencesStandardDomain(appPreferences->_appName, kCFPreferencesAnyUser, kCFPreferencesAnyHost));
430    ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesCurrentHost));
431    ADD_DOMAIN(_CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesAnyUser, kCFPreferencesAnyHost));
432
433    _CFApplicationPreferencesSetSearchList(appPreferences, search);
434    CFRelease(search);
435}
436#undef ADD_DOMAIN
437
438
439CF_PRIVATE _CFApplicationPreferences *_CFStandardApplicationPreferences(CFStringRef appName) {
440    _CFApplicationPreferences *appPreferences;
441//    CFAssert(appName != kCFPreferencesAnyApplication, __kCFLogAssertion, "Cannot use any of the CFPreferences...App... functions with an appName of kCFPreferencesAnyApplication");
442    __CFSpinLock(&__CFApplicationPreferencesLock);
443    if (!__CFStandardUserPreferences)  {
444        __CFStandardUserPreferences = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, & kCFTypeDictionaryKeyCallBacks, NULL);
445    }
446    if (!__CFStandardUserPreferences) {
447        // Couldn't create
448        __CFSpinUnlock(&__CFApplicationPreferencesLock);
449        return NULL;
450    }
451    if ((appPreferences = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName)) == NULL ) {
452        appPreferences = _CFApplicationPreferencesCreateWithUser(kCFPreferencesCurrentUser, appName);
453        CFDictionarySetValue(__CFStandardUserPreferences, appName, appPreferences);
454        __CFSpinUnlock(&__CFApplicationPreferencesLock);
455        _CFApplicationPreferencesSetStandardSearchList(appPreferences);
456    } else {
457        __CFSpinUnlock(&__CFApplicationPreferencesLock);
458    }
459    return appPreferences;
460}
461
462// Exclusively for Foundation's use
463void _CFApplicationPreferencesSetCacheForApp(_CFApplicationPreferences *appPrefs, CFStringRef appName) {
464    __CFSpinLock(&__CFApplicationPreferencesLock);
465    if (!__CFStandardUserPreferences) {
466        __CFStandardUserPreferences = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
467        CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs);
468        __CFSpinUnlock(&__CFApplicationPreferencesLock);
469    } else {
470        _CFApplicationPreferences *oldPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName);
471        CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs);
472        __CFSpinUnlock(&__CFApplicationPreferencesLock);
473        if (oldPrefs) {
474            _CFDeallocateApplicationPreferences(oldPrefs);
475        }
476    }
477}
478
479
480void _CFDeallocateApplicationPreferences(_CFApplicationPreferences *self) {
481    CFAllocatorRef alloc = __CFPreferencesAllocator();
482    _CFApplicationPreferences *cachedPrefs = NULL;
483    __CFSpinLock(&__CFApplicationPreferencesLock);
484
485    // Get us out of the cache before destroying!
486    if (__CFStandardUserPreferences)  {
487        cachedPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, self->_appName);
488    }
489    if (cachedPrefs == self) {
490        CFDictionaryRemoveValue(__CFStandardUserPreferences, self->_appName);
491    }
492
493    if (self->_dictRep) CFRelease(self->_dictRep);
494    CFRelease(self->_search);
495    CFRelease(self->_appName);
496    CFAllocatorDeallocate(alloc, self);
497    __CFSpinUnlock(&__CFApplicationPreferencesLock);
498}
499
500
501CF_EXPORT
502CFDictionaryRef _CFApplicationPreferencesCopyRepresentation(_CFApplicationPreferences *self) {
503    CFDictionaryRef dict;
504    __CFSpinLock(&__CFApplicationPreferencesLock);
505    if (!self->_dictRep) {
506        self->_dictRep = computeDictRep(self, true);
507    }
508    if (self->_dictRep) {
509        CFRetain(self->_dictRep);
510    }
511    dict = self->_dictRep;
512    __CFSpinUnlock(&__CFApplicationPreferencesLock);
513    return dict;
514}
515
516static void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList) {
517    CFIndex idx, count;
518    __CFSpinLock(&__CFApplicationPreferencesLock);
519    CFArrayRemoveAllValues(self->_search);
520    count = CFArrayGetCount(newSearchList);
521    for (idx = 0; idx < count; idx ++) {
522        CFArrayAppendValue(self->_search, CFArrayGetValueAtIndex(newSearchList, idx));
523    }
524    updateDictRep(self);
525    __CFSpinUnlock(&__CFApplicationPreferencesLock);
526}
527
528void CFPreferencesAddSuitePreferencesToApp(CFStringRef appName, CFStringRef suiteName) {
529    _CFApplicationPreferences *appPrefs;
530
531    appPrefs = _CFStandardApplicationPreferences(appName);
532    _CFApplicationPreferencesAddSuitePreferences(appPrefs, suiteName);
533}
534
535void _CFApplicationPreferencesAddSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) {
536    CFPreferencesDomainRef domain;
537    CFIndex idx;
538    CFRange range;
539
540    // Find where to insert the new suite
541    __CFSpinLock(&__CFApplicationPreferencesLock);
542    domain = _CFPreferencesStandardDomain(appPrefs->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
543    range.location = 0;
544    range.length = CFArrayGetCount(appPrefs->_search);
545    idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound;
546    __CFSpinUnlock(&__CFApplicationPreferencesLock);
547    idx ++; // We want just below the app domain.  Coincidentally, this gives us the top of the list if the app domain has been removed.
548    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
549    if (domain) {
550        __CFSpinLock(&__CFApplicationPreferencesLock);
551        CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
552        __CFSpinUnlock(&__CFApplicationPreferencesLock);
553        range.length ++;
554    }
555    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
556    if (domain) {
557        __CFSpinLock(&__CFApplicationPreferencesLock);
558        CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
559        __CFSpinUnlock(&__CFApplicationPreferencesLock);
560        range.length ++;
561    }
562
563    // Now the AnyUser domains
564    domain = _CFPreferencesStandardDomain(appPrefs->_appName, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
565    idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound;
566    if (idx == kCFNotFound) {
567        // Someone blew away the app domain. For the any user case, we look for right below the global domain
568        domain = _CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
569        idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound;
570        if (idx == kCFNotFound) {
571            // Try the "any host" choice
572            domain = _CFPreferencesStandardDomain(kCFPreferencesAnyApplication, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
573            idx = domain ? CFArrayGetFirstIndexOfValue(appPrefs->_search, range, domain) : kCFNotFound;
574            if (idx == kCFNotFound) {
575                // We give up; put the new domains at the bottom
576                idx = CFArrayGetCount(appPrefs->_search) - 1;
577            }
578        }
579    }
580    idx ++;
581    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
582    if (domain) {
583        __CFSpinLock(&__CFApplicationPreferencesLock);
584        CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
585        __CFSpinUnlock(&__CFApplicationPreferencesLock);
586    }
587    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
588    if (domain) {
589        __CFSpinLock(&__CFApplicationPreferencesLock);
590        CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
591        __CFSpinUnlock(&__CFApplicationPreferencesLock);
592    }
593    __CFSpinLock(&__CFApplicationPreferencesLock);
594    updateDictRep(appPrefs);
595    __CFSpinUnlock(&__CFApplicationPreferencesLock);
596}
597
598void CFPreferencesRemoveSuitePreferencesFromApp(CFStringRef appName, CFStringRef suiteName) {
599    _CFApplicationPreferences *appPrefs;
600
601    appPrefs = _CFStandardApplicationPreferences(appName);
602
603    _CFApplicationPreferencesRemoveSuitePreferences(appPrefs, suiteName);
604}
605
606void _CFApplicationPreferencesRemoveSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) {
607    CFPreferencesDomainRef domain;
608
609    __CFSpinLock(&__CFApplicationPreferencesLock);
610    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
611    __CFSpinUnlock(&__CFApplicationPreferencesLock);
612    if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
613
614    __CFSpinLock(&__CFApplicationPreferencesLock);
615    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
616    __CFSpinUnlock(&__CFApplicationPreferencesLock);
617    if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
618
619    __CFSpinLock(&__CFApplicationPreferencesLock);
620    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
621    __CFSpinUnlock(&__CFApplicationPreferencesLock);
622    if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
623
624    __CFSpinLock(&__CFApplicationPreferencesLock);
625    domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
626    __CFSpinUnlock(&__CFApplicationPreferencesLock);
627    if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
628}
629
630void _CFApplicationPreferencesAddDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain, Boolean addAtTop) {
631    __CFSpinLock(&__CFApplicationPreferencesLock);
632    if (addAtTop) {
633        CFArrayInsertValueAtIndex(self->_search, 0, domain);
634    } else {
635        CFArrayAppendValue(self->_search, domain);
636    }
637    updateDictRep(self);
638    __CFSpinUnlock(&__CFApplicationPreferencesLock);
639}
640
641Boolean _CFApplicationPreferencesContainsDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) {
642    Boolean result;
643
644    if (!domain) {
645        return false;
646    }
647
648    __CFSpinLock(&__CFApplicationPreferencesLock);
649    result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain);
650    __CFSpinUnlock(&__CFApplicationPreferencesLock);
651    return result;
652}
653
654Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) {
655    Boolean result;
656    result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain);
657    return result;
658}
659
660void _CFApplicationPreferencesRemoveDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) {
661    CFIndex idx;
662    CFRange range;
663    __CFSpinLock(&__CFApplicationPreferencesLock);
664    range.location = 0;
665    range.length = CFArrayGetCount(self->_search);
666    while ((idx = CFArrayGetFirstIndexOfValue(self->_search, range, domain)) != kCFNotFound) {
667        CFArrayRemoveValueAtIndex(self->_search, idx);
668        range.location = idx;
669        range.length  = range.length - idx - 1;
670    }
671    updateDictRep(self);
672    __CFSpinUnlock(&__CFApplicationPreferencesLock);
673}
674