/ 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};