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