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