/ CFPreferences.c
CFPreferences.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  /*	CFPreferences.c
 25  	Copyright (c) 1998-2014, Apple Inc. All rights reserved.
 26  	Responsibility: David Smith
 27  */
 28  
 29  #include <CoreFoundation/CFPreferences.h>
 30  #include <CoreFoundation/CFURLAccess.h>
 31  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
 32  #include <CoreFoundation/CFUserNotification.h>
 33  #endif
 34  #include <CoreFoundation/CFPropertyList.h>
 35  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
 36  #include <CoreFoundation/CFBundle.h>
 37  #endif
 38  #include <CoreFoundation/CFNumber.h>
 39  #include <CoreFoundation/CFPriv.h>
 40  #include "CFInternal.h"
 41  #include <sys/stat.h>
 42  #if DEPLOYMENT_TARGET_MACOSX
 43  #include <unistd.h>
 44  #include <CoreFoundation/CFUUID.h>
 45  #endif
 46  
 47  #if DEBUG_PREFERENCES_MEMORY
 48  #include "../Tests/CFCountingAllocator.c"
 49  #endif
 50  
 51  static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel);
 52  
 53  struct __CFPreferencesDomain {
 54      CFRuntimeBase _base;
 55      /* WARNING - not copying the callbacks; we know they are always static structs */
 56      const _CFPreferencesDomainCallBacks *_callBacks;
 57      CFTypeRef _context;
 58      void *_domain;
 59  };
 60  
 61  CONST_STRING_DECL(kCFPreferencesAnyApplication, "kCFPreferencesAnyApplication")
 62  CONST_STRING_DECL(kCFPreferencesAnyHost, "kCFPreferencesAnyHost")
 63  CONST_STRING_DECL(kCFPreferencesAnyUser, "kCFPreferencesAnyUser")
 64  CONST_STRING_DECL(kCFPreferencesCurrentApplication, "kCFPreferencesCurrentApplication")
 65  CONST_STRING_DECL(kCFPreferencesCurrentHost, "kCFPreferencesCurrentHost")
 66  CONST_STRING_DECL(kCFPreferencesCurrentUser, "kCFPreferencesCurrentUser")
 67  
 68  
 69  static CFAllocatorRef _preferencesAllocator = NULL;
 70  CF_PRIVATE CFAllocatorRef __CFPreferencesAllocator(void) {
 71      if (!_preferencesAllocator) {
 72  #if DEBUG_PREFERENCES_MEMORY
 73          _preferencesAllocator = CFCountingAllocatorCreate(NULL);
 74  #else
 75          _preferencesAllocator = __CFGetDefaultAllocator();
 76          CFRetain(_preferencesAllocator);
 77  #endif
 78      }
 79      return _preferencesAllocator;
 80  }
 81  
 82  // declaration for telling the 
 83  void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef);
 84  
 85  #if DEBUG_PREFERENCES_MEMORY
 86  #warning Preferences debugging on
 87  CF_EXPORT void CFPreferencesDumpMem(void) {
 88      if (_preferencesAllocator) {
 89  //        CFCountingAllocatorPrintSummary(_preferencesAllocator);
 90          CFCountingAllocatorPrintPointers(_preferencesAllocator);
 91      }
 92  //    CFCountingAllocatorReset(_preferencesAllocator);
 93  }
 94  #endif
 95  
 96  #if DEPLOYMENT_TARGET_MACOSX
 97  #pragma mark -
 98  #pragma mark Determining host UUID
 99  #endif
