2 * Copyright (c) 2000-2006, 2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * November 16, 2000 Allan Nathanson <ajn@apple.com>
34 #include <SystemConfiguration/SystemConfiguration.h>
35 #include <SystemConfiguration/SCValidation.h>
36 #include <SystemConfiguration/SCPrivate.h>
37 #include "SCPreferencesInternal.h"
42 normalizePath(CFStringRef path
)
44 CFArrayRef tmpElements
;
45 CFMutableArrayRef elements
;
49 if (!CFStringHasPrefix(path
, CFSTR("/"))) {
50 /* if no root separator */
54 tmpElements
= CFStringCreateArrayBySeparatingStrings(NULL
, path
, CFSTR("/"));
55 elements
= CFArrayCreateMutableCopy(NULL
, 0, tmpElements
);
56 CFRelease(tmpElements
);
58 /* remove empty path components */
59 nElements
= CFArrayGetCount(elements
);
60 for (i
= nElements
; i
> 0; i
--) {
61 CFStringRef pathElement
;
63 pathElement
= CFArrayGetValueAtIndex(elements
, i
- 1);
64 if (CFStringGetLength(pathElement
) == 0) {
65 CFArrayRemoveValueAtIndex(elements
, i
- 1);
75 getPath(SCPreferencesRef prefs
, CFStringRef path
, CFDictionaryRef
*entity
)
84 CFDictionaryRef value
= NULL
;
86 elements
= normalizePath(path
);
87 if (elements
== NULL
) {
88 _SCErrorSet(kSCStatusNoKey
);
94 nElements
= CFArrayGetCount(elements
);
97 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
99 __SCPreferencesAccess(prefs
);
100 value
= prefsPrivate
->prefs
;
103 for (i
= 0; i
< nElements
; i
++) {
104 element
= CFArrayGetValueAtIndex(elements
, i
);
106 value
= SCPreferencesGetValue(prefs
, CFArrayGetValueAtIndex(elements
, 0));
108 value
= CFDictionaryGetValue(value
, element
);
111 /* if path component does not exist */
112 _SCErrorSet(kSCStatusNoKey
);
116 if (!isA_CFDictionary(value
)) {
117 /* if path component not a dictionary */
118 _SCErrorSet(kSCStatusNoKey
);
122 if ((i
< nElements
- 1) &&
123 CFDictionaryGetValueIfPresent(value
, kSCResvLink
, (const void **)&link
)) {
125 * if not the last path component and this
128 CFArrayRef linkElements
;
129 CFMutableArrayRef newElements
;
131 if (++nLinks
> MAXLINKS
) {
132 /* if we are chasing our tail */
133 _SCErrorSet(kSCStatusMaxLink
);
137 linkElements
= normalizePath(link
);
138 if (linkElements
== NULL
) {
139 /* if the link is bad */
140 _SCErrorSet(kSCStatusNoKey
);
144 newElements
= CFArrayCreateMutableCopy(NULL
, 0, linkElements
);
145 CFRelease(linkElements
);
146 CFArrayAppendArray(newElements
,
148 CFRangeMake(i
+ 1, nElements
-i
- 1));
150 elements
= newElements
;
167 setPath(SCPreferencesRef prefs
, CFStringRef path
, CFDictionaryRef entity
)
175 CFDictionaryRef newEntity
= NULL
;
176 CFDictionaryRef node
= NULL
;
177 CFMutableArrayRef nodes
= NULL
;
180 if ((entity
!= NULL
) && !isA_CFDictionary(entity
)) {
181 _SCErrorSet(kSCStatusInvalidArgument
);
185 elements
= normalizePath(path
);
186 if (elements
== NULL
) {
187 _SCErrorSet(kSCStatusNoKey
);
193 nElements
= CFArrayGetCount(elements
);
196 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
198 __SCPreferencesAccess(prefs
);
200 if (prefsPrivate
->prefs
!= NULL
) {
201 CFRelease(prefsPrivate
->prefs
);
204 if (entity
== NULL
) {
205 prefsPrivate
->prefs
= CFDictionaryCreateMutable(NULL
,
207 &kCFTypeDictionaryKeyCallBacks
,
208 &kCFTypeDictionaryValueCallBacks
);
210 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
213 prefsPrivate
->changed
= TRUE
;
218 nodes
= CFArrayCreateMutable(NULL
, nElements
- 1, &kCFTypeArrayCallBacks
);
219 for (i
= 0; i
< nElements
- 1; i
++) {
220 element
= CFArrayGetValueAtIndex(elements
, i
);
222 node
= SCPreferencesGetValue(prefs
, element
);
224 node
= CFDictionaryGetValue(node
, element
);
229 /* if path component exists */
230 CFArrayAppendValue(nodes
, node
);
232 /* if path component does not exist */
233 node
= CFDictionaryCreate(NULL
,
237 &kCFTypeDictionaryKeyCallBacks
,
238 &kCFTypeDictionaryValueCallBacks
);
239 CFArrayAppendValue(nodes
, node
);
243 if (!isA_CFDictionary(node
)) {
244 _SCErrorSet(kSCStatusNoKey
);
248 if ((i
< nElements
- 1) &&
249 CFDictionaryGetValueIfPresent(node
, kSCResvLink
, (const void **)&link
)) {
251 * if not the last path component and this
254 CFArrayRef linkElements
;
255 CFMutableArrayRef newElements
;
257 if (++nLinks
> MAXLINKS
) {
258 /* if we are chasing our tail */
259 _SCErrorSet(kSCStatusMaxLink
);
263 linkElements
= normalizePath(link
);
264 if (linkElements
== NULL
) {
265 /* if the link is bad */
266 _SCErrorSet(kSCStatusNoKey
);
270 newElements
= CFArrayCreateMutableCopy(NULL
, 0, linkElements
);
271 CFRelease(linkElements
);
272 CFArrayAppendArray(newElements
,
274 CFRangeMake(i
+ 1, nElements
-i
- 1));
276 elements
= newElements
;
284 * make sure that the last component doesn't step on top
285 * of a non-dictionary component.
287 element
= CFArrayGetValueAtIndex(elements
, nElements
- 1);
289 node
= CFArrayGetValueAtIndex(nodes
, nElements
- 2);
290 node
= CFDictionaryGetValue(node
, element
);
292 node
= SCPreferencesGetValue(prefs
, element
);
294 if ((node
!= NULL
) && !isA_CFDictionary(node
)) {
295 // we won't step on a non-dictionary component
296 _SCErrorSet(kSCStatusInvalidArgument
);
300 if (entity
!= NULL
) {
301 newEntity
= CFRetain(entity
);
303 for (i
= nElements
- 1; i
>= 0; i
--) {
304 element
= CFArrayGetValueAtIndex(elements
, i
);
306 if (newEntity
!= NULL
) {
307 ok
= SCPreferencesSetValue(prefs
, element
, newEntity
);
309 ok
= SCPreferencesRemoveValue(prefs
, element
);
312 CFMutableDictionaryRef newNode
;
314 node
= CFArrayGetValueAtIndex(nodes
, i
- 1);
315 newNode
= CFDictionaryCreateMutableCopy(NULL
, 0, node
);
316 if (newEntity
!= NULL
) {
317 CFDictionarySetValue(newNode
, element
, newEntity
);
318 CFRelease(newEntity
);
320 CFDictionaryRemoveValue(newNode
, element
);
321 if (CFDictionaryGetCount(newNode
) == 0) {
322 // prune the (now empty) parent
330 if (newEntity
!= NULL
) {
331 CFRelease(newEntity
);
336 if (nodes
!= NULL
) CFRelease(nodes
);
343 SCPreferencesPathCreateUniqueChild(SCPreferencesRef prefs
,
347 CFStringRef newPath
= NULL
;
348 CFDictionaryRef newDict
;
350 CFDictionaryRef entity
;
353 /* sorry, you must provide a session */
354 _SCErrorSet(kSCStatusNoPrefsSession
);
358 if (getPath(prefs
, prefix
, &entity
)) {
359 // if prefix path exists
360 if (CFDictionaryContainsKey(entity
, kSCResvLink
)) {
361 /* the path is a link... */
362 _SCErrorSet(kSCStatusFailed
);
365 } else if (SCError() != kSCStatusNoKey
) {
366 // if any error except for a missing prefix path component
370 uuid
= CFUUIDCreate(NULL
);
371 child
= CFUUIDCreateString(NULL
, uuid
);
372 newPath
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@"), prefix
, child
);
376 /* save a new/empty dictionary */
377 newDict
= CFDictionaryCreate(NULL
,
379 &kCFTypeDictionaryKeyCallBacks
,
380 &kCFTypeDictionaryValueCallBacks
);
381 if (!setPath(prefs
, newPath
, newDict
)) {
392 SCPreferencesPathGetValue(SCPreferencesRef prefs
,
395 CFDictionaryRef entity
;
396 CFStringRef entityLink
;
399 /* sorry, you must provide a session */
400 _SCErrorSet(kSCStatusNoPrefsSession
);
404 if (!getPath(prefs
, path
, &entity
)) {
408 if (isA_CFDictionary(entity
) &&
409 (CFDictionaryGetValueIfPresent(entity
, kSCResvLink
, (const void **)&entityLink
))) {
410 /* if this is a dictionary AND it is a link */
411 if (!getPath(prefs
, entityLink
, &entity
)) {
412 /* if it was a bad link */
422 SCPreferencesPathGetLink(SCPreferencesRef prefs
,
425 CFDictionaryRef entity
;
426 CFStringRef entityLink
;
429 /* sorry, you must provide a session */
430 _SCErrorSet(kSCStatusNoPrefsSession
);
434 if (!getPath(prefs
, path
, &entity
)) {
438 if (isA_CFDictionary(entity
) &&
439 (CFDictionaryGetValueIfPresent(entity
, kSCResvLink
, (const void **)&entityLink
))) {
440 /* if this is a dictionary AND it is a link */
449 SCPreferencesPathSetValue(SCPreferencesRef prefs
,
451 CFDictionaryRef value
)
456 /* sorry, you must provide a session */
457 _SCErrorSet(kSCStatusNoPrefsSession
);
461 #define NETPREF_NEEDS_REPAIR
462 #ifdef NETPREF_NEEDS_REPAIR
463 if (CFEqual(path
, CFSTR("/CurrentSet")) && isA_CFString(value
)) {
464 static Boolean warned
= FALSE
;
466 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path
);
469 return SCPreferencesSetValue(prefs
, CFSTR("CurrentSet"), value
);
471 #endif // NETPREF_NEEDS_REPAIR
473 if (!isA_CFDictionary(value
)) {
474 #ifdef NETPREF_NEEDS_REPAIR
475 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path
);
476 #endif // NETPREF_NEEDS_REPAIR
477 _SCErrorSet(kSCStatusInvalidArgument
);
481 ok
= setPath(prefs
, path
, value
);
487 SCPreferencesPathSetLink(SCPreferencesRef prefs
,
491 CFMutableDictionaryRef dict
;
492 CFDictionaryRef entity
;
496 /* sorry, you must provide a session */
497 _SCErrorSet(kSCStatusNoPrefsSession
);
501 if (!isA_CFString(link
)) {
502 _SCErrorSet(kSCStatusInvalidArgument
);
506 if (!getPath(prefs
, link
, &entity
)) {
511 dict
= CFDictionaryCreateMutable(NULL
,
513 &kCFTypeDictionaryKeyCallBacks
,
514 &kCFTypeDictionaryValueCallBacks
);
515 CFDictionarySetValue(dict
, kSCResvLink
, link
);
516 ok
= setPath(prefs
, path
, dict
);
524 SCPreferencesPathRemoveValue(SCPreferencesRef prefs
,
527 CFArrayRef elements
= NULL
;
529 CFDictionaryRef value
;
532 /* sorry, you must provide a session */
533 _SCErrorSet(kSCStatusNoPrefsSession
);
537 if (!getPath(prefs
, path
, &value
)) {
542 elements
= normalizePath(path
);
543 if (elements
== NULL
) {
544 _SCErrorSet(kSCStatusNoKey
);
548 ok
= setPath(prefs
, path
, NULL
);