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