]> git.saurik.com Git - apple/cf.git/blame - CFPreferences.c
CF-1153.18.tar.gz
[apple/cf.git] / CFPreferences.c
CommitLineData
d8925383 1/*
e29e285d 2 * Copyright (c) 2015 Apple Inc. All rights reserved.
d8925383
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
d7384798 5 *
d8925383
A
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.
d7384798 12 *
d8925383
A
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.
d7384798 20 *
d8925383
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
f64f9b69 23
d8925383 24/* CFPreferences.c
d7384798 25 Copyright (c) 1998-2014, Apple Inc. All rights reserved.
8ca704e1 26 Responsibility: David Smith
d8925383
A
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 34#include <CoreFoundation/CFPropertyList.h>
8ca704e1 35#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_WINDOWS
d8925383 36#include <CoreFoundation/CFBundle.h>
8ca704e1 37#endif
d8925383
A
38#include <CoreFoundation/CFNumber.h>
39#include <CoreFoundation/CFPriv.h>
d8925383
A
40#include "CFInternal.h"
41#include <sys/stat.h>
bd5b749c
A
42#if DEPLOYMENT_TARGET_MACOSX
43#include <unistd.h>
cf7d2af9
A
44#include <CoreFoundation/CFUUID.h>
45#endif
d8925383 46
d8925383
A
47#if DEBUG_PREFERENCES_MEMORY
48#include "../Tests/CFCountingAllocator.c"
49#endif
50
bd5b749c
A
51static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel);
52
d8925383
A
53struct __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
61CONST_STRING_DECL(kCFPreferencesAnyApplication, "kCFPreferencesAnyApplication")
62CONST_STRING_DECL(kCFPreferencesAnyHost, "kCFPreferencesAnyHost")
63CONST_STRING_DECL(kCFPreferencesAnyUser, "kCFPreferencesAnyUser")
64CONST_STRING_DECL(kCFPreferencesCurrentApplication, "kCFPreferencesCurrentApplication")
65CONST_STRING_DECL(kCFPreferencesCurrentHost, "kCFPreferencesCurrentHost")
66CONST_STRING_DECL(kCFPreferencesCurrentUser, "kCFPreferencesCurrentUser")
67
68
69static CFAllocatorRef _preferencesAllocator = NULL;
a48904a4 70CF_PRIVATE CFAllocatorRef __CFPreferencesAllocator(void) {
d8925383
A
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
83void _CFApplicationPreferencesDomainHasChanged(CFPreferencesDomainRef);
84
85#if DEBUG_PREFERENCES_MEMORY
86#warning Preferences debugging on
87CF_EXPORT void CFPreferencesDumpMem(void) {
88 if (_preferencesAllocator) {
89// CFCountingAllocatorPrintSummary(_preferencesAllocator);
90 CFCountingAllocatorPrintPointers(_preferencesAllocator);
91 }
92// CFCountingAllocatorReset(_preferencesAllocator);
93}
94#endif
95
bd5b749c
A
96#if DEPLOYMENT_TARGET_MACOSX
97#pragma mark -
98#pragma mark Determining host UUID
99#endif
100
cf7d2af9
A
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
104int gethostuuid(unsigned char *uuid_buf, const struct timespec *timeoutp);
105
a48904a4 106CF_PRIVATE CFStringRef _CFGetHostUUIDString(void) {
cf7d2af9
A
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
a48904a4 134CF_PRIVATE CFStringRef _CFPreferencesGetByHostIdentifierString(void) {
cf7d2af9
A
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
bd5b749c 166
a48904a4 167CF_PRIVATE CFStringRef _CFPreferencesGetByHostIdentifierString(void) {
bd5b749c
A
168 return CFSTR("");
169}
170
cf7d2af9 171#endif
bd5b749c 172
4c91a73d 173
d8925383
A
174static unsigned long __CFSafeLaunchLevel = 0;
175
cf7d2af9 176#if DEPLOYMENT_TARGET_WINDOWS
bd5b749c
A
177#include <shfolder.h>
178
bd5b749c
A
179#endif
180
d8925383
A
181static CFURLRef _preferencesDirectoryForUserHostSafetyLevel(CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) {
182 CFAllocatorRef alloc = __CFPreferencesAllocator();
cf7d2af9 183#if DEPLOYMENT_TARGET_WINDOWS
bd5b749c
A
184
185 CFURLRef url = NULL;
186
cf7d2af9
A
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);
bd5b749c
A
193 }
194
cf7d2af9 195
bd5b749c
A
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
d8925383
A
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;
bd5b749c 234#endif
d8925383
A
235}
236
237static CFURLRef _preferencesDirectoryForUserHost(CFStringRef userName, CFStringRef hostName) {
238 return _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, __CFSafeLaunchLevel);
239}
240
bd5b749c 241static Boolean __CFPreferencesWritesXML = true;
d8925383
A
242
243Boolean __CFPreferencesShouldWriteXML(void) {
244 return __CFPreferencesWritesXML;
245}
246
d7384798 247static CFLock_t domainCacheLock = CFLockInit;
d8925383
A
248static CFMutableDictionaryRef domainCache = NULL; // mutable
249
250// Public API
251
252CFTypeRef 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
bd5b749c 265CFDictionaryRef CFPreferencesCopyMultiple(CFArrayRef keysToFetch, CFStringRef appName, CFStringRef user, CFStringRef host) {
d8925383
A
266 CFPreferencesDomainRef domain;
267 CFMutableDictionaryRef result;
268 CFIndex idx, count;
269
bd5b749c 270 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
d8925383 271 __CFGenericValidateType(appName, CFStringGetTypeID());
bd5b749c
A
272 __CFGenericValidateType(user, CFStringGetTypeID());
273 __CFGenericValidateType(host, CFStringGetTypeID());
d8925383 274
bd5b749c 275 domain = _CFPreferencesStandardDomain(appName, user, host);
d8925383
A
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 ++) {
bd5b749c 285 CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keysToFetch, idx);
d8925383
A
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
298void 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) {
d8925383
A
305 _CFPreferencesDomainSet(domain, key, value);
306 _CFApplicationPreferencesDomainHasChanged(domain);
d8925383
A
307 }
308}
309
310
bd5b749c 311void CFPreferencesSetMultiple(CFDictionaryRef keysToSet, CFArrayRef keysToRemove, CFStringRef appName, CFStringRef user, CFStringRef host) {
d8925383
A
312 CFPreferencesDomainRef domain;
313 CFIndex idx, count;
bd5b749c 314 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
d8925383
A
315 if (keysToSet) __CFGenericValidateType(keysToSet, CFDictionaryGetTypeID());
316 if (keysToRemove) __CFGenericValidateType(keysToRemove, CFArrayGetTypeID());
317 __CFGenericValidateType(appName, CFStringGetTypeID());
bd5b749c
A
318 __CFGenericValidateType(user, CFStringGetTypeID());
319 __CFGenericValidateType(host, CFStringGetTypeID());
d8925383
A
320
321 CFTypeRef *keys = NULL;
322 CFTypeRef *values;
323 CFIndex numOfKeysToSet = 0;
324
bd5b749c 325 domain = _CFPreferencesStandardDomain(appName, user, host);
d8925383
A
326 if (!domain) return;
327
328 CFAllocatorRef alloc = CFGetAllocator(domain);
d8925383
A
329
330 if (keysToSet && (count = CFDictionaryGetCount(keysToSet))) {
331 numOfKeysToSet = count;
bd5b749c 332 keys = (CFTypeRef *)CFAllocatorAllocate(alloc, 2*count*sizeof(CFTypeRef), 0);
d8925383
A
333 if (keys) {
334 values = &(keys[count]);
335 CFDictionaryGetKeysAndValues(keysToSet, keys, values);
336 for (idx = 0; idx < count; idx ++) {
bd5b749c 337 _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], values[idx]);
d8925383
A
338 }
339 }
340 }
341 if (keysToRemove && (count = CFArrayGetCount(keysToRemove))) {
342 for (idx = 0; idx < count; idx ++) {
bd5b749c 343 CFStringRef removedKey = (CFStringRef)CFArrayGetValueAtIndex(keysToRemove, idx);
d8925383
A
344 _CFPreferencesDomainSet(domain, removedKey, NULL);
345 }
346 }
347
348
349 _CFApplicationPreferencesDomainHasChanged(domain);
350
d8925383
A
351 if(keys) CFAllocatorDeallocate(alloc, keys);
352}
353
354Boolean 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
d8925383
A
358 domain = _CFPreferencesStandardDomain(appName, user, host);
359 if(domain) _CFApplicationPreferencesDomainHasChanged(domain);
360
361 return domain ? _CFPreferencesDomainSynchronize(domain) : false;
362}
363
bd5b749c 364CFArrayRef CFPreferencesCopyApplicationList(CFStringRef user, CFStringRef host) {
d8925383 365 CFArrayRef array;
bd5b749c
A
366 CFAssert1(user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL user or host", __PRETTY_FUNCTION__);
367 array = _CFPreferencesCreateDomainList(user, host);
d8925383
A
368 return array;
369}
370
bd5b749c 371CFArrayRef CFPreferencesCopyKeyList(CFStringRef appName, CFStringRef user, CFStringRef host) {
d8925383 372 CFPreferencesDomainRef domain;
bd5b749c 373 CFAssert1(appName != NULL && user != NULL && host != NULL, __kCFLogAssertion, "%s(): Cannot access preferences for a NULL application name, user, or host", __PRETTY_FUNCTION__);
d8925383 374
bd5b749c 375 domain = _CFPreferencesStandardDomain(appName, user, host);
d8925383
A
376 if (!domain) {
377 return NULL;
378 } else {
d8925383 379 CFArrayRef result;
bd5b749c
A
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) {
d8925383
A
387 result = NULL;
388 } else {
bd5b749c 389 result = CFArrayCreate(alloc, keys, count, &kCFTypeArrayCallBacks);
d8925383 390 }
bd5b749c
A
391 CFAllocatorDeallocate(alloc, keys);
392 if (d) CFRelease(d);
d8925383
A
393 return result;
394 }
395}
396
397
398/****************************/
399/* CFPreferencesDomain */
400/****************************/
401
402static CFStringRef __CFPreferencesDomainCopyDescription(CFTypeRef cf) {
bd5b749c 403 return CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("<Private CFType %p>\n"), cf);
d8925383
A
404}
405
406static void __CFPreferencesDomainDeallocate(CFTypeRef cf) {
bd5b749c 407 const struct __CFPreferencesDomain *domain = (struct __CFPreferencesDomain *)cf;
d8925383
A
408 CFAllocatorRef alloc = __CFPreferencesAllocator();
409 domain->_callBacks->freeDomain(alloc, domain->_context, domain->_domain);
410 if (domain->_context) CFRelease(domain->_context);
411}
412
413static CFTypeID __kCFPreferencesDomainTypeID = _kCFRuntimeNotATypeID;
414
415static 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
d8925383 427/* We spend a lot of time constructing these prefixes; we should cache. REW, 7/19/99 */
bd5b749c
A
428static CFStringRef _CFPreferencesCachePrefixForUserHost(CFStringRef userName, CFStringRef hostName) {
429 if (userName == kCFPreferencesAnyUser && hostName == kCFPreferencesAnyHost) {
430 return (CFStringRef)CFRetain(CFSTR("*/*/"));
431 }
432 CFMutableStringRef result = CFStringCreateMutable(__CFPreferencesAllocator(), 0);
d8925383 433 if (userName == kCFPreferencesCurrentUser) {
8ca704e1 434 userName = CFCopyUserName();
bd5b749c 435 CFStringAppend(result, userName);
8ca704e1 436 CFRelease(userName);
bd5b749c 437 CFStringAppend(result, CFSTR("/"));
d8925383 438 } else if (userName == kCFPreferencesAnyUser) {
bd5b749c 439 CFStringAppend(result, CFSTR("*/"));
d8925383 440 }
d8925383 441 if (hostName == kCFPreferencesCurrentHost) {
bd5b749c
A
442 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
443 CFStringAppend(result, hostID);
444 CFStringAppend(result, CFSTR("/"));
d8925383 445 } else if (hostName == kCFPreferencesAnyHost) {
bd5b749c 446 CFStringAppend(result, CFSTR("*/"));
d8925383 447 }
d8925383
A
448 return result;
449}
450
451// 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
452static CFStringRef _CFPreferencesStandardDomainCacheKey(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
453 CFStringRef prefix = _CFPreferencesCachePrefixForUserHost(userName, hostName);
454 CFStringRef result = NULL;
455
456 if (prefix) {
457 result = CFStringCreateWithFormat(__CFPreferencesAllocator(), NULL, CFSTR("%@%@"), prefix, domainName);
458 CFRelease(prefix);
459 }
460 return result;
461}
462
d8925383
A
463static CFURLRef _CFPreferencesURLForStandardDomainWithSafetyLevel(CFStringRef domainName, CFStringRef userName, CFStringRef hostName, unsigned long safeLevel) {
464 CFURLRef theURL = NULL;
465 CFAllocatorRef prefAlloc = __CFPreferencesAllocator();
cf7d2af9 466#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_WINDOWS
d8925383
A
467 CFURLRef prefDir = _preferencesDirectoryForUserHostSafetyLevel(userName, hostName, safeLevel);
468 CFStringRef appName;
469 CFStringRef fileName;
470 Boolean mustFreeAppName = false;
471
472 if (!prefDir) return NULL;
473 if (domainName == kCFPreferencesAnyApplication) {
474 appName = CFSTR(".GlobalPreferences");
475 } else if (domainName == kCFPreferencesCurrentApplication) {
476 CFBundleRef mainBundle = CFBundleGetMainBundle();
477 appName = mainBundle ? CFBundleGetIdentifier(mainBundle) : NULL;
478 if (!appName || CFStringGetLength(appName) == 0) {
479 appName = _CFProcessNameString();
480 }
481 } else {
482 appName = domainName;
483 }
484 if (userName != kCFPreferencesAnyUser) {
485 if (hostName == kCFPreferencesAnyHost) {
486 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName);
487 } else if (hostName == kCFPreferencesCurrentHost) {
bd5b749c
A
488 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
489 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostID);
d8925383 490 } else {
bd5b749c 491 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.%@.plist"), appName, hostName); // sketchy - this allows someone to set an arbitrary hostname.
d8925383
A
492 }
493 } else {
494 fileName = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR("%@.plist"), appName);
495 }
496 if (mustFreeAppName) {
497 CFRelease(appName);
498 }
499 if (fileName) {
cf7d2af9 500#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
d8925383 501 theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLPOSIXPathStyle, false, prefDir);
cf7d2af9
A
502#elif DEPLOYMENT_TARGET_WINDOWS
503 theURL = CFURLCreateWithFileSystemPathRelativeToBase(prefAlloc, fileName, kCFURLWindowsPathStyle, false, prefDir);
504#endif
d8925383
A
505 if (prefDir) CFRelease(prefDir);
506 CFRelease(fileName);
507 }
508#else
bd5b749c 509//#error Do not know where to store NSUserDefaults on this platform
d8925383
A
510#endif
511 return theURL;
512}
513
514static CFURLRef _CFPreferencesURLForStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
515 return _CFPreferencesURLForStandardDomainWithSafetyLevel(domainName, userName, hostName, __CFSafeLaunchLevel);
516}
517
518CFPreferencesDomainRef _CFPreferencesStandardDomain(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
519 CFPreferencesDomainRef domain;
520 CFStringRef domainKey;
521 Boolean shouldReleaseDomain = true;
522 domainKey = _CFPreferencesStandardDomainCacheKey(domainName, userName, hostName);
d7384798 523 __CFLock(&domainCacheLock);
d8925383
A
524 if (!domainCache) {
525 CFAllocatorRef alloc = __CFPreferencesAllocator();
526 domainCache = CFDictionaryCreateMutable(alloc, 0, & kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
527 }
528 domain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey);
d7384798 529 __CFUnlock(&domainCacheLock);
d8925383
A
530 if (!domain) {
531 // Domain's not in the cache; load from permanent storage
bd5b749c 532 CFURLRef theURL = _CFPreferencesURLForStandardDomain(domainName, userName, hostName);
d8925383 533 if (theURL) {
bd5b749c
A
534 domain = _CFPreferencesDomainCreate(theURL, &__kCFXMLPropertyListDomainCallBacks);
535
d8925383
A
536 if (userName == kCFPreferencesAnyUser) {
537 _CFPreferencesDomainSetIsWorldReadable(domain, true);
538 }
539 CFRelease(theURL);
540 }
d7384798 541 __CFLock(&domainCacheLock);
d8925383
A
542 if (domain && domainCache) {
543 // 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.
544 CFPreferencesDomainRef checkDomain = (CFPreferencesDomainRef)CFDictionaryGetValue(domainCache, domainKey);
545 if(checkDomain) {
546 // 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.
547 // checkDomain was retrieved with a Get, so we don't want to over-release.
548 shouldReleaseDomain = false;
549 CFRelease(domain); // release the domain we synthesized earlier.
550 domain = checkDomain; // repoint it at the domain picked up out of the cache.
551 } else {
552 // We must not have found the domain in the cache, so it's ok for us to put this in.
553 CFDictionarySetValue(domainCache, domainKey, domain);
554 }
555 if(shouldReleaseDomain) CFRelease(domain);
556 }
d7384798 557 __CFUnlock(&domainCacheLock);
d8925383
A
558 }
559 CFRelease(domainKey);
560 return domain;
561}
562
563static void __CFPreferencesPerformSynchronize(const void *key, const void *value, void *context) {
564 CFPreferencesDomainRef domain = (CFPreferencesDomainRef)value;
565 Boolean *cumulativeResult = (Boolean *)context;
566 if (!_CFPreferencesDomainSynchronize(domain)) *cumulativeResult = false;
567}
568
a48904a4 569CF_PRIVATE Boolean _CFSynchronizeDomainCache(void) {
d8925383 570 Boolean result = true;
d7384798 571 __CFLock(&domainCacheLock);
d8925383
A
572 if (domainCache) {
573 CFDictionaryApplyFunction(domainCache, __CFPreferencesPerformSynchronize, &result);
574 }
d7384798 575 __CFUnlock(&domainCacheLock);
d8925383
A
576 return result;
577}
578
a48904a4 579CF_PRIVATE void _CFPreferencesPurgeDomainCache(void) {
d8925383 580 _CFSynchronizeDomainCache();
d7384798 581 __CFLock(&domainCacheLock);
d8925383
A
582 if (domainCache) {
583 CFRelease(domainCache);
584 domainCache = NULL;
585 }
d7384798 586 __CFUnlock(&domainCacheLock);
d8925383
A
587}
588
a48904a4 589CF_PRIVATE CFArrayRef _CFPreferencesCreateDomainList(CFStringRef userName, CFStringRef hostName) {
d8925383
A
590 CFAllocatorRef prefAlloc = __CFPreferencesAllocator();
591 CFArrayRef domains;
592 CFMutableArrayRef marray;
593 CFStringRef *cachedDomainKeys;
594 CFPreferencesDomainRef *cachedDomains;
595 SInt32 idx, cnt;
596 CFStringRef suffix;
597 UInt32 suffixLen;
598 CFURLRef prefDir = _preferencesDirectoryForUserHost(userName, hostName);
bd5b749c 599
d8925383
A
600 if (!prefDir) {
601 return NULL;
602 }
603 if (hostName == kCFPreferencesAnyHost) {
604 suffix = CFStringCreateWithCString(prefAlloc, ".plist", kCFStringEncodingASCII);
605 } else if (hostName == kCFPreferencesCurrentHost) {
bd5b749c
A
606 CFStringRef hostID = _CFPreferencesGetByHostIdentifierString();
607 suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostID);
d8925383 608 } else {
bd5b749c 609 suffix = CFStringCreateWithFormat(prefAlloc, NULL, CFSTR(".%@.plist"), hostName); // sketchy - this allows someone to create a domain list for an arbitrary hostname.
d8925383
A
610 }
611 suffixLen = CFStringGetLength(suffix);
bd5b749c
A
612
613 domains = (CFArrayRef)CFURLCreatePropertyFromResource(prefAlloc, prefDir, kCFURLFileDirectoryContents, NULL);
d8925383
A
614 CFRelease(prefDir);
615 if (domains){
616 marray = CFArrayCreateMutableCopy(prefAlloc, 0, domains);
617 CFRelease(domains);
618 } else {
619 marray = CFArrayCreateMutable(prefAlloc, 0, & kCFTypeArrayCallBacks);
620 }
621 for (idx = CFArrayGetCount(marray)-1; idx >= 0; idx --) {
bd5b749c 622 CFURLRef url = (CFURLRef)CFArrayGetValueAtIndex(marray, idx);
d8925383
A
623 CFStringRef string = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
624 if (!CFStringHasSuffix(string, suffix)) {
625 CFArrayRemoveValueAtIndex(marray, idx);
626 } else {
627 CFStringRef dom = CFStringCreateWithSubstring(prefAlloc, string, CFRangeMake(0, CFStringGetLength(string) - suffixLen));
628 if (CFEqual(dom, CFSTR(".GlobalPreferences"))) {
629 CFArraySetValueAtIndex(marray, idx, kCFPreferencesAnyApplication);
630 } else {
631 CFArraySetValueAtIndex(marray, idx, dom);
632 }
633 CFRelease(dom);
634 }
635 CFRelease(string);
636 }
637 CFRelease(suffix);
bd5b749c 638
d8925383 639 // Now add any domains added in the cache; delete any that have been deleted in the cache
d7384798 640 __CFLock(&domainCacheLock);
d8925383 641 if (!domainCache) {
d7384798 642 __CFUnlock(&domainCacheLock);
d8925383
A
643 return marray;
644 }
645 cnt = CFDictionaryGetCount(domainCache);
bd5b749c 646 cachedDomainKeys = (CFStringRef *)CFAllocatorAllocate(prefAlloc, 2 * cnt * sizeof(CFStringRef), 0);
d8925383
A
647 cachedDomains = (CFPreferencesDomainRef *)(cachedDomainKeys + cnt);
648 CFDictionaryGetKeysAndValues(domainCache, (const void **)cachedDomainKeys, (const void **)cachedDomains);
d7384798 649 __CFUnlock(&domainCacheLock);
d8925383
A
650 suffix = _CFPreferencesCachePrefixForUserHost(userName, hostName);
651 suffixLen = CFStringGetLength(suffix);
652
653 for (idx = 0; idx < cnt; idx ++) {
654 CFStringRef domainKey = cachedDomainKeys[idx];
655 CFPreferencesDomainRef domain = cachedDomains[idx];
656 CFStringRef domainName;
657 CFIndex keyCount = 0;
bd5b749c 658
d8925383
A
659 if (!CFStringHasPrefix(domainKey, suffix)) continue;
660 domainName = CFStringCreateWithSubstring(prefAlloc, domainKey, CFRangeMake(suffixLen, CFStringGetLength(domainKey) - suffixLen));
661 if (CFEqual(domainName, CFSTR("*"))) {
662 CFRelease(domainName);
bd5b749c 663 domainName = (CFStringRef)CFRetain(kCFPreferencesAnyApplication);
d8925383
A
664 } else if (CFEqual(domainName, kCFPreferencesCurrentApplication)) {
665 CFRelease(domainName);
bd5b749c 666 domainName = (CFStringRef)CFRetain(_CFProcessNameString());
d8925383 667 }
bd5b749c
A
668 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
669 keyCount = d ? CFDictionaryGetCount(d) : 0;
670 if (keyCount) CFRelease(d);
d8925383
A
671 if (keyCount == 0) {
672 // Domain was deleted
673 SInt32 firstIndexOfValue = CFArrayGetFirstIndexOfValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName);
674 if (0 <= firstIndexOfValue) {
675 CFArrayRemoveValueAtIndex(marray, firstIndexOfValue);
676 }
677 } else if (!CFArrayContainsValue(marray, CFRangeMake(0, CFArrayGetCount(marray)), domainName)) {
678 CFArrayAppendValue(marray, domainName);
679 }
680 CFRelease(domainName);
681 }
682 CFRelease(suffix);
683 CFAllocatorDeallocate(prefAlloc, cachedDomainKeys);
684 return marray;
d8925383
A
685}
686
687//
688// CFPreferencesDomain functions
689//
690
691CFPreferencesDomainRef _CFPreferencesDomainCreate(CFTypeRef context, const _CFPreferencesDomainCallBacks *callBacks) {
d7384798
A
692 static dispatch_once_t initOnce;
693 dispatch_once(&initOnce, ^{ __kCFPreferencesDomainTypeID = _CFRuntimeRegisterClass(&__CFPreferencesDomainClass); });
d8925383
A
694 CFAllocatorRef alloc = __CFPreferencesAllocator();
695 CFPreferencesDomainRef newDomain;
696 CFAssert(callBacks != NULL && callBacks->createDomain != NULL && callBacks->freeDomain != NULL && callBacks->fetchValue != NULL && callBacks->writeValue != NULL, __kCFLogAssertion, "Cannot create a domain with NULL callbacks");
697 newDomain = (CFPreferencesDomainRef)_CFRuntimeCreateInstance(alloc, __kCFPreferencesDomainTypeID, sizeof(struct __CFPreferencesDomain) - sizeof(CFRuntimeBase), NULL);
698 if (newDomain) {
699 newDomain->_callBacks = callBacks;
700 if (context) CFRetain(context);
701 newDomain->_context = context;
702 newDomain->_domain = callBacks->createDomain(alloc, context);
703 }
704 return newDomain;
705}
706
707CFTypeRef _CFPreferencesDomainCreateValueForKey(CFPreferencesDomainRef domain, CFStringRef key) {
708 return domain->_callBacks->fetchValue(domain->_context, domain->_domain, key);
709}
710
711void _CFPreferencesDomainSet(CFPreferencesDomainRef domain, CFStringRef key, CFTypeRef value) {
712 domain->_callBacks->writeValue(domain->_context, domain->_domain, key, value);
713}
714
a48904a4 715CF_PRIVATE Boolean _CFPreferencesDomainSynchronize(CFPreferencesDomainRef domain) {
d8925383
A
716 return domain->_callBacks->synchronize(domain->_context, domain->_domain);
717}
718
a48904a4 719CF_PRIVATE void _CFPreferencesDomainSetIsWorldReadable(CFPreferencesDomainRef domain, Boolean isWorldReadable) {
d8925383
A
720 if (domain->_callBacks->setIsWorldReadable) {
721 domain->_callBacks->setIsWorldReadable(domain->_context, domain->_domain, isWorldReadable);
722 }
723}
724
a48904a4 725CF_PRIVATE void *_CFPreferencesDomainCopyDictFunc(CFPreferencesDomainRef domain) {
bd5b749c
A
726 return domain->_callBacks->copyDomainDictionary;
727}
728
d8925383 729void _CFPreferencesDomainSetDictionary(CFPreferencesDomainRef domain, CFDictionaryRef dict) {
d8925383 730 CFAllocatorRef alloc = __CFPreferencesAllocator();
bd5b749c
A
731 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
732 CFIndex idx, count = d ? CFDictionaryGetCount(d) : 0;
733
734 CFTypeRef *keys = (CFTypeRef *)CFAllocatorAllocate(alloc, count * sizeof(CFTypeRef), 0);
735 if (d) CFDictionaryGetKeysAndValues(d, keys, NULL);
d8925383
A
736 for (idx = 0; idx < count; idx ++) {
737 _CFPreferencesDomainSet(domain, (CFStringRef)keys[idx], NULL);
738 }
bd5b749c
A
739 CFAllocatorDeallocate(alloc, keys);
740 if (d) CFRelease(d);
d8925383
A
741
742 if (dict && (count = CFDictionaryGetCount(dict)) != 0) {
bd5b749c 743 CFStringRef *newKeys = (CFStringRef *)CFAllocatorAllocate(alloc, count * sizeof(CFStringRef), 0);
d8925383
A
744 CFDictionaryGetKeysAndValues(dict, (const void **)newKeys, NULL);
745 for (idx = 0; idx < count; idx ++) {
746 CFStringRef key = newKeys[idx];
747 _CFPreferencesDomainSet(domain, key, (CFTypeRef)CFDictionaryGetValue(dict, key));
748 }
d8925383 749 CFAllocatorDeallocate(alloc, newKeys);
d8925383
A
750 }
751}
752
d8925383
A
753CFDictionaryRef _CFPreferencesDomainDeepCopyDictionary(CFPreferencesDomainRef domain) {
754 CFDictionaryRef result = domain->_callBacks->copyDomainDictionary(domain->_context, domain->_domain);
755 if(result && CFDictionaryGetCount(result) == 0) {
756 CFRelease(result);
757 result = NULL;
758 }
759 return result;
760}
761
762Boolean _CFPreferencesDomainExists(CFStringRef domainName, CFStringRef userName, CFStringRef hostName) {
763 CFPreferencesDomainRef domain;
d8925383
A
764 domain = _CFPreferencesStandardDomain(domainName, userName, hostName);
765 if (domain) {
bd5b749c
A
766 CFDictionaryRef d = _CFPreferencesDomainDeepCopyDictionary(domain);
767 if (d) CFRelease(d);
768 return d != NULL;
d8925383
A
769 } else {
770 return false;
771 }
772}
773
774/* Volatile domains - context is ignored; domain is a CFDictionary (mutable) */
775static void *createVolatileDomain(CFAllocatorRef allocator, CFTypeRef context) {
776 return CFDictionaryCreateMutable(allocator, 0, & kCFTypeDictionaryKeyCallBacks, & kCFTypeDictionaryValueCallBacks);
777}
778
779static void freeVolatileDomain(CFAllocatorRef allocator, CFTypeRef context, void *domain) {
780 CFRelease((CFTypeRef)domain);
781}
782
783static CFTypeRef fetchVolatileValue(CFTypeRef context, void *domain, CFStringRef key) {
784 CFTypeRef result = CFDictionaryGetValue((CFMutableDictionaryRef )domain, key);
785 if (result) CFRetain(result);
786 return result;
787}
788
789static void writeVolatileValue(CFTypeRef context, void *domain, CFStringRef key, CFTypeRef value) {
790 if (value)
791 CFDictionarySetValue((CFMutableDictionaryRef )domain, key, value);
792 else
793 CFDictionaryRemoveValue((CFMutableDictionaryRef )domain, key);
794}
795
796static Boolean synchronizeVolatileDomain(CFTypeRef context, void *domain) {
797 return true;
798}
799
800static void getVolatileKeysAndValues(CFAllocatorRef alloc, CFTypeRef context, void *domain, void **buf[], CFIndex *numKeyValuePairs) {
801 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)domain;
802 CFIndex count = CFDictionaryGetCount(dict);
803
804 if (buf) {
805 void **values;
806 if ( count < *numKeyValuePairs ) {
807 values = *buf + count;
808 CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values);
809 } else if (alloc != kCFAllocatorNull) {
810 if (*buf) {
bd5b749c 811 *buf = (void **)CFAllocatorReallocate(alloc, *buf, count * 2 * sizeof(void *), 0);
d8925383 812 } else {
bd5b749c 813 *buf = (void **)CFAllocatorAllocate(alloc, count*2*sizeof(void *), 0);
d8925383
A
814 }
815 if (*buf) {
816 values = *buf + count;
817 CFDictionaryGetKeysAndValues(dict, (const void **)*buf, (const void **)values);
818 }
819 }
820 }
821 *numKeyValuePairs = count;
822}
823
824static CFDictionaryRef copyVolatileDomainDictionary(CFTypeRef context, void *volatileDomain) {
825 CFMutableDictionaryRef dict = (CFMutableDictionaryRef)volatileDomain;
826
827 CFDictionaryRef result = (CFDictionaryRef)CFPropertyListCreateDeepCopy(__CFPreferencesAllocator(), dict, kCFPropertyListImmutable);
828 return result;
829}
830
831const _CFPreferencesDomainCallBacks __kCFVolatileDomainCallBacks = {createVolatileDomain, freeVolatileDomain, fetchVolatileValue, writeVolatileValue, synchronizeVolatileDomain, getVolatileKeysAndValues, copyVolatileDomainDictionary, NULL};