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