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