/ CFApplicationPreferences.c
CFApplicationPreferences.c
  1  /*
  2   * Copyright (c) 2015 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-2014, 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  
 42  static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self);
 43  void _CFPreferencesDomainSetMultiple(CFPreferencesDomainRef domain, CFDictionaryRef dict);
 44  static void updateDictRep(_CFApplicationPreferences *self);
 45  static void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList);
 46  Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain);
 47  static 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  
 51  CFPropertyListRef 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  
 60  CF_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  
109  CF_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  
151  Boolean 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  
157  CFIndex 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  
163  void 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  
176  static CFLock_t __CFApplicationPreferencesLock = CFLockInit; // Locks access to __CFStandardUserPreferences
177  static CFMutableDictionaryRef __CFStandardUserPreferences = NULL; // Mutable dictionary; keys are app names, values are _CFApplicationPreferences 
178  
179  Boolean 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      __CFLock(&__CFApplicationPreferencesLock);
186      if (__CFStandardUserPreferences)  {
187          standardPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName);
188      } else {
189          standardPrefs = NULL;
190      }
191  
192      result = standardPrefs ? _CFApplicationPreferencesSynchronizeNoLock(standardPrefs) : _CFSynchronizeDomainCache();
193      __CFUnlock(&__CFApplicationPreferencesLock);
194      return result;
195  }
196  
197  void CFPreferencesFlushCaches(void) {
198      CFAllocatorRef alloc = __CFPreferencesAllocator();
199      __CFLock(&__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          __CFUnlock(&__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          __CFLock(&__CFApplicationPreferencesLock);
218  
219          CFRelease(__CFStandardUserPreferences);
220          __CFStandardUserPreferences = NULL;
221          if(prefsArray != prefsBuf) CFAllocatorDeallocate(alloc, prefsArray);
222      }
223      __CFUnlock(&__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.
228  void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef changedDomain) {
229      CFAllocatorRef alloc = __CFPreferencesAllocator();
230      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
250  }
251  
252  
253  // Begin ported code from NSUserDefaults.m
254  
255  
256  static void updateDictRep(_CFApplicationPreferences *self) {
257      if (self->_dictRep) {
258          CFRelease(self->_dictRep);
259          self->_dictRep = NULL;
260      }
261  }
262  
263  static void __addKeysAndValues(const void *key, const void *value, void *context) {
264      CFDictionarySetValue((CFMutableDictionaryRef)context, key, value);
265  }
266  
267  static 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  
293  CFTypeRef _CFApplicationPreferencesSearchDownToDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef stopper, CFStringRef key) {
294      return NULL;
295  }
296  
297  
298  void _CFApplicationPreferencesUpdate(_CFApplicationPreferences *self) {
299      __CFLock(&__CFApplicationPreferencesLock);
300      updateDictRep(self);
301      __CFUnlock(&__CFApplicationPreferencesLock);
302  }
303  
304  CF_EXPORT CFDictionaryRef _CFApplicationPreferencesCopyRepresentation(_CFApplicationPreferences *self);
305  
306  CF_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
313  static CFTypeRef _CFApplicationPreferencesCreateValueForKey2(_CFApplicationPreferences *self, CFStringRef defaultName) {
314      CFTypeRef result;
315      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
324      return result;
325  }
326  
327  
328  void _CFApplicationPreferencesSet(_CFApplicationPreferences *self, CFStringRef defaultName, CFTypeRef value) {
329      CFPreferencesDomainRef applicationDomain;
330  
331      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
341  }
342  
343  void _CFApplicationPreferencesRemove(_CFApplicationPreferences *self, CFStringRef defaultName) {
344      CFPreferencesDomainRef appDomain;
345  
346      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
356  }
357  
358  static Boolean _CFApplicationPreferencesSynchronizeNoLock(_CFApplicationPreferences *self) {
359      Boolean success = _CFSynchronizeDomainCache();
360      updateDictRep(self);
361      return success;
362  }
363  
364  Boolean _CFApplicationPreferencesSynchronize(_CFApplicationPreferences *self) {
365      Boolean result;
366      __CFLock(&__CFApplicationPreferencesLock);
367      result = _CFApplicationPreferencesSynchronizeNoLock(self);
368      __CFUnlock(&__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);}
391  void _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  
439  CF_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      __CFLock(&__CFApplicationPreferencesLock);
443      if (!__CFStandardUserPreferences)  {
444          __CFStandardUserPreferences = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, & kCFTypeDictionaryKeyCallBacks, NULL);
445      }
446      if (!__CFStandardUserPreferences) {
447          // Couldn't create
448          __CFUnlock(&__CFApplicationPreferencesLock);
449          return NULL;
450      }
451      if ((appPreferences = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName)) == NULL ) {
452          appPreferences = _CFApplicationPreferencesCreateWithUser(kCFPreferencesCurrentUser, appName);
453          CFDictionarySetValue(__CFStandardUserPreferences, appName, appPreferences);
454          __CFUnlock(&__CFApplicationPreferencesLock);
455          _CFApplicationPreferencesSetStandardSearchList(appPreferences);
456      } else {
457          __CFUnlock(&__CFApplicationPreferencesLock);
458      }
459      return appPreferences;
460  }
461  
462  // Exclusively for Foundation's use
463  void _CFApplicationPreferencesSetCacheForApp(_CFApplicationPreferences *appPrefs, CFStringRef appName) {
464      __CFLock(&__CFApplicationPreferencesLock);
465      if (!__CFStandardUserPreferences) {
466          __CFStandardUserPreferences = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL);
467          CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs);
468          __CFUnlock(&__CFApplicationPreferencesLock);
469      } else {
470          _CFApplicationPreferences *oldPrefs = (_CFApplicationPreferences *)CFDictionaryGetValue(__CFStandardUserPreferences, appName);
471          CFDictionarySetValue(__CFStandardUserPreferences, appName, appPrefs);
472          __CFUnlock(&__CFApplicationPreferencesLock);
473          if (oldPrefs) {
474              _CFDeallocateApplicationPreferences(oldPrefs);
475          }
476      }
477  }
478  
479  
480  void _CFDeallocateApplicationPreferences(_CFApplicationPreferences *self) {
481      CFAllocatorRef alloc = __CFPreferencesAllocator();
482      _CFApplicationPreferences *cachedPrefs = NULL;
483      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
498  }
499  
500  
501  CF_EXPORT
502  CFDictionaryRef _CFApplicationPreferencesCopyRepresentation(_CFApplicationPreferences *self) {
503      CFDictionaryRef dict;
504      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
513      return dict;
514  }
515  
516  static void _CFApplicationPreferencesSetSearchList(_CFApplicationPreferences *self, CFArrayRef newSearchList) {
517      CFIndex idx, count;
518      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
526  }
527  
528  void CFPreferencesAddSuitePreferencesToApp(CFStringRef appName, CFStringRef suiteName) {
529      _CFApplicationPreferences *appPrefs;
530      
531      appPrefs = _CFStandardApplicationPreferences(appName);
532      _CFApplicationPreferencesAddSuitePreferences(appPrefs, suiteName);
533  }
534  
535  void _CFApplicationPreferencesAddSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) {
536      CFPreferencesDomainRef domain; 
537      CFIndex idx;
538      CFRange range;
539  
540      // Find where to insert the new suite
541      __CFLock(&__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      __CFUnlock(&__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          __CFLock(&__CFApplicationPreferencesLock);
551          CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
552          __CFUnlock(&__CFApplicationPreferencesLock);
553          range.length ++;
554      }
555      domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
556      if (domain) {
557          __CFLock(&__CFApplicationPreferencesLock);
558          CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
559          __CFUnlock(&__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          __CFLock(&__CFApplicationPreferencesLock);
584          CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
585          __CFUnlock(&__CFApplicationPreferencesLock);
586      }
587      domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
588      if (domain) {
589          __CFLock(&__CFApplicationPreferencesLock);
590          CFArrayInsertValueAtIndex(appPrefs->_search, idx, domain);
591          __CFUnlock(&__CFApplicationPreferencesLock);
592      }
593      __CFLock(&__CFApplicationPreferencesLock);
594      updateDictRep(appPrefs);
595      __CFUnlock(&__CFApplicationPreferencesLock);
596  }
597  
598  void CFPreferencesRemoveSuitePreferencesFromApp(CFStringRef appName, CFStringRef suiteName) {
599      _CFApplicationPreferences *appPrefs;
600  
601      appPrefs = _CFStandardApplicationPreferences(appName);
602      
603      _CFApplicationPreferencesRemoveSuitePreferences(appPrefs, suiteName);
604  }
605  
606  void _CFApplicationPreferencesRemoveSuitePreferences(_CFApplicationPreferences *appPrefs, CFStringRef suiteName) {
607      CFPreferencesDomainRef domain;
608  
609      __CFLock(&__CFApplicationPreferencesLock);
610      domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
611      __CFUnlock(&__CFApplicationPreferencesLock);
612      if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
613  
614      __CFLock(&__CFApplicationPreferencesLock);
615      domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
616      __CFUnlock(&__CFApplicationPreferencesLock);
617      if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
618  
619      __CFLock(&__CFApplicationPreferencesLock);
620      domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
621      __CFUnlock(&__CFApplicationPreferencesLock);
622      if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
623  
624      __CFLock(&__CFApplicationPreferencesLock);
625      domain = _CFPreferencesStandardDomain(suiteName, kCFPreferencesAnyUser, kCFPreferencesCurrentHost);
626      __CFUnlock(&__CFApplicationPreferencesLock);
627      if (domain) _CFApplicationPreferencesRemoveDomain(appPrefs, domain);
628  }
629  
630  void _CFApplicationPreferencesAddDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain, Boolean addAtTop) {
631      __CFLock(&__CFApplicationPreferencesLock);
632      if (addAtTop) {
633          CFArrayInsertValueAtIndex(self->_search, 0, domain);
634      } else {
635          CFArrayAppendValue(self->_search, domain);
636      }
637      updateDictRep(self);
638      __CFUnlock(&__CFApplicationPreferencesLock);
639  }
640  
641  Boolean _CFApplicationPreferencesContainsDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) {
642      Boolean result;
643  
644      if (!domain) {
645          return false;
646      }
647  
648      __CFLock(&__CFApplicationPreferencesLock);
649      result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain);
650      __CFUnlock(&__CFApplicationPreferencesLock);
651      return result;
652  }
653  
654  Boolean _CFApplicationPreferencesContainsDomainNoLock(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) {
655      Boolean result;
656      result = CFArrayContainsValue(self->_search, CFRangeMake(0, CFArrayGetCount(self->_search)), domain);
657      return result;
658  }
659  
660  void _CFApplicationPreferencesRemoveDomain(_CFApplicationPreferences *self, CFPreferencesDomainRef domain) {
661      CFIndex idx;
662      CFRange range;
663      __CFLock(&__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      __CFUnlock(&__CFApplicationPreferencesLock);
673  }
674  
675  CFDictionaryRef _CFPrefsCopyAppDictionary(_CFApplicationPreferences *prefs) {
676      CFDictionaryRef retVal = NULL;
677      __CFLock(&__CFApplicationPreferencesLock);
678      CFPreferencesDomainRef applicationDomain = _CFPreferencesStandardDomain(prefs->_appName, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
679      if (applicationDomain) {
680          retVal = _CFPreferencesDomainDeepCopyDictionary(applicationDomain);
681      }
682      __CFUnlock(&__CFApplicationPreferencesLock);
683      return retVal;
684  }