]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCPPath.c
configd-84.6.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCPPath.c
1 /*
2 * Copyright(c) 2000-2003 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 session, 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 sessionPrivate = (SCPreferencesPrivateRef)session;
90 CFDictionaryRef value = NULL;
91
92 elements = normalizePath(path);
93 if (elements == NULL) {
94 _SCErrorSet(kSCStatusNoKey);
95 return FALSE;
96 }
97
98 restart :
99
100 nElements = CFArrayGetCount(elements);
101 for (i = 0; i < nElements; i++) {
102 element = CFArrayGetValueAtIndex(elements, i);
103 if (i == 0) {
104 sessionPrivate->accessed = TRUE;
105 value = CFDictionaryGetValue(sessionPrivate->prefs,
106 CFArrayGetValueAtIndex(elements, 0));
107 } else {
108 value = CFDictionaryGetValue(value, element);
109 }
110 if (value == NULL) {
111 /* if path component does not exist */
112 _SCErrorSet(kSCStatusNoKey);
113 goto done;
114 }
115
116 if (!isA_CFDictionary(value)) {
117 /* if path component not a dictionary */
118 _SCErrorSet(kSCStatusNoKey);
119 goto done;
120 }
121
122 if ((i < nElements-1) &&
123 CFDictionaryGetValueIfPresent(value, kSCResvLink, (const void **)&link)) {
124 /*
125 * if not the last path component and this
126 * element is a link
127 */
128 CFArrayRef linkElements;
129 CFMutableArrayRef newElements;
130
131 if (++nLinks > MAXLINKS) {
132 /* if we are chasing our tail */
133 _SCErrorSet(kSCStatusMaxLink);
134 goto done;
135 }
136
137 linkElements = normalizePath(link);
138 if (linkElements == NULL) {
139 /* if the link is bad */
140 _SCErrorSet(kSCStatusNoKey);
141 goto done;
142 }
143
144 newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
145 CFArrayAppendArray(newElements,
146 elements,
147 CFRangeMake(i+1, nElements-i-1));
148 CFRelease(elements);
149 elements = newElements;
150
151 goto restart;
152 }
153 }
154
155 *entity = value;
156 ok = TRUE;
157
158 done :
159
160 CFRelease(elements);
161 return ok;
162 }
163
164
165 static Boolean
166 setPath(SCPreferencesRef session, CFStringRef path, CFDictionaryRef entity)
167 {
168 CFStringRef element;
169 CFArrayRef elements;
170 CFIndex i;
171 CFStringRef link;
172 CFIndex nElements;
173 CFIndex nLinks = 0;
174 CFDictionaryRef newEntity = NULL;
175 CFDictionaryRef node = NULL;
176 CFMutableArrayRef nodes;
177 Boolean ok = FALSE;
178 SCPreferencesPrivateRef sessionPrivate = (SCPreferencesPrivateRef)session;
179
180 elements = normalizePath(path);
181 if (elements == NULL) {
182 _SCErrorSet(kSCStatusNoKey);
183 return FALSE;
184 }
185
186 restart :
187
188 nElements = CFArrayGetCount(elements);
189 nodes = CFArrayCreateMutable(NULL, nElements-1, &kCFTypeArrayCallBacks);
190 for (i = 0; i < nElements - 1; i++) {
191 element = CFArrayGetValueAtIndex(elements, i);
192 if (i == 0) {
193 sessionPrivate->accessed = TRUE;
194 node = CFDictionaryGetValue(sessionPrivate->prefs, element);
195 } else {
196 node = CFDictionaryGetValue(node, element);
197
198 }
199
200 if (node) {
201 /* if path component exists */
202 CFArrayAppendValue(nodes, node);
203 } else {
204 /* if path component does not exist */
205 node = CFDictionaryCreate(NULL,
206 NULL,
207 NULL,
208 0,
209 &kCFTypeDictionaryKeyCallBacks,
210 &kCFTypeDictionaryValueCallBacks);
211 CFArrayAppendValue(nodes, node);
212 CFRelease(node);
213 }
214
215 if (!isA_CFDictionary(node)) {
216 _SCErrorSet(kSCStatusNoKey);
217 goto done;
218 }
219
220 if ((i < nElements-1) &&
221 CFDictionaryGetValueIfPresent(node, kSCResvLink, (const void **)&link)) {
222 /*
223 * if not the last path component and this
224 * element is a link
225 */
226 CFArrayRef linkElements;
227 CFMutableArrayRef newElements;
228
229 if (++nLinks > MAXLINKS) {
230 /* if we are chasing our tail */
231 _SCErrorSet(kSCStatusMaxLink);
232 goto done;
233 }
234
235 linkElements = normalizePath(link);
236 if (linkElements == NULL) {
237 /* if the link is bad */
238 _SCErrorSet(kSCStatusNoKey);
239 goto done;
240 }
241
242 newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
243 CFArrayAppendArray(newElements,
244 elements,
245 CFRangeMake(i+1, nElements-i-1));
246 CFRelease(elements);
247 elements = newElements;
248
249 CFRelease(nodes);
250 goto restart;
251 }
252 }
253
254 if (entity) {
255 newEntity = CFRetain(entity);
256 }
257 for (i = nElements - 1; i >= 0; i--) {
258 element = CFArrayGetValueAtIndex(elements, i);
259 if (i == 0) {
260 if (newEntity) {
261 CFDictionarySetValue(sessionPrivate->prefs, element, newEntity);
262 } else {
263 CFDictionaryRemoveValue(sessionPrivate->prefs, element);
264 }
265 sessionPrivate->changed = TRUE;
266 ok = TRUE;
267 } else {
268 CFMutableDictionaryRef newNode;
269
270 node = CFArrayGetValueAtIndex(nodes, i-1);
271 newNode = CFDictionaryCreateMutableCopy(NULL, 0, node);
272 if (newEntity) {
273 CFDictionarySetValue(newNode, element, newEntity);
274 CFRelease(newEntity);
275 } else {
276 CFDictionaryRemoveValue(newNode, element);
277 }
278 newEntity = newNode;
279 }
280 }
281 if (newEntity) {
282 CFRelease(newEntity);
283 }
284
285 done :
286
287 CFRelease(nodes);
288 CFRelease(elements);
289 return ok;
290 }
291
292
293 CFStringRef
294 SCPreferencesPathCreateUniqueChild(SCPreferencesRef session,
295 CFStringRef prefix)
296 {
297 CFStringRef child;
298 CFStringRef newPath = NULL;
299 CFMutableDictionaryRef newDict = NULL;
300 CFUUIDRef uuid;
301 CFDictionaryRef entity;
302
303 if (_sc_verbose) {
304 SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathCreateUniqueChild:"));
305 SCLog(TRUE, LOG_DEBUG, CFSTR(" prefix = %@"), prefix);
306 }
307
308 if (getPath(session, prefix, &entity)) {
309 // if prefix path exists
310 if (CFDictionaryContainsKey(entity, kSCResvLink)) {
311 /* the path is a link... */
312 _SCErrorSet(kSCStatusFailed);
313 return NULL;
314 }
315 } else if (SCError() != kSCStatusNoKey) {
316 // if any error except for a missing prefix path component
317 return NULL;
318 }
319
320 uuid = CFUUIDCreate(NULL);
321 child = CFUUIDCreateString(NULL, uuid);
322 newPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, child);
323 CFRelease(child);
324 CFRelease(uuid);
325
326 /* save the new dictionary */
327 newDict = CFDictionaryCreateMutable(NULL,
328 0,
329 &kCFTypeDictionaryKeyCallBacks,
330 &kCFTypeDictionaryValueCallBacks);
331 if (setPath(session, newPath, newDict)) {
332 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" child = %@"), newPath);
333 } else {
334 CFRelease(newPath);
335 newPath = NULL;
336 }
337 CFRelease(newDict);
338
339 return newPath;
340 }
341
342
343 CFDictionaryRef
344 SCPreferencesPathGetValue(SCPreferencesRef session,
345 CFStringRef path)
346 {
347 CFDictionaryRef entity;
348 CFStringRef entityLink;
349
350 if (_sc_verbose) {
351 SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathGetValue:"));
352 SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
353 }
354
355 if (!getPath(session, path, &entity)) {
356 return NULL;
357 }
358
359 if (isA_CFDictionary(entity) &&
360 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
361 /* if this is a dictionary AND it is a link */
362 if (!getPath(session, entityLink, &entity)) {
363 /* if it was a bad link */
364 return NULL;
365 }
366 }
367
368 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" value = %@"), entity);
369 return entity;
370 }
371
372
373 CFStringRef
374 SCPreferencesPathGetLink(SCPreferencesRef session,
375 CFStringRef path)
376 {
377 CFDictionaryRef entity;
378 CFStringRef entityLink;
379
380 if (_sc_verbose) {
381 SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathGetLink:"));
382 SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
383 }
384
385 if (!getPath(session, path, &entity)) {
386 return NULL;
387 }
388
389 if (isA_CFDictionary(entity) &&
390 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
391 /* if this is a dictionary AND it is a link */
392 SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" link = %@"), entityLink);
393 return entityLink;
394 }
395
396 return NULL;
397 }
398
399
400 Boolean
401 SCPreferencesPathSetValue(SCPreferencesRef session,
402 CFStringRef path,
403 CFDictionaryRef value)
404 {
405 Boolean ok;
406
407 if (_sc_verbose) {
408 SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathSetValue:"));
409 SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
410 SCLog(TRUE, LOG_DEBUG, CFSTR(" value = %@"), value);
411 }
412
413 if (!value) {
414 _SCErrorSet(kSCStatusInvalidArgument);
415 return FALSE;
416 }
417
418 ok = setPath(session, path, value);
419 return ok;
420 }
421
422
423 Boolean
424 SCPreferencesPathSetLink(SCPreferencesRef session,
425 CFStringRef path,
426 CFStringRef link)
427 {
428 CFMutableDictionaryRef dict;
429 CFDictionaryRef entity;
430 Boolean ok;
431
432 if (_sc_verbose) {
433 SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathSetLink:"));
434 SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
435 SCLog(TRUE, LOG_DEBUG, CFSTR(" link = %@"), link);
436 }
437
438 if (!link) {
439 _SCErrorSet(kSCStatusInvalidArgument);
440 return FALSE;
441 }
442
443 if (!getPath(session, link, &entity)) {
444 // if bad link
445 return FALSE;
446 }
447
448 dict = CFDictionaryCreateMutable(NULL,
449 0,
450 &kCFTypeDictionaryKeyCallBacks,
451 &kCFTypeDictionaryValueCallBacks);
452 CFDictionaryAddValue(dict, kSCResvLink, link);
453 ok = setPath(session, path, dict);
454 CFRelease(dict);
455
456 return ok;
457 }
458
459
460 Boolean
461 SCPreferencesPathRemoveValue(SCPreferencesRef session,
462 CFStringRef path)
463 {
464 CFArrayRef elements = NULL;
465 Boolean ok = FALSE;
466 CFDictionaryRef value;
467
468 if (_sc_verbose) {
469 SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathRemoveValue:"));
470 SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
471 }
472
473 if (!getPath(session, path, &value)) {
474 // if no such path
475 return FALSE;
476 }
477
478 elements = normalizePath(path);
479 if (elements == NULL) {
480 _SCErrorSet(kSCStatusNoKey);
481 return FALSE;
482 }
483
484 ok = setPath(session, path, NULL);
485
486 CFRelease(elements);
487 return ok;
488 }