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