]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPPath.c
configd-135.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPPath.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * November 16, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include <SystemConfiguration/SystemConfiguration.h>
35 #include <SystemConfiguration/SCValidation.h>
36 #include <SystemConfiguration/SCPrivate.h>
37 #include "SCPreferencesInternal.h"
38
39 #define MAXLINKS 8
40
41 static CFArrayRef
42 normalizePath(CFStringRef path)
43 {
44 CFArrayRef tmpElements;
45 CFMutableArrayRef elements;
46 CFIndex nElements;
47 CFIndex i;
48
49 if (!CFStringHasPrefix(path, CFSTR("/"))) {
50 /* if no root separator */
51 return NULL;
52 }
53
54 tmpElements = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
55 elements = CFArrayCreateMutableCopy(NULL, 0, tmpElements);
56 CFRelease(tmpElements);
57
58 /* remove empty path components */
59 nElements = CFArrayGetCount(elements);
60 for (i = nElements; i > 0; i--) {
61 CFStringRef pathElement;
62
63 pathElement = CFArrayGetValueAtIndex(elements, i-1);
64 if (CFStringGetLength(pathElement) == 0) {
65 CFArrayRemoveValueAtIndex(elements, i-1);
66 nElements--;
67 }
68 }
69
70 if (nElements < 1) {
71 CFRelease(elements);
72 return NULL;
73 }
74
75 return elements;
76 }
77
78
79 static Boolean
80 getPath(SCPreferencesRef prefs, CFStringRef path, CFDictionaryRef *entity)
81 {
82 CFStringRef element;
83 CFArrayRef elements;
84 CFIndex i;
85 CFStringRef link;
86 CFIndex nElements;
87 CFIndex nLinks = 0;
88 Boolean ok = FALSE;
89 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
90 CFDictionaryRef value = NULL;
91
92 elements = normalizePath(path);
93 if (elements == NULL) {
94 _SCErrorSet(kSCStatusNoKey);
95 return FALSE;
96 }
97
98 __SCPreferencesAccess(prefs);
99
100 restart :
101
102 nElements = CFArrayGetCount(elements);
103 for (i = 0; i < nElements; i++) {
104 element = CFArrayGetValueAtIndex(elements, i);
105 if (i == 0) {
106 value = CFDictionaryGetValue(prefsPrivate->prefs,
107 CFArrayGetValueAtIndex(elements, 0));
108 } else {
109 value = CFDictionaryGetValue(value, element);
110 }
111 if (value == NULL) {
112 /* if path component does not exist */
113 _SCErrorSet(kSCStatusNoKey);
114 goto done;
115 }
116
117 if (!isA_CFDictionary(value)) {
118 /* if path component not a dictionary */
119 _SCErrorSet(kSCStatusNoKey);
120 goto done;
121 }
122
123 if ((i < nElements-1) &&
124 CFDictionaryGetValueIfPresent(value, kSCResvLink, (const void **)&link)) {
125 /*
126 * if not the last path component and this
127 * element is a link
128 */
129 CFArrayRef linkElements;
130 CFMutableArrayRef newElements;
131
132 if (++nLinks > MAXLINKS) {
133 /* if we are chasing our tail */
134 _SCErrorSet(kSCStatusMaxLink);
135 goto done;
136 }
137
138 linkElements = normalizePath(link);
139 if (linkElements == NULL) {
140 /* if the link is bad */
141 _SCErrorSet(kSCStatusNoKey);
142 goto done;
143 }
144
145 newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
146 CFArrayAppendArray(newElements,
147 elements,
148 CFRangeMake(i+1, nElements-i-1));
149 CFRelease(elements);
150 elements = newElements;
151
152 goto restart;
153 }
154 }
155
156 *entity = value;
157 ok = TRUE;
158
159 done :
160
161 CFRelease(elements);
162 return ok;
163 }
164
165
166 static Boolean
167 setPath(SCPreferencesRef prefs, CFStringRef path, CFDictionaryRef entity)
168 {
169 CFStringRef element;
170 CFArrayRef elements;
171 CFIndex i;
172 CFStringRef link;
173 CFIndex nElements;
174 CFIndex nLinks = 0;
175 CFDictionaryRef newEntity = NULL;
176 CFDictionaryRef node = NULL;
177 CFMutableArrayRef nodes;
178 Boolean ok = FALSE;
179 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
180
181 elements = normalizePath(path);
182 if (elements == NULL) {
183 _SCErrorSet(kSCStatusNoKey);
184 return FALSE;
185 }
186
187 __SCPreferencesAccess(prefs);
188
189 restart :
190
191 nElements = CFArrayGetCount(elements);
192 nodes = CFArrayCreateMutable(NULL, nElements-1, &kCFTypeArrayCallBacks);
193 for (i = 0; i < nElements - 1; i++) {
194 element = CFArrayGetValueAtIndex(elements, i);
195 if (i == 0) {
196 node = CFDictionaryGetValue(prefsPrivate->prefs, element);
197 } else {
198 node = CFDictionaryGetValue(node, element);
199
200 }
201
202 if (node) {
203 /* if path component exists */
204 CFArrayAppendValue(nodes, node);
205 } else {
206 /* if path component does not exist */
207 node = CFDictionaryCreate(NULL,
208 NULL,
209 NULL,
210 0,
211 &kCFTypeDictionaryKeyCallBacks,
212 &kCFTypeDictionaryValueCallBacks);
213 CFArrayAppendValue(nodes, node);
214 CFRelease(node);
215 }
216
217 if (!isA_CFDictionary(node)) {
218 _SCErrorSet(kSCStatusNoKey);
219 goto done;
220 }
221
222 if ((i < nElements-1) &&
223 CFDictionaryGetValueIfPresent(node, kSCResvLink, (const void **)&link)) {
224 /*
225 * if not the last path component and this
226 * element is a link
227 */
228 CFArrayRef linkElements;
229 CFMutableArrayRef newElements;
230
231 if (++nLinks > MAXLINKS) {
232 /* if we are chasing our tail */
233 _SCErrorSet(kSCStatusMaxLink);
234 goto done;
235 }
236
237 linkElements = normalizePath(link);
238 if (linkElements == NULL) {
239 /* if the link is bad */
240 _SCErrorSet(kSCStatusNoKey);
241 goto done;
242 }
243
244 newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
245 CFArrayAppendArray(newElements,
246 elements,
247 CFRangeMake(i+1, nElements-i-1));
248 CFRelease(elements);
249 elements = newElements;
250
251 CFRelease(nodes);
252 goto restart;
253 }
254 }
255
256 if (entity) {
257 newEntity = CFRetain(entity);
258 }
259 for (i = nElements - 1; i >= 0; i--) {
260 element = CFArrayGetValueAtIndex(elements, i);
261 if (i == 0) {
262 if (newEntity) {
263 CFDictionarySetValue(prefsPrivate->prefs, element, newEntity);
264 } else {
265 CFDictionaryRemoveValue(prefsPrivate->prefs, element);
266 }
267 prefsPrivate->changed = TRUE;
268 ok = TRUE;
269 } else {
270 CFMutableDictionaryRef newNode;
271
272 node = CFArrayGetValueAtIndex(nodes, i-1);
273 newNode = CFDictionaryCreateMutableCopy(NULL, 0, node);
274 if (newEntity) {
275 CFDictionarySetValue(newNode, element, newEntity);
276 CFRelease(newEntity);
277 } else {
278 CFDictionaryRemoveValue(newNode, element);
279 if (CFDictionaryGetCount(newNode) == 0) {
280 // prune the (now empty) parent
281 CFRelease(newNode);
282 newNode = NULL;
283 }
284 }
285 newEntity = newNode;
286 }
287 }
288 if (newEntity) {
289 CFRelease(newEntity);
290 }
291
292 done :
293
294 CFRelease(nodes);
295 CFRelease(elements);
296 return ok;
297 }
298
299
300 CFStringRef
301 SCPreferencesPathCreateUniqueChild(SCPreferencesRef prefs,
302 CFStringRef prefix)
303 {
304 CFStringRef child;
305 CFStringRef newPath = NULL;
306 CFMutableDictionaryRef newDict = NULL;
307 CFUUIDRef uuid;
308 CFDictionaryRef entity;
309
310 if (prefs == NULL) {
311 /* sorry, you must provide a session */
312 _SCErrorSet(kSCStatusNoPrefsSession);
313 return NULL;
314 }
315
316 if (getPath(prefs, prefix, &entity)) {
317 // if prefix path exists
318 if (CFDictionaryContainsKey(entity, kSCResvLink)) {
319 /* the path is a link... */
320 _SCErrorSet(kSCStatusFailed);
321 return NULL;
322 }
323 } else if (SCError() != kSCStatusNoKey) {
324 // if any error except for a missing prefix path component
325 return NULL;
326 }
327
328 uuid = CFUUIDCreate(NULL);
329 child = CFUUIDCreateString(NULL, uuid);
330 newPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, child);
331 CFRelease(child);
332 CFRelease(uuid);
333
334 /* save the new dictionary */
335 newDict = CFDictionaryCreateMutable(NULL,
336 0,
337 &kCFTypeDictionaryKeyCallBacks,
338 &kCFTypeDictionaryValueCallBacks);
339 if (!setPath(prefs, newPath, newDict)) {
340 CFRelease(newPath);
341 newPath = NULL;
342 }
343 CFRelease(newDict);
344
345 return newPath;
346 }
347
348
349 CFDictionaryRef
350 SCPreferencesPathGetValue(SCPreferencesRef prefs,
351 CFStringRef path)
352 {
353 CFDictionaryRef entity;
354 CFStringRef entityLink;
355
356 if (prefs == NULL) {
357 /* sorry, you must provide a session */
358 _SCErrorSet(kSCStatusNoPrefsSession);
359 return NULL;
360 }
361
362 if (!getPath(prefs, path, &entity)) {
363 return NULL;
364 }
365
366 if (isA_CFDictionary(entity) &&
367 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
368 /* if this is a dictionary AND it is a link */
369 if (!getPath(prefs, entityLink, &entity)) {
370 /* if it was a bad link */
371 return NULL;
372 }
373 }
374
375 return entity;
376 }
377
378
379 CFStringRef
380 SCPreferencesPathGetLink(SCPreferencesRef prefs,
381 CFStringRef path)
382 {
383 CFDictionaryRef entity;
384 CFStringRef entityLink;
385
386 if (prefs == NULL) {
387 /* sorry, you must provide a session */
388 _SCErrorSet(kSCStatusNoPrefsSession);
389 return NULL;
390 }
391
392 if (!getPath(prefs, path, &entity)) {
393 return NULL;
394 }
395
396 if (isA_CFDictionary(entity) &&
397 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
398 /* if this is a dictionary AND it is a link */
399 return entityLink;
400 }
401
402 return NULL;
403 }
404
405
406 Boolean
407 SCPreferencesPathSetValue(SCPreferencesRef prefs,
408 CFStringRef path,
409 CFDictionaryRef value)
410 {
411 Boolean ok;
412
413 if (prefs == NULL) {
414 /* sorry, you must provide a session */
415 _SCErrorSet(kSCStatusNoPrefsSession);
416 return FALSE;
417 }
418
419 if (!value) {
420 _SCErrorSet(kSCStatusInvalidArgument);
421 return FALSE;
422 }
423
424 ok = setPath(prefs, path, value);
425 return ok;
426 }
427
428
429 Boolean
430 SCPreferencesPathSetLink(SCPreferencesRef prefs,
431 CFStringRef path,
432 CFStringRef link)
433 {
434 CFMutableDictionaryRef dict;
435 CFDictionaryRef entity;
436 Boolean ok;
437
438 if (prefs == NULL) {
439 /* sorry, you must provide a session */
440 _SCErrorSet(kSCStatusNoPrefsSession);
441 return FALSE;
442 }
443
444 if (!link) {
445 _SCErrorSet(kSCStatusInvalidArgument);
446 return FALSE;
447 }
448
449 if (!getPath(prefs, link, &entity)) {
450 // if bad link
451 return FALSE;
452 }
453
454 dict = CFDictionaryCreateMutable(NULL,
455 0,
456 &kCFTypeDictionaryKeyCallBacks,
457 &kCFTypeDictionaryValueCallBacks);
458 CFDictionaryAddValue(dict, kSCResvLink, link);
459 ok = setPath(prefs, path, dict);
460 CFRelease(dict);
461
462 return ok;
463 }
464
465
466 Boolean
467 SCPreferencesPathRemoveValue(SCPreferencesRef prefs,
468 CFStringRef path)
469 {
470 CFArrayRef elements = NULL;
471 Boolean ok = FALSE;
472 CFDictionaryRef value;
473
474 if (prefs == NULL) {
475 /* sorry, you must provide a session */
476 _SCErrorSet(kSCStatusNoPrefsSession);
477 return FALSE;
478 }
479
480 if (!getPath(prefs, path, &value)) {
481 // if no such path
482 return FALSE;
483 }
484
485 elements = normalizePath(path);
486 if (elements == NULL) {
487 _SCErrorSet(kSCStatusNoKey);
488 return FALSE;
489 }
490
491 ok = setPath(prefs, path, NULL);
492
493 CFRelease(elements);
494 return ok;
495 }