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