100  
101  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
102  // The entry point is in libSystem.B.dylib, but not actually declared
103  // If this becomes available in a header (<rdar://problem/4943036>), I need to pull this out
104  int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp);
105  
106  CF_PRIVATE CFStringRef _CFGetHostUUIDString(void) {
107      static CFStringRef __hostUUIDString = NULL;
108      
109      if (!__hostUUIDString) {
110          CFUUIDBytes uuidBytes;
111          int getuuidErr = 0;
112          struct timespec timeout = {0, 0};   // Infinite timeout for gethostuuid()
113          
114          getuuidErr = gethostuuid((unsigned char *)&uuidBytes, &timeout);
115          if (getuuidErr == -1) {
116              // An error has occurred trying to get the host UUID string. There's nothing we can do here, so we should just return NULL.
117              CFLog(kCFLogLevelWarning, CFSTR("_CFGetHostUUIDString: unable to determine UUID for host. Error: %d"), errno);
118              return NULL;
119          }
120          
121          CFUUIDRef uuidRef = CFUUIDCreateFromUUIDBytes(kCFAllocatorSystemDefault, uuidBytes);
122          CFStringRef uuidAsString = CFUUIDCreateString(kCFAllocatorSystemDefault, uuidRef);
123          
124          if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)uuidAsString, (void *)&__hostUUIDString)) {
125              CFRelease(uuidAsString);    // someone else made the assignment, so just release the extra string.
126          }
127          
128          CFRelease(uuidRef);
129      }
130      
131      return __hostUUIDString;
132  }
133  
134  CF_PRIVATE CFStringRef _CFPreferencesGetByHostIdentifierString(void) {
135      static CFStringRef __byHostIdentifierString = NULL;
136  
137      if (!__byHostIdentifierString) {
138          CFStringRef hostID = _CFGetHostUUIDString();
139          if (hostID) {
140              if (CFStringHasPrefix(hostID, CFSTR("00000000-0000-1000-8000-"))) {
141                  // If the host UUID is prefixed by "00000000-0000-1000-8000-" then the UUID returned is the "compatible" type. The last field of the string will be the MAC address of the primary ethernet interface of the computer. We use this for compatibility with existing by-host preferences.
142                  CFStringRef lastField = CFStringCreateWithSubstring(kCFAllocatorSystemDefault, hostID, CFRangeMake(24, 12));
143                  CFMutableStringRef tmpstr = CFStringCreateMutableCopy(kCFAllocatorSystemDefault, 0, lastField);
144                  CFStringLowercase(tmpstr, NULL);
145                  CFStringRef downcasedField = CFStringCreateCopy(kCFAllocatorSystemDefault, tmpstr);
146                  
147                  if (!OSAtomicCompareAndSwapPtrBarrier(NULL, (void *)downcasedField, (void *)&__byHostIdentifierString)) {
148                      CFRelease(downcasedField);
149                  }
150                  
151                  CFRelease(tmpstr);
152                  CFRelease(lastField);
153              } else {
154                  // The host UUID is a full UUID, and we should just use that. This doesn't involve any additional string creation, so we should just be able to do the assignment.
155                  __byHostIdentifierString = hostID;
156              }
157          } else {
158              __byHostIdentifierString = CFSTR("UnknownHostID");
159          }
160      }
161      
162      return __byHostIdentifierString;
163  }
164  
165  #else
166  
167  CF_PRIVATE CFStringRef _CFPreferencesGetByHostIdentifierString(void) {
168      return CFSTR("");
169  }
170  
171  #endif
172  
173  
174  static unsigned long __CFSafeLaunchLevel = 0;
175  
176  #if DEPLOYMENT_TARGET_WINDOWS
177  #include <shfolder.h>
178  
179  #endif
180  
181  static CFURLRef _preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) {
182      CFAllocatorRef alloc = __CFPreferencesAllocator();
183  #if DEPLOYMENT_TARGET_WINDOWS
184  
185  	CFURLRef url = NULL;
186  
187  	CFMutableStringRef completePath = _CFCreateApplicationRepositoryPath(alloc, CSIDL_APPDATA);
188   	if (completePath) {
189  	    // append "Preferences\" and make the CFURL
190  	    CFStringAppend(completePath, CFSTR("Preferences\\"));
191  		url = CFURLCreateWithFileSystemPath(alloc, completePath, kCFURLWindowsPathStyle, true);
192  		CFRelease(completePath);
193  	}
194  
195  
196  	// Can't find a better place?  Home directory then?
197  	if (url == NULL)
198  		url = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName);
199  
200  	return url;
201   
202  #else
203      CFURLRef  home = NULL;
204      CFURLRef  url;
205      int levels = 0;
206      //    if (hostName != kCFPreferencesCurrentHost && hostName != kCFPreferencesAnyHost) return NULL; // Arbitrary host access not permitted
207      if (userName == kCFPreferencesAnyUser) {
208          if (!home) home = CFURLCreateWithFileSystemPath(alloc, CFSTR("/Library/Preferences/"), kCFURLPOSIXPathStyle, true);
209          levels = 1;
210          if (hostName == kCFPreferencesCurrentHost) url = home;
211          else {
212              url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Network/"), kCFURLPOSIXPathStyle, true, home);
213              levels ++;
214              CFRelease(home);
215          }
216      } else {
217          home = CFCopyHomeDirectoryURLForUser((userName == kCFPreferencesCurrentUser) ? NULL : userName);
218          if (home) {
219              url = (safeLevel > 0) ? CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Safe Preferences/"), kCFURLPOSIXPathStyle, true, home) :
220              CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("Library/Preferences/"), kCFURLPOSIXPathStyle, true, home);
221              levels = 2;
222              CFRelease(home);
223              if (hostName != kCFPreferencesAnyHost) {
224                  home = url;
225                  url = CFURLCreateWithFileSystemPathRelativeToBase(alloc, CFSTR("ByHost/"), kCFURLPOSIXPathStyle, true, home);
226                  levels ++;
227                  CFRelease(home);
228              }
229          } else {
230              url = NULL;
231          }
232      }
233      return url;
234  #endif
235  }
236  
237  static CFURLRef  _preferencesDirectoryForUserHost(CFStringRef  userName, CFStringRef  hostName) {
238      return _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, __CFSafeLaunchLevel);
239  }
240  
241  static Boolean __CFPreferencesWritesXML = true;
242  
243  Boolean __CFPreferencesShouldWriteXML(void) {
244      return __CFPreferencesWritesXML;
245  }
246  
247  static CFLock_t domainCacheLock = CFLockInit;
248  static CFMutableDictionaryRef  domainCache = NULL; // mutable
249  
250  // Public API
251  
252  CFTypeRef  CFPreferencesCopyValue(CFStringRef  key, CFStringRef  appName, CFStringRef  user, CFStringRef  host) {
253      CFPreferencesDomainRef domain;
254      CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
255      CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
256      
257      domain = _CFPreferencesStandardDomain(appName, user, host);
258      if (domain) {
259          return _CFPreferencesDomainCreateValueForKey(domain, key);
260      } else {
261          return NULL;
262      }
263  }
264  
265  CFDictionaryRef CFPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef appName, CFStringRef user, CFStringRef host) {
266      CFPreferencesDomainRef domain;
267      CFMutableDictionaryRef result;
268      CFIndex idx, count;
269  
270      CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
271      __CFGenericValidateType(appName, CFStringGetTypeID());
272      __CFGenericValidateType(user, CFStringGetTypeID());
273      __CFGenericValidateType(host, CFStringGetTypeID());
274  
275      domain = _CFPreferencesStandardDomain(appName, user, host);
276      if (!domain) return NULL;
277      if (!keysToFetch) {
278          return _CFPreferencesDomainDeepCopyDictionary(domain);
279      } else {
280          __CFGenericValidateType(keysToFetch, CFArrayGetTypeID());
281          count = CFArrayGetCount(keysToFetch);
282          result = CFDictionaryCreateMutable(CFGetAllocator(domain), count, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
283          if (!result) return NULL;
284          for (idx = 0; idx < count; idx ++) {
285              CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keysToFetch, idx);
286              CFPropertyListRef value;
287              __CFGenericValidateType(key, CFStringGetTypeID());
288              value = _CFPreferencesDomainCreateValueForKey(domain, key);
289              if (value) {
290                  CFDictionarySetValue(result, key, value);
291                  CFRelease(value);
292              }
293          }
294      }
295      return result;
296  }
297  
298  void CFPreferencesSetValue(CFStringRef  key, CFTypeRef  value, CFStringRef  appName, CFStringRef  user, CFStringRef  host) {
299      CFPreferencesDomainRef domain;
300      CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
301      CFAssert1(key != NULL, __kCFLogAssertion, "%s(): Cannot access preferences with a NULL key", __PRETTY_FUNCTION__);
302  
303      domain = _CFPreferencesStandardDomain(appName, user, host);
304      if (domain) {
305          _CFPreferencesDomainSet(domain, key, value);
306          _CFApplicationPreferencesDomainHasChanged(domain);
307      }
308  }
309  
310  
311  void CFPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef appName, CFStringRef user, CFStringRef host) {
312      CFPreferencesDomainRef domain;
313      CFIndex idx, count;
314      CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
315      if (keysToSet) __CFGenericValidateType(keysToSet, CFDictionaryGetTypeID());
316      if (keysToRemove) __CFGenericValidateType(keysToRemove, CFArrayGetTypeID());
317      __CFGenericValidateType(appName, CFStringGetTypeID());
318      __CFGenericValidateType(user, CFStringGetTypeID());
319      __CFGenericValidateType(host, CFStringGetTypeID());
320  
321      CFTypeRef *keys = NULL;
322      CFTypeRef *values;
323      CFIndex numOfKeysToSet = 0;
324      
325      domain = _CFPreferencesStandardDomain(appName, user, host);
326      if (!domain) return;
327  
328      CFAllocatorRef alloc = CFGetAllocator(domain);
329      
330      if (keysToSet && (count = CFDictionaryGetCount(keysToSet))) {
331          numOfKeysToSet = count;
332          keys = (CFTypeRef *)CFAllocatorAllocate(alloc, 2*count*sizeof(CFTypeRef), 0);
333          if (keys) {
334              values = &(keys[count]);
335              CFDictionaryGetKeysAndValues(keysToSet, keys, values);
336              for (idx = 0; idx < count; idx ++) {
337                  _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], values[idx]);
338              }
339          }
340      }
341      if (keysToRemove && (count = CFArrayGetCount(keysToRemove))) {
342          for (idx = 0; idx < count; idx ++) {
343              CFStringRef removedKey = (CFStringRef)CFArrayGetValueAtIndex(keysToRemove, idx);
344              _CFPreferencesDomainSet(domain, removedKey, NULL);
345          }
346      }
347  
348  
349      _CFApplicationPreferencesDomainHasChanged(domain);
350      
351      if(keys) CFAllocatorDeallocate(alloc, keys);
352  }
353  
354  Boolean CFPreferencesSynchronize(CFStringRef  appName, CFStringRef  user, CFStringRef  host) {
355      CFPreferencesDomainRef domain;
356      CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
357  
358      domain = _CFPreferencesStandardDomain(appName, user, host);
359      if(domain) _CFApplicationPreferencesDomainHasChanged(domain);
360      
361      return domain ? _CFPreferencesDomainSynchronize(domain) : false;
362  }
363  
364  CFArrayRef  CFPreferencesCopyApplicationList(CFStringRef  user, CFStringRef  host) {
365      CFArrayRef  array;
366      CFAssert1(user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__);
367      array = _CFPreferencesCreateDomainList(user, host);
368      return array;
369  }
370  
371  CFArrayRef  CFPreferencesCopyKeyList(CFStringRef  appName, CFStringRef  user, CFStringRef  host) {
372      CFPreferencesDomainRef domain;
373      CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
374  
375      domain = _CFPreferencesStandardDomain(appName, user, host);
376      if (!domain) {
377          return NULL;
378      } else {
379          CFArrayRef  result;
380  
381  	CFAllocatorRef alloc = __CFPreferencesAllocator();
382  	CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
383  	CFIndex count = d ? CFDictionaryGetCount(d) : 0;
384  	CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0);
385  	if (d) CFDictionaryGetKeysAndValues(d, keys, NULL);
386          if (count == 0) {
387              result = NULL;
388          } else {
389              result = CFArrayCreate(alloc, keys, count, &kCFTypeArrayCallBacks);
390          }
391  	CFAllocatorDeallocate(alloc, keys);
392  	if (d) CFRelease(d);
393          return result;
394      }
395  }
396  
397  Boolean CFPreferencesAppValueIsForced(CFStringRef key, CFStringRef applicationID) {
398      // kind of a stub but not really
399      // Darling doesn't have "forced" preferences
400      // (those would be preferences enforced by an administrator)
401      return false;
402  };
403  
404  
405  /****************************/
406  /*  CFPreferencesDomain     */
407  /****************************/
408  
409  static CFStringRef __CFPreferencesDomainCopyDescription(CFTypeRef cf) {
410      return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("<Private CFType %p>\n"), cf);
411  }
412  
413  static void __CFPreferencesDomainDeallocate(CFTypeRef cf) {
414      const struct __CFPreferencesDomain *domain = (struct __CFPreferencesDomain *)cf;
415      CFAllocatorRef alloc = __CFPreferencesAllocator();
416      domain->_callBacks->freeDomain(alloc, domain->_context, domain->_domain);
417      if (domain->_context) CFRelease(domain->_context);
418  }
419  
420  static CFTypeID __kCFPreferencesDomainTypeID = _kCFRuntimeNotATypeID;
421  
422  static const CFRuntimeClass __CFPreferencesDomainClass = {
423      0,
424      "CFPreferencesDomain",
425      NULL,      // init
426      NULL,      // copy
427      __CFPreferencesDomainDeallocate,
428      NULL,
429      NULL,
430      NULL,      // 
431      __CFPreferencesDomainCopyDescription
432  };
433  
434  /* We spend a lot of time constructing these prefixes; we should cache.  REW, 7/19/99 */
435  static CFStringRef  _CFPreferencesCachePrefixForUserHost(CFStringRef  userName, CFStringRef  hostName) {
436      if (userName == kCFPreferencesAnyUser && hostName == kCFPreferencesAnyHost) {
437          return (CFStringRef)CFRetain(CFSTR("*/*/"));
438      }
439      CFMutableStringRef result = CFStringCreateMutable(__CFPreferencesAllocator(), 0);
440      if (userName == kCFPreferencesCurrentUser) {
441          userName = CFCopyUserName();
442          CFStringAppend(result, userName);
443          CFRelease(userName);
444          CFStringAppend(result, CFSTR("/"));
445      } else if (userName == kCFPreferencesAnyUser) {
446          CFStringAppend(result, CFSTR("*/"));
447      }
448      if (hostName == kCFPreferencesCurrentHost) {
449          CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
450          CFStringAppend(result, hostID);
451          CFStringAppend(result, CFSTR("/"));
452      } else if (hostName == kCFPreferencesAnyHost) {
453          CFStringAppend(result, CFSTR("*/"));
454      }
455      return result;
456  }
457  
458  // 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
459  static CFStringRef  _CFPreferencesStandardDomainCacheKey(CFStringRef  domainName, CFStringRef  userName, CFStringRef  hostName) {
460      CFStringRef  prefix = _CFPreferencesCachePrefixForUserHost(userName, hostName);
461      CFStringRef  result = NULL;
462      
463      if (prefix) {
464          result = CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("%@%@"), prefix, domainName);
465          CFRelease(prefix);
466      }
467      return result;
468  }
469  
470  static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) {
471      CFURLRef theURL = NULL;
472      CFAllocatorRef prefAlloc = __CFPreferencesAllocator();
473  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
474      CFURLRef prefDir = _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, safeLevel);
475      CFStringRef  appName;
476      CFStringRef  fileName;
477      Boolean mustFreeAppName = false;
478      
479      if (!prefDir) return NULL;
480      if (domainName == kCFPreferencesAnyApplication) {
481          appName = CFSTR(".GlobalPreferences");
482      } else if (domainName == kCFPreferencesCurrentApplication) {
483          CFBundleRef mainBundle = CFBundleGetMainBundle();
484          appName = mainBundle ? CFBundleGetIdentifier(mainBundle) : NULL;
485          if (!appName || CFStringGetLength(appName) == 0) {
486              appName = _CFProcessNameString();
487          }
488      } else {
489          appName = domainName;
490      }
491      if (userName != kCFPreferencesAnyUser) {
492          if (hostName == kCFPreferencesAnyHost) {
493              fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName);
494          } else if (hostName == kCFPreferencesCurrentHost) {
495              CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
496              fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostID);
497          } else {
498              fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostName);      // sketchy - this allows someone to set an arbitrary hostname.
499          }
500      } else {
501          fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName);
502      }
503      if (mustFreeAppName) {
504  	CFRelease(appName);
505      }
506      if (fileName) {
507  #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
508          theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLPOSIXPathStyle, false, prefDir);
509  #elif DEPLOYMENT_TARGET_WINDOWS
510  		theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLWindowsPathStyle, false, prefDir);
511  #endif
512          if (prefDir) CFRelease(prefDir);
513          CFRelease(fileName);
514      }
515  #else
516  //#error Do not know where to store NSUserDefaults on this platform
517  #endif
518      return theURL;
519  }
520  
521  static CFURLRef _CFPreferencesURLForStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
522      return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName, userName, hostName, __CFSafeLaunchLevel);
523  }
524  
525  CFPreferencesDomainRef _CFPreferencesStandardDomain(CFStringRef  domainName, CFStringRef  userName, CFStringRef  hostName) {
526      CFPreferencesDomainRef domain;
527      CFStringRef  domainKey;
528      Boolean shouldReleaseDomain = true;
529       domainKey = _CFPreferencesStandardDomainCacheKey(domainName, userName, hostName);
530      __CFLock(&domainCacheLock);
531      if (!domainCache) {
532          CFAllocatorRef alloc = __CFPreferencesAllocator();
533          domainCache = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
534      }
535      domain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey);
536      __CFUnlock(&domainCacheLock);
537      if (!domain) {
538          // Domain's not in the cache; load from permanent storage
539  		CFURLRef  theURL = _CFPreferencesURLForStandardDomain(domainName, userName, hostName);
540          if (theURL) {
541  			domain = _CFPreferencesDomainCreate(theURL, &__kCFXMLPropertyListDomainCallBacks);
542  
543              if (userName == kCFPreferencesAnyUser) {
544                  _CFPreferencesDomainSetIsWorldReadable(domain, true);
545              }
546              CFRelease(theURL);
547          }
548  	__CFLock(&domainCacheLock);
549          if (domain && domainCache) {
550              // 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.
551              CFPreferencesDomainRef checkDomain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey);
552              if(checkDomain) {
553                  // 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.
554                  // checkDomain was retrieved with a Get, so we don't want to over-release.
555                  shouldReleaseDomain = false;
556                  CFRelease(domain);	// release the domain we synthesized earlier.
557                  domain = checkDomain;	// repoint it at the domain picked up out of the cache.
558              } else {
559                  // We must not have found the domain in the cache, so it's ok for us to put this in.
560                  CFDictionarySetValue(domainCache, domainKey, domain);                
561              }
562              if(shouldReleaseDomain) CFRelease(domain);
563          }
564  	__CFUnlock(&domainCacheLock);
565      }
566      CFRelease(domainKey);
567      return domain;
568  }
569  
570  static void __CFPreferencesPerformSynchronize(const void *key, const void *value, void *context) {
571      CFPreferencesDomainRef domain = (CFPreferencesDomainRef)value;
572      Boolean *cumulativeResult = (Boolean *)context;
573      if (!_CFPreferencesDomainSynchronize(domain)) *cumulativeResult = false;
574  }
575  
576  CF_PRIVATE Boolean _CFSynchronizeDomainCache(void) {
577      Boolean result = true;
578      __CFLock(&domainCacheLock);
579      if (domainCache) {
580          CFDictionaryApplyFunction(domainCache, __CFPreferencesPerformSynchronize, &result);
581      }
582      __CFUnlock(&domainCacheLock);
583      return result;
584  }
585  
586  CF_PRIVATE void _CFPreferencesPurgeDomainCache(void) {
587      _CFSynchronizeDomainCache();
588      __CFLock(&domainCacheLock);
589      if (domainCache) {
590          CFRelease(domainCache);
591          domainCache = NULL;
592      }
593      __CFUnlock(&domainCacheLock);
594  }
595  
596  CFArrayRef  _CFPreferencesCreateDomainList(CFStringRef  userName, CFStringRef  hostName) {
597      CFAllocatorRef prefAlloc = __CFPreferencesAllocator();
598      CFArrayRef  domains;
599      CFMutableArrayRef  marray;
600      CFStringRef  *cachedDomainKeys;
601      CFPreferencesDomainRef *cachedDomains;
602      SInt32 idx, cnt;
603      CFStringRef  suffix;
604      UInt32 suffixLen;
605      CFURLRef prefDir = _preferencesDirectoryForUserHost(userName, hostName);
606      
607      if (!prefDir) {
608          return NULL;
609      }
610      if (hostName == kCFPreferencesAnyHost) {
611          suffix = CFStringCreateWithCString(prefAlloc, ".plist", kCFStringEncodingASCII);
612      } else if (hostName == kCFPreferencesCurrentHost) {
613          CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
614          suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostID);
615      } else {
616          suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostName);   // sketchy - this allows someone to create a domain list for an arbitrary hostname.
617      }
618      suffixLen = CFStringGetLength(suffix);
619      
620      domains = (CFArrayRef)CFURLCreatePropertyFromResource(prefAlloc, prefDir, kCFURLFileDirectoryContents, NULL);
621      CFRelease(prefDir);
622      if (domains){
623          marray = CFArrayCreateMutableCopy(prefAlloc, 0, domains);
624          CFRelease(domains);
625      } else {
626          marray = CFArrayCreateMutable(prefAlloc, 0, & kCFTypeArrayCallBacks);
627      }
628      for (idx = CFArrayGetCount(marray)-1; idx >= 0; idx --) {
629          CFURLRef  url = (CFURLRef)CFArrayGetValueAtIndex(marray, idx);
630          CFStringRef string = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
631          if (!CFStringHasSuffix(string, suffix)) {
632              CFArrayRemoveValueAtIndex(marray, idx);
633          } else {
634              CFStringRef  dom = CFStringCreateWithSubstring(prefAlloc, string, CFRangeMake(0, CFStringGetLength(string) - suffixLen));
635              if (CFEqual(dom, CFSTR(".GlobalPreferences"))) {
636                  CFArraySetValueAtIndex(marray, idx, kCFPreferencesAnyApplication);
637              } else {
638                  CFArraySetValueAtIndex(marray, idx, dom);
639              }
640              CFRelease(dom);
641          }
642          CFRelease(string);
643      }
644      CFRelease(suffix);
645      
646      // Now add any domains added in the cache; delete any that have been deleted in the cache
647      __CFLock(&domainCacheLock);
648      if (!domainCache) {
649          __CFUnlock(&domainCacheLock);
650          return marray;
651      }
652      cnt = CFDictionaryGetCount(domainCache);
653      cachedDomainKeys = (CFStringRef *)CFAllocatorAllocate(prefAlloc, 2 * cnt * sizeof(CFStringRef), 0);
654      cachedDomains = (CFPreferencesDomainRef *)(cachedDomainKeys + cnt);
655      CFDictionaryGetKeysAndValues(domainCache, (const void **)cachedDomainKeys, (const void **)cachedDomains);
656      __CFUnlock(&domainCacheLock);
657      suffix = _CFPreferencesCachePrefixForUserHost(userName, hostName);
658      suffixLen = CFStringGetLength(suffix);
659      
660      for (idx = 0; idx < cnt; idx ++) {
661          CFStringRef  domainKey = cachedDomainKeys[idx];
662          CFPreferencesDomainRef domain = cachedDomains[idx];
663          CFStringRef  domainName;
664          CFIndex keyCount = 0;
665          
666          if (!CFStringHasPrefix(domainKey, suffix)) continue;
667          domainName = CFStringCreateWithSubstring(prefAlloc, domainKey, CFRangeMake(suffixLen, CFStringGetLength(domainKey) - suffixLen));
668          if (CFEqual(domainName, CFSTR("*"))) {
669              CFRelease(domainName);
670              domainName = (CFStringRef)CFRetain(kCFPreferencesAnyApplication);
671          } else if (CFEqual(domainName, kCFPreferencesCurrentApplication)) {
672              CFRelease(domainName);
673              domainName = (CFStringRef)CFRetain(_CFProcessNameString());
674          }
675          CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
676          keyCount = d ? CFDictionaryGetCount(d) : 0;
677          if (keyCount) CFRelease(d);
678          if (keyCount == 0) {
679              // Domain was deleted
680              SInt32 firstIndexOfValue = CFArrayGetFirstIndexOfValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName);
681              if (0 <= firstIndexOfValue) {
682                  CFArrayRemoveValueAtIndex(marray, firstIndexOfValue);
683              }
684          } else if (!CFArrayContainsValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName)) {
685              CFArrayAppendValue(marray, domainName);
686          }
687          CFRelease(domainName);
688      }
689      CFRelease(suffix);
690      CFAllocatorDeallocate(prefAlloc, cachedDomainKeys);
691      return marray;
692  }
693  
694  //
695  // CFPreferencesDomain functions
696  //
697  
698  CFPreferencesDomainRef _CFPreferencesDomainCreate(CFTypeRef  context, const _CFPreferencesDomainCallBacks *callBacks) {
699      static dispatch_once_t initOnce;
700      dispatch_once(&initOnce, ^{ __kCFPreferencesDomainTypeID = _CFRuntimeRegisterClass(&__CFPreferencesDomainClass); });
701      CFAllocatorRef alloc = __CFPreferencesAllocator();
702      CFPreferencesDomainRef newDomain;
703      CFAssert(callBacks != NULL && callBacks->createDomain != NULL && callBacks->freeDomain != NULL && callBacks->fetchValue != NULL && callBacks->writeValue != NULL, __kCFLogAssertion, "Cannot create a domain with NULL callbacks");
704      newDomain = (CFPreferencesDomainRef)_CFRuntimeCreateInstance(alloc, __kCFPreferencesDomainTypeID, sizeof(struct __CFPreferencesDomain) - sizeof(CFRuntimeBase), NULL);
705      if (newDomain) {
706          newDomain->_callBacks = callBacks;
707          if (context) CFRetain(context);
708          newDomain->_context = context;
709          newDomain->_domain = callBacks->createDomain(alloc, context);
710      }
711      return newDomain;
712  }
713  
714  CFTypeRef  _CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain, CFStringRef key) {
715      return domain->_callBacks->fetchValue(domain->_context, domain->_domain, key);
716  }
717  
718  void _CFPreferencesDomainSet(CFPreferencesDomainRef domain, CFStringRef  key, CFTypeRef  value) {
719      domain->_callBacks->writeValue(domain->_context, domain->_domain, key, value);
720  }
721  
722  CF_PRIVATE Boolean _CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain) {
723      return domain->_callBacks->synchronize(domain->_context, domain->_domain);
724  }
725  
726  CF_PRIVATE void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain, Boolean isWorldReadable) {
727      if (domain->_callBacks->setIsWorldReadable) {
728          domain->_callBacks->setIsWorldReadable(domain->_context, domain->_domain, isWorldReadable);
729      }
730  }
731  
732  CF_PRIVATE void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain) {
733      return domain->_callBacks->copyDomainDictionary;
734  }
735  
736  void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain, CFDictionaryRef dict) {
737      CFAllocatorRef alloc = __CFPreferencesAllocator();
738      CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
739      CFIndex idx, count = d ? CFDictionaryGetCount(d) : 0;
740  
741      CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0);
742      if (d) CFDictionaryGetKeysAndValues(d, keys, NULL);
743      for (idx = 0; idx < count; idx ++) {
744          _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], NULL);
745      }
746      CFAllocatorDeallocate(alloc, keys);
747      if (d) CFRelease(d);
748  
749      if (dict && (count = CFDictionaryGetCount(dict)) != 0) {
750          CFStringRef *newKeys = (CFStringRef *)CFAllocatorAllocate(alloc, count * sizeof(CFStringRef), 0);
751          CFDictionaryGetKeysAndValues(dict, (const void **)newKeys, NULL);
752          for (idx = 0; idx < count; idx ++) {
753              CFStringRef key = newKeys[idx];
754              _CFPreferencesDomainSet(domain, key, (CFTypeRef)CFDictionaryGetValue(dict, key));
755          }
756              CFAllocatorDeallocate(alloc, newKeys);
757      }
758  }
759  
760  CFDictionaryRef _CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain) {
761      CFDictionaryRef result = domain->_callBacks->copyDomainDictionary(domain->_context, domain->_domain);
762      if(result && CFDictionaryGetCount(result) == 0) {
763          CFRelease(result);
764          result = NULL;
765      }
766      return result;
767  }
768  
769  Boolean _CFPreferencesDomainExists(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
770      CFPreferencesDomainRef domain;
771      domain = _CFPreferencesStandardDomain(domainName, userName, hostName);
772      if (domain) {
773  		CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
774  		if (d) CFRelease(d);
775  		return d != NULL;
776      } else {
777          return false;
778      }
779  }
780  
781  /* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
782  static void *createVolatileDomain(CFAllocatorRef allocator, CFTypeRef  context) {
783      return CFDictionaryCreateMutable(allocator, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
784  }
785  
786  static void freeVolatileDomain(CFAllocatorRef allocator, CFTypeRef  context, void *domain) {
787      CFRelease((CFTypeRef)domain);
788  }
789  
790  static CFTypeRef  fetchVolatileValue(CFTypeRef  context, void *domain, CFStringRef  key) {
791      CFTypeRef  result = CFDictionaryGetValue((CFMutableDictionaryRef  )domain, key);
792      if (result) CFRetain(result);
793      return result;
794  }
795  
796  static void writeVolatileValue(CFTypeRef  context, void *domain, CFStringRef  key, CFTypeRef  value) {
797      if (value)
798          CFDictionarySetValue((CFMutableDictionaryRef  )domain, key, value);
799      else
800          CFDictionaryRemoveValue((CFMutableDictionaryRef  )domain, key);
801  }
802  
803  static Boolean synchronizeVolatileDomain(CFTypeRef  context, void *domain) {
804      return true;
805  }
806  
807  static void getVolatileKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *domain, void **buf[], CFIndex *numKeyValuePairs) {
808      CFMutableDictionaryRef dict = (CFMutableDictionaryRef)domain;
809      CFIndex count = CFDictionaryGetCount(dict);
810  
811      if (buf) {
812          void **values;
813          if ( count < *numKeyValuePairs ) {
814              values = *buf + count;
815              CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values);
816          } else if (alloc != kCFAllocatorNull) {
817              if (*buf) {
818                  *buf = (void **)CFAllocatorReallocate(alloc, *buf, count * 2 * sizeof(void *), 0);
819              } else {
820                  *buf = (void **)CFAllocatorAllocate(alloc, count*2*sizeof(void *), 0);
821              }
822              if (*buf) {
823                  values = *buf + count;
824                  CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values);
825              }
826          }
827      }
828      *numKeyValuePairs = count;
829  }
830  
831  static CFDictionaryRef copyVolatileDomainDictionary(CFTypeRef context, void *volatileDomain) {
832      CFMutableDictionaryRef dict = (CFMutableDictionaryRef)volatileDomain;
833      
834      CFDictionaryRef result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict, kCFPropertyListImmutable);
835      return result;
836  }
837  
838  const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks = {createVolatileDomain, freeVolatileDomain, fetchVolatileValue, writeVolatileValue, synchronizeVolatileDomain, getVolatileKeysAndValues, copyVolatileDomainDictionary, NULL};