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