2 * Copyright (c) 2000-2006, 2008, 2011, 2012 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"
41 static CF_RETURNS_RETAINED CFMutableArrayRef
42 normalizePath(CFStringRef path
)
44 CFMutableArrayRef elements
;
46 CFArrayRef tmpElements
;
48 if (!isA_CFString(path
)) {
49 _SCErrorSet(kSCStatusInvalidArgument
);
53 if (!CFStringHasPrefix(path
, CFSTR("/"))) {
54 /* if no root separator */
58 tmpElements
= CFStringCreateArrayBySeparatingStrings(NULL
, path
, CFSTR("/"));
59 elements
= CFArrayCreateMutableCopy(NULL
, 0, tmpElements
);
60 CFRelease(tmpElements
);
62 /* remove empty path components */
63 n
= CFArrayGetCount(elements
);
65 CFStringRef pathElement
;
67 pathElement
= CFArrayGetValueAtIndex(elements
, n
);
68 if (CFStringGetLength(pathElement
) == 0) {
69 CFArrayRemoveValueAtIndex(elements
, n
);
78 getPath(SCPreferencesRef prefs
, CFStringRef path
, CFDictionaryRef
*entity
)
81 CFMutableArrayRef elements
;
87 CFDictionaryRef value
= NULL
;
89 elements
= normalizePath(path
);
90 if (elements
== NULL
) {
91 _SCErrorSet(kSCStatusNoKey
);
97 nElements
= CFArrayGetCount(elements
);
100 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
102 __SCPreferencesAccess(prefs
);
104 *entity
= prefsPrivate
->prefs
;
109 for (i
= 0; i
< nElements
; i
++) {
110 element
= CFArrayGetValueAtIndex(elements
, i
);
112 value
= SCPreferencesGetValue(prefs
, CFArrayGetValueAtIndex(elements
, 0));
114 value
= CFDictionaryGetValue(value
, element
);
117 /* if path component does not exist */
118 _SCErrorSet(kSCStatusNoKey
);
122 if (!isA_CFDictionary(value
)) {
123 /* if path component not a dictionary */
124 _SCErrorSet(kSCStatusNoKey
);
128 if ((i
< nElements
- 1) &&
129 CFDictionaryGetValueIfPresent(value
, kSCResvLink
, (const void **)&link
)) {
131 * if not the last path component and this
134 CFMutableArrayRef linkElements
;
136 if (++nLinks
> MAXLINKS
) {
137 /* if we are chasing our tail */
138 _SCErrorSet(kSCStatusMaxLink
);
142 linkElements
= normalizePath(link
);
143 if (linkElements
== NULL
) {
144 /* if the link is bad */
145 _SCErrorSet(kSCStatusNoKey
);
149 CFArrayAppendArray(linkElements
,
151 CFRangeMake(i
+ 1, nElements
-i
- 1));
153 elements
= linkElements
;
170 setPath(SCPreferencesRef prefs
, CFStringRef path
, CFDictionaryRef entity
)
173 CFMutableArrayRef elements
;
178 CFDictionaryRef newEntity
= NULL
;
179 CFDictionaryRef node
= NULL
;
180 CFMutableArrayRef nodes
= NULL
;
183 if ((entity
!= NULL
) && !isA_CFDictionary(entity
)) {
184 _SCErrorSet(kSCStatusInvalidArgument
);
188 elements
= normalizePath(path
);
189 if (elements
== NULL
) {
190 _SCErrorSet(kSCStatusNoKey
);
196 nElements
= CFArrayGetCount(elements
);
199 SCPreferencesPrivateRef prefsPrivate
= (SCPreferencesPrivateRef
)prefs
;
201 __SCPreferencesAccess(prefs
);
203 if (prefsPrivate
->prefs
!= NULL
) {
204 CFRelease(prefsPrivate
->prefs
);
207 if (entity
== NULL
) {
208 prefsPrivate
->prefs
= CFDictionaryCreateMutable(NULL
,
210 &kCFTypeDictionaryKeyCallBacks
,
211 &kCFTypeDictionaryValueCallBacks
);
213 prefsPrivate
->prefs
= CFDictionaryCreateMutableCopy(NULL
, 0, entity
);
216 prefsPrivate
->changed
= TRUE
;
221 nodes
= CFArrayCreateMutable(NULL
, nElements
- 1, &kCFTypeArrayCallBacks
);
222 for (i
= 0; i
< nElements
- 1; i
++) {
223 element
= CFArrayGetValueAtIndex(elements
, i
);
225 node
= SCPreferencesGetValue(prefs
, element
);
227 node
= CFDictionaryGetValue(node
, element
);
232 /* if path component exists */
233 CFArrayAppendValue(nodes
, node
);
235 /* if path component does not exist */
236 node
= CFDictionaryCreate(NULL
,
240 &kCFTypeDictionaryKeyCallBacks
,
241 &kCFTypeDictionaryValueCallBacks
);
242 CFArrayAppendValue(nodes
, node
);
246 if (!isA_CFDictionary(node
)) {
247 _SCErrorSet(kSCStatusNoKey
);
251 if ((i
< nElements
- 1) &&
252 CFDictionaryGetValueIfPresent(node
, kSCResvLink
, (const void **)&link
)) {
254 * if not the last path component and this
257 CFMutableArrayRef linkElements
;
259 if (++nLinks
> MAXLINKS
) {
260 /* if we are chasing our tail */
261 _SCErrorSet(kSCStatusMaxLink
);
265 linkElements
= normalizePath(link
);
266 if (linkElements
== NULL
) {
267 /* if the link is bad */
268 _SCErrorSet(kSCStatusNoKey
);
272 CFArrayAppendArray(linkElements
,
274 CFRangeMake(i
+ 1, nElements
-i
- 1));
276 elements
= linkElements
;
285 * make sure that the last component doesn't step on top
286 * of a non-dictionary component.
288 element
= CFArrayGetValueAtIndex(elements
, nElements
- 1);
290 node
= CFArrayGetValueAtIndex(nodes
, nElements
- 2);
291 node
= CFDictionaryGetValue(node
, element
);
293 node
= SCPreferencesGetValue(prefs
, element
);
295 if ((node
!= NULL
) && !isA_CFDictionary(node
)) {
296 // we won't step on a non-dictionary component
297 _SCErrorSet(kSCStatusInvalidArgument
);
301 if (entity
!= NULL
) {
302 newEntity
= CFRetain(entity
);
304 for (i
= nElements
- 1; i
>= 0; i
--) {
305 element
= CFArrayGetValueAtIndex(elements
, i
);
307 if (newEntity
!= NULL
) {
308 ok
= SCPreferencesSetValue(prefs
, element
, newEntity
);
310 ok
= SCPreferencesRemoveValue(prefs
, element
);
313 CFMutableDictionaryRef newNode
;
315 node
= CFArrayGetValueAtIndex(nodes
, i
- 1);
316 newNode
= CFDictionaryCreateMutableCopy(NULL
, 0, node
);
317 if (newEntity
!= NULL
) {
318 CFDictionarySetValue(newNode
, element
, newEntity
);
319 CFRelease(newEntity
);
321 CFDictionaryRemoveValue(newNode
, element
);
322 if (CFDictionaryGetCount(newNode
) == 0) {
323 // prune the (now empty) parent
331 if (newEntity
!= NULL
) {
332 CFRelease(newEntity
);
337 if (nodes
!= NULL
) CFRelease(nodes
);
344 SCPreferencesPathCreateUniqueChild(SCPreferencesRef prefs
,
348 CFStringRef newPath
= NULL
;
349 CFDictionaryRef newDict
;
351 CFDictionaryRef entity
;
354 /* sorry, you must provide a session */
355 _SCErrorSet(kSCStatusNoPrefsSession
);
359 if (getPath(prefs
, prefix
, &entity
)) {
360 // if prefix path exists
361 if (CFDictionaryContainsKey(entity
, kSCResvLink
)) {
362 /* the path is a link... */
363 _SCErrorSet(kSCStatusFailed
);
366 } else if (SCError() != kSCStatusNoKey
) {
367 // if any error except for a missing prefix path component
371 uuid
= CFUUIDCreate(NULL
);
372 child
= CFUUIDCreateString(NULL
, uuid
);
373 newPath
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@/%@"), prefix
, child
);
377 /* save a new/empty dictionary */
378 newDict
= CFDictionaryCreate(NULL
,
380 &kCFTypeDictionaryKeyCallBacks
,
381 &kCFTypeDictionaryValueCallBacks
);
382 assert(newDict
!= NULL
);
384 if (!setPath(prefs
, newPath
, newDict
)) {
395 SCPreferencesPathGetValue(SCPreferencesRef prefs
,
398 CFDictionaryRef entity
;
399 CFStringRef entityLink
;
402 /* sorry, you must provide a session */
403 _SCErrorSet(kSCStatusNoPrefsSession
);
407 if (!getPath(prefs
, path
, &entity
)) {
411 if (isA_CFDictionary(entity
) &&
412 (CFDictionaryGetValueIfPresent(entity
, kSCResvLink
, (const void **)&entityLink
))) {
413 /* if this is a dictionary AND it is a link */
414 if (!getPath(prefs
, entityLink
, &entity
)) {
415 /* if it was a bad link */
425 SCPreferencesPathGetLink(SCPreferencesRef prefs
,
428 CFDictionaryRef entity
;
429 CFStringRef entityLink
;
432 /* sorry, you must provide a session */
433 _SCErrorSet(kSCStatusNoPrefsSession
);
437 if (!getPath(prefs
, path
, &entity
)) {
441 if (isA_CFDictionary(entity
) &&
442 (CFDictionaryGetValueIfPresent(entity
, kSCResvLink
, (const void **)&entityLink
))) {
443 /* if this is a dictionary AND it is a link */
452 SCPreferencesPathSetValue(SCPreferencesRef prefs
,
454 CFDictionaryRef value
)
459 /* sorry, you must provide a session */
460 _SCErrorSet(kSCStatusNoPrefsSession
);
464 #define NETPREF_NEEDS_REPAIR
465 #ifdef NETPREF_NEEDS_REPAIR
466 if (CFEqual(path
, CFSTR("/CurrentSet")) && isA_CFString(value
)) {
467 static Boolean warned
= FALSE
;
469 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path
);
472 return SCPreferencesSetValue(prefs
, CFSTR("CurrentSet"), value
);
474 #endif // NETPREF_NEEDS_REPAIR
476 if (!isA_CFDictionary(value
)) {
477 #ifdef NETPREF_NEEDS_REPAIR
478 SCPrint(TRUE
, stderr
, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path
);
479 #endif // NETPREF_NEEDS_REPAIR
480 _SCErrorSet(kSCStatusInvalidArgument
);
484 ok
= setPath(prefs
, path
, value
);
490 SCPreferencesPathSetLink(SCPreferencesRef prefs
,
494 CFMutableDictionaryRef dict
;
495 CFDictionaryRef entity
;
499 /* sorry, you must provide a session */
500 _SCErrorSet(kSCStatusNoPrefsSession
);
504 if (!isA_CFString(link
)) {
505 _SCErrorSet(kSCStatusInvalidArgument
);
509 if (!getPath(prefs
, link
, &entity
)) {
514 dict
= CFDictionaryCreateMutable(NULL
,
516 &kCFTypeDictionaryKeyCallBacks
,
517 &kCFTypeDictionaryValueCallBacks
);
518 CFDictionarySetValue(dict
, kSCResvLink
, link
);
519 ok
= setPath(prefs
, path
, dict
);
527 SCPreferencesPathRemoveValue(SCPreferencesRef prefs
,
530 CFMutableArrayRef elements
= NULL
;
532 CFDictionaryRef value
;
535 /* sorry, you must provide a session */
536 _SCErrorSet(kSCStatusNoPrefsSession
);
540 if (!getPath(prefs
, path
, &value
)) {
545 elements
= normalizePath(path
);
546 if (elements
== NULL
) {
547 _SCErrorSet(kSCStatusNoKey
);
551 ok
= setPath(prefs
, path
, NULL
);