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