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/* CFPreferences.c 25 Copyright (c) 1998-2013, 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 51static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel); 52 53struct __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 61CONST_STRING_DECL(kCFPreferencesAnyApplication, "kCFPreferencesAnyApplication") 62CONST_STRING_DECL(kCFPreferencesAnyHost, "kCFPreferencesAnyHost") 63CONST_STRING_DECL(kCFPreferencesAnyUser, "kCFPreferencesAnyUser") 64CONST_STRING_DECL(kCFPreferencesCurrentApplication, "kCFPreferencesCurrentApplication") 65CONST_STRING_DECL(kCFPreferencesCurrentHost, "kCFPreferencesCurrentHost") 66CONST_STRING_DECL(kCFPreferencesCurrentUser, "kCFPreferencesCurrentUser") 67 68 69static CFAllocatorRef _preferencesAllocator = NULL; 70CF_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 83void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef); 84 85#if DEBUG_PREFERENCES_MEMORY 86#warning Preferences debugging on 87CF_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 104int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp); 105 106CF_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 134CF_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 167CF_PRIVATE CFStringRef _CFPreferencesGetByHostIdentifierString(void) { 168 return CFSTR(""); 169} 170 171#endif 172 173 174static unsigned long __CFSafeLaunchLevel = 0; 175 176#if DEPLOYMENT_TARGET_WINDOWS 177#include <shfolder.h> 178 179#endif 180 181static 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 237static CFURLRef _preferencesDirectoryForUserHost(CFStringRef userName, CFStringRef hostName) { 238 return _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, __CFSafeLaunchLevel); 239} 240 241static Boolean __CFPreferencesWritesXML = true; 242 243Boolean __CFPreferencesShouldWriteXML(void) { 244 return __CFPreferencesWritesXML; 245} 246 247static CFSpinLock_t domainCacheLock = CFSpinLockInit; 248static CFMutableDictionaryRef domainCache = NULL; // mutable 249 250// Public API 251 252CFTypeRef 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 265CFDictionaryRef 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 298void 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 311void 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 354Boolean 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 364CFArrayRef 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 371CFArrayRef 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 398/****************************/ 399/* CFPreferencesDomain */ 400/****************************/ 401 402static CFStringRef __CFPreferencesDomainCopyDescription(CFTypeRef cf) { 403 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("<Private CFType %p>\n"), cf); 404} 405 406static void __CFPreferencesDomainDeallocate(CFTypeRef cf) { 407 const struct __CFPreferencesDomain *domain = (struct __CFPreferencesDomain *)cf; 408 CFAllocatorRef alloc = __CFPreferencesAllocator(); 409 domain->_callBacks->freeDomain(alloc, domain->_context, domain->_domain); 410 if (domain->_context) CFRelease(domain->_context); 411} 412 413static CFTypeID __kCFPreferencesDomainTypeID = _kCFRuntimeNotATypeID; 414 415static const CFRuntimeClass __CFPreferencesDomainClass = { 416 0, 417 "CFPreferencesDomain", 418 NULL, // init 419 NULL, // copy 420 __CFPreferencesDomainDeallocate, 421 NULL, 422 NULL, 423 NULL, // 424 __CFPreferencesDomainCopyDescription 425}; 426 427/* This is called once at CFInitialize() time. */ 428CF_PRIVATE void __CFPreferencesDomainInitialize(void) { 429 __kCFPreferencesDomainTypeID = _CFRuntimeRegisterClass(&__CFPreferencesDomainClass); 430} 431 432/* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */ 433static CFStringRef _CFPreferencesCachePrefixForUserHost(CFStringRef userName, CFStringRef hostName) { 434 if (userName == kCFPreferencesAnyUser && hostName == kCFPreferencesAnyHost) { 435 return (CFStringRef)CFRetain(CFSTR("*/*/")); 436 } 437 CFMutableStringRef result = CFStringCreateMutable(__CFPreferencesAllocator(), 0); 438 if (userName == kCFPreferencesCurrentUser) { 439 userName = CFCopyUserName(); 440 CFStringAppend(result, userName); 441 CFRelease(userName); 442 CFStringAppend(result, CFSTR("/")); 443 } else if (userName == kCFPreferencesAnyUser) { 444 CFStringAppend(result, CFSTR("*/")); 445 } 446 if (hostName == kCFPreferencesCurrentHost) { 447 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString(); 448 CFStringAppend(result, hostID); 449 CFStringAppend(result, CFSTR("/")); 450 } else if (hostName == kCFPreferencesAnyHost) { 451 CFStringAppend(result, CFSTR("*/")); 452 } 453 return result; 454} 455 456// 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 457static CFStringRef _CFPreferencesStandardDomainCacheKey(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { 458 CFStringRef prefix = _CFPreferencesCachePrefixForUserHost(userName, hostName); 459 CFStringRef result = NULL; 460 461 if (prefix) { 462 result = CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("%@%@"), prefix, domainName); 463 CFRelease(prefix); 464 } 465 return result; 466} 467 468static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) { 469 CFURLRef theURL = NULL; 470 CFAllocatorRef prefAlloc = __CFPreferencesAllocator(); 471#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS 472 CFURLRef prefDir = _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, safeLevel); 473 CFStringRef appName; 474 CFStringRef fileName; 475 Boolean mustFreeAppName = false; 476 477 if (!prefDir) return NULL; 478 if (domainName == kCFPreferencesAnyApplication) { 479 appName = CFSTR(".GlobalPreferences"); 480 } else if (domainName == kCFPreferencesCurrentApplication) { 481 CFBundleRef mainBundle = CFBundleGetMainBundle(); 482 appName = mainBundle ? CFBundleGetIdentifier(mainBundle) : NULL; 483 if (!appName || CFStringGetLength(appName) == 0) { 484 appName = _CFProcessNameString(); 485 } 486 } else { 487 appName = domainName; 488 } 489 if (userName != kCFPreferencesAnyUser) { 490 if (hostName == kCFPreferencesAnyHost) { 491 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName); 492 } else if (hostName == kCFPreferencesCurrentHost) { 493 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString(); 494 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostID); 495 } else { 496 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostName); // sketchy - this allows someone to set an arbitrary hostname. 497 } 498 } else { 499 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName); 500 } 501 if (mustFreeAppName) { 502 CFRelease(appName); 503 } 504 if (fileName) { 505#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED 506 theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLPOSIXPathStyle, false, prefDir); 507#elif DEPLOYMENT_TARGET_WINDOWS 508 theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLWindowsPathStyle, false, prefDir); 509#endif 510 if (prefDir) CFRelease(prefDir); 511 CFRelease(fileName); 512 } 513#else 514//#error Do not know where to store NSUserDefaults on this platform 515#endif 516 return theURL; 517} 518 519static CFURLRef _CFPreferencesURLForStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { 520 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName, userName, hostName, __CFSafeLaunchLevel); 521} 522 523CFPreferencesDomainRef _CFPreferencesStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { 524 CFPreferencesDomainRef domain; 525 CFStringRef domainKey; 526 Boolean shouldReleaseDomain = true; 527 domainKey = _CFPreferencesStandardDomainCacheKey(domainName, userName, hostName); 528 __CFSpinLock(&domainCacheLock); 529 if (!domainCache) { 530 CFAllocatorRef alloc = __CFPreferencesAllocator(); 531 domainCache = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 532 } 533 domain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey); 534 __CFSpinUnlock(&domainCacheLock); 535 if (!domain) { 536 // Domain's not in the cache; load from permanent storage 537 CFURLRef theURL = _CFPreferencesURLForStandardDomain(domainName, userName, hostName); 538 if (theURL) { 539 domain = _CFPreferencesDomainCreate(theURL, &__kCFXMLPropertyListDomainCallBacks); 540 541 if (userName == kCFPreferencesAnyUser) { 542 _CFPreferencesDomainSetIsWorldReadable(domain, true); 543 } 544 CFRelease(theURL); 545 } 546 __CFSpinLock(&domainCacheLock); 547 if (domain && domainCache) { 548 // 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. 549 CFPreferencesDomainRef checkDomain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey); 550 if(checkDomain) { 551 // 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. 552 // checkDomain was retrieved with a Get, so we don't want to over-release. 553 shouldReleaseDomain = false; 554 CFRelease(domain); // release the domain we synthesized earlier. 555 domain = checkDomain; // repoint it at the domain picked up out of the cache. 556 } else { 557 // We must not have found the domain in the cache, so it's ok for us to put this in. 558 CFDictionarySetValue(domainCache, domainKey, domain); 559 } 560 if(shouldReleaseDomain) CFRelease(domain); 561 } 562 __CFSpinUnlock(&domainCacheLock); 563 } 564 CFRelease(domainKey); 565 return domain; 566} 567 568static void __CFPreferencesPerformSynchronize(const void *key, const void *value, void *context) { 569 CFPreferencesDomainRef domain = (CFPreferencesDomainRef)value; 570 Boolean *cumulativeResult = (Boolean *)context; 571 if (!_CFPreferencesDomainSynchronize(domain)) *cumulativeResult = false; 572} 573 574CF_PRIVATE Boolean _CFSynchronizeDomainCache(void) { 575 Boolean result = true; 576 __CFSpinLock(&domainCacheLock); 577 if (domainCache) { 578 CFDictionaryApplyFunction(domainCache, __CFPreferencesPerformSynchronize, &result); 579 } 580 __CFSpinUnlock(&domainCacheLock); 581 return result; 582} 583 584CF_PRIVATE void _CFPreferencesPurgeDomainCache(void) { 585 _CFSynchronizeDomainCache(); 586 __CFSpinLock(&domainCacheLock); 587 if (domainCache) { 588 CFRelease(domainCache); 589 domainCache = NULL; 590 } 591 __CFSpinUnlock(&domainCacheLock); 592} 593 594CF_PRIVATE CFArrayRef _CFPreferencesCreateDomainList(CFStringRef userName, CFStringRef hostName) { 595 CFAllocatorRef prefAlloc = __CFPreferencesAllocator(); 596 CFArrayRef domains; 597 CFMutableArrayRef marray; 598 CFStringRef *cachedDomainKeys; 599 CFPreferencesDomainRef *cachedDomains; 600 SInt32 idx, cnt; 601 CFStringRef suffix; 602 UInt32 suffixLen; 603 CFURLRef prefDir = _preferencesDirectoryForUserHost(userName, hostName); 604 605 if (!prefDir) { 606 return NULL; 607 } 608 if (hostName == kCFPreferencesAnyHost) { 609 suffix = CFStringCreateWithCString(prefAlloc, ".plist", kCFStringEncodingASCII); 610 } else if (hostName == kCFPreferencesCurrentHost) { 611 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString(); 612 suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostID); 613 } else { 614 suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostName); // sketchy - this allows someone to create a domain list for an arbitrary hostname. 615 } 616 suffixLen = CFStringGetLength(suffix); 617 618 domains = (CFArrayRef)CFURLCreatePropertyFromResource(prefAlloc, prefDir, kCFURLFileDirectoryContents, NULL); 619 CFRelease(prefDir); 620 if (domains){ 621 marray = CFArrayCreateMutableCopy(prefAlloc, 0, domains); 622 CFRelease(domains); 623 } else { 624 marray = CFArrayCreateMutable(prefAlloc, 0, & kCFTypeArrayCallBacks); 625 } 626 for (idx = CFArrayGetCount(marray)-1; idx >= 0; idx --) { 627 CFURLRef url = (CFURLRef)CFArrayGetValueAtIndex(marray, idx); 628 CFStringRef string = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); 629 if (!CFStringHasSuffix(string, suffix)) { 630 CFArrayRemoveValueAtIndex(marray, idx); 631 } else { 632 CFStringRef dom = CFStringCreateWithSubstring(prefAlloc, string, CFRangeMake(0, CFStringGetLength(string) - suffixLen)); 633 if (CFEqual(dom, CFSTR(".GlobalPreferences"))) { 634 CFArraySetValueAtIndex(marray, idx, kCFPreferencesAnyApplication); 635 } else { 636 CFArraySetValueAtIndex(marray, idx, dom); 637 } 638 CFRelease(dom); 639 } 640 CFRelease(string); 641 } 642 CFRelease(suffix); 643 644 // Now add any domains added in the cache; delete any that have been deleted in the cache 645 __CFSpinLock(&domainCacheLock); 646 if (!domainCache) { 647 __CFSpinUnlock(&domainCacheLock); 648 return marray; 649 } 650 cnt = CFDictionaryGetCount(domainCache); 651 cachedDomainKeys = (CFStringRef *)CFAllocatorAllocate(prefAlloc, 2 * cnt * sizeof(CFStringRef), 0); 652 cachedDomains = (CFPreferencesDomainRef *)(cachedDomainKeys + cnt); 653 CFDictionaryGetKeysAndValues(domainCache, (const void **)cachedDomainKeys, (const void **)cachedDomains); 654 __CFSpinUnlock(&domainCacheLock); 655 suffix = _CFPreferencesCachePrefixForUserHost(userName, hostName); 656 suffixLen = CFStringGetLength(suffix); 657 658 for (idx = 0; idx < cnt; idx ++) { 659 CFStringRef domainKey = cachedDomainKeys[idx]; 660 CFPreferencesDomainRef domain = cachedDomains[idx]; 661 CFStringRef domainName; 662 CFIndex keyCount = 0; 663 664 if (!CFStringHasPrefix(domainKey, suffix)) continue; 665 domainName = CFStringCreateWithSubstring(prefAlloc, domainKey, CFRangeMake(suffixLen, CFStringGetLength(domainKey) - suffixLen)); 666 if (CFEqual(domainName, CFSTR("*"))) { 667 CFRelease(domainName); 668 domainName = (CFStringRef)CFRetain(kCFPreferencesAnyApplication); 669 } else if (CFEqual(domainName, kCFPreferencesCurrentApplication)) { 670 CFRelease(domainName); 671 domainName = (CFStringRef)CFRetain(_CFProcessNameString()); 672 } 673 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain); 674 keyCount = d ? CFDictionaryGetCount(d) : 0; 675 if (keyCount) CFRelease(d); 676 if (keyCount == 0) { 677 // Domain was deleted 678 SInt32 firstIndexOfValue = CFArrayGetFirstIndexOfValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName); 679 if (0 <= firstIndexOfValue) { 680 CFArrayRemoveValueAtIndex(marray, firstIndexOfValue); 681 } 682 } else if (!CFArrayContainsValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName)) { 683 CFArrayAppendValue(marray, domainName); 684 } 685 CFRelease(domainName); 686 } 687 CFRelease(suffix); 688 CFAllocatorDeallocate(prefAlloc, cachedDomainKeys); 689 return marray; 690} 691 692// 693// CFPreferencesDomain functions 694// 695 696CFPreferencesDomainRef _CFPreferencesDomainCreate(CFTypeRef context, const _CFPreferencesDomainCallBacks *callBacks) { 697 CFAllocatorRef alloc = __CFPreferencesAllocator(); 698 CFPreferencesDomainRef newDomain; 699 CFAssert(callBacks != NULL && callBacks->createDomain != NULL && callBacks->freeDomain != NULL && callBacks->fetchValue != NULL && callBacks->writeValue != NULL, __kCFLogAssertion, "Cannot create a domain with NULL callbacks"); 700 newDomain = (CFPreferencesDomainRef)_CFRuntimeCreateInstance(alloc, __kCFPreferencesDomainTypeID, sizeof(struct __CFPreferencesDomain) - sizeof(CFRuntimeBase), NULL); 701 if (newDomain) { 702 newDomain->_callBacks = callBacks; 703 if (context) CFRetain(context); 704 newDomain->_context = context; 705 newDomain->_domain = callBacks->createDomain(alloc, context); 706 } 707 return newDomain; 708} 709 710CFTypeRef _CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain, CFStringRef key) { 711 return domain->_callBacks->fetchValue(domain->_context, domain->_domain, key); 712} 713 714void _CFPreferencesDomainSet(CFPreferencesDomainRef domain, CFStringRef key, CFTypeRef value) { 715 domain->_callBacks->writeValue(domain->_context, domain->_domain, key, value); 716} 717 718CF_PRIVATE Boolean _CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain) { 719 return domain->_callBacks->synchronize(domain->_context, domain->_domain); 720} 721 722CF_PRIVATE void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain, Boolean isWorldReadable) { 723 if (domain->_callBacks->setIsWorldReadable) { 724 domain->_callBacks->setIsWorldReadable(domain->_context, domain->_domain, isWorldReadable); 725 } 726} 727 728CF_PRIVATE void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain) { 729 return domain->_callBacks->copyDomainDictionary; 730} 731 732void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain, CFDictionaryRef dict) { 733 CFAllocatorRef alloc = __CFPreferencesAllocator(); 734 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain); 735 CFIndex idx, count = d ? CFDictionaryGetCount(d) : 0; 736 737 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0); 738 if (d) CFDictionaryGetKeysAndValues(d, keys, NULL); 739 for (idx = 0; idx < count; idx ++) { 740 _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], NULL); 741 } 742 CFAllocatorDeallocate(alloc, keys); 743 if (d) CFRelease(d); 744 745 if (dict && (count = CFDictionaryGetCount(dict)) != 0) { 746 CFStringRef *newKeys = (CFStringRef *)CFAllocatorAllocate(alloc, count * sizeof(CFStringRef), 0); 747 CFDictionaryGetKeysAndValues(dict, (const void **)newKeys, NULL); 748 for (idx = 0; idx < count; idx ++) { 749 CFStringRef key = newKeys[idx]; 750 _CFPreferencesDomainSet(domain, key, (CFTypeRef)CFDictionaryGetValue(dict, key)); 751 } 752 CFAllocatorDeallocate(alloc, newKeys); 753 } 754} 755 756CFDictionaryRef _CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain) { 757 CFDictionaryRef result = domain->_callBacks->copyDomainDictionary(domain->_context, domain->_domain); 758 if(result && CFDictionaryGetCount(result) == 0) { 759 CFRelease(result); 760 result = NULL; 761 } 762 return result; 763} 764 765Boolean _CFPreferencesDomainExists(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) { 766 CFPreferencesDomainRef domain; 767 domain = _CFPreferencesStandardDomain(domainName, userName, hostName); 768 if (domain) { 769 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain); 770 if (d) CFRelease(d); 771 return d != NULL; 772 } else { 773 return false; 774 } 775} 776 777/* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */ 778static void *createVolatileDomain(CFAllocatorRef allocator, CFTypeRef context) { 779 return CFDictionaryCreateMutable(allocator, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks); 780} 781 782static void freeVolatileDomain(CFAllocatorRef allocator, CFTypeRef context, void *domain) { 783 CFRelease((CFTypeRef)domain); 784} 785 786static CFTypeRef fetchVolatileValue(CFTypeRef context, void *domain, CFStringRef key) { 787 CFTypeRef result = CFDictionaryGetValue((CFMutableDictionaryRef )domain, key); 788 if (result) CFRetain(result); 789 return result; 790} 791 792static void writeVolatileValue(CFTypeRef context, void *domain, CFStringRef key, CFTypeRef value) { 793 if (value) 794 CFDictionarySetValue((CFMutableDictionaryRef )domain, key, value); 795 else 796 CFDictionaryRemoveValue((CFMutableDictionaryRef )domain, key); 797} 798 799static Boolean synchronizeVolatileDomain(CFTypeRef context, void *domain) { 800 return true; 801} 802 803static void getVolatileKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *domain, void **buf[], CFIndex *numKeyValuePairs) { 804 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)domain; 805 CFIndex count = CFDictionaryGetCount(dict); 806 807 if (buf) { 808 void **values; 809 if ( count < *numKeyValuePairs ) { 810 values = *buf + count; 811 CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values); 812 } else if (alloc != kCFAllocatorNull) { 813 if (*buf) { 814 *buf = (void **)CFAllocatorReallocate(alloc, *buf, count * 2 * sizeof(void *), 0); 815 } else { 816 *buf = (void **)CFAllocatorAllocate(alloc, count*2*sizeof(void *), 0); 817 } 818 if (*buf) { 819 values = *buf + count; 820 CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values); 821 } 822 } 823 } 824 *numKeyValuePairs = count; 825} 826 827static CFDictionaryRef copyVolatileDomainDictionary(CFTypeRef context, void *volatileDomain) { 828 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)volatileDomain; 829 830 CFDictionaryRef result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict, kCFPropertyListImmutable); 831 return result; 832} 833 834const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks = {createVolatileDomain, freeVolatileDomain, fetchVolatileValue, writeVolatileValue, synchronizeVolatileDomain, getVolatileKeysAndValues, copyVolatileDomainDictionary, NULL}; 835