1/* 2 * Copyright (c) 2000-2006, 2008, 2011, 2012 Apple 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 41static CF_RETURNS_RETAINED CFMutableArrayRef 42normalizePath(CFStringRef path) 43{ 44 CFMutableArrayRef elements; 45 CFIndex n; 46 CFArrayRef tmpElements; 47 48 if (!isA_CFString(path)) { 49 _SCErrorSet(kSCStatusInvalidArgument); 50 return NULL; 51 } 52 53 if (!CFStringHasPrefix(path, CFSTR("/"))) { 54 /* if no root separator */ 55 return NULL; 56 } 57 58 tmpElements = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/")); 59 elements = CFArrayCreateMutableCopy(NULL, 0, tmpElements); 60 CFRelease(tmpElements); 61 62 /* remove empty path components */ 63 n = CFArrayGetCount(elements); 64 while (n-- > 0) { 65 CFStringRef pathElement; 66 67 pathElement = CFArrayGetValueAtIndex(elements, n); 68 if (CFStringGetLength(pathElement) == 0) { 69 CFArrayRemoveValueAtIndex(elements, n); 70 } 71 } 72 73 return elements; 74} 75 76 77static Boolean 78getPath(SCPreferencesRef prefs, CFStringRef path, CFDictionaryRef *entity) 79{ 80 CFStringRef element; 81 CFMutableArrayRef elements; 82 CFIndex i; 83 CFStringRef link; 84 CFIndex nElements; 85 CFIndex nLinks = 0; 86 Boolean ok = FALSE; 87 CFDictionaryRef value = NULL; 88 89 elements = normalizePath(path); 90 if (elements == NULL) { 91 _SCErrorSet(kSCStatusNoKey); 92 return FALSE; 93 } 94 95 restart : 96 97 nElements = CFArrayGetCount(elements); 98 99 if (nElements < 1) { 100 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 101 102 __SCPreferencesAccess(prefs); 103 104 *entity = prefsPrivate->prefs; 105 ok = TRUE; 106 goto done; 107 } 108 109 for (i = 0; i < nElements; i++) { 110 element = CFArrayGetValueAtIndex(elements, i); 111 if (i == 0) { 112 value = SCPreferencesGetValue(prefs, CFArrayGetValueAtIndex(elements, 0)); 113 } else { 114 value = CFDictionaryGetValue(value, element); 115 } 116 if (value == NULL) { 117 /* if path component does not exist */ 118 _SCErrorSet(kSCStatusNoKey); 119 goto done; 120 } 121 122 if (!isA_CFDictionary(value)) { 123 /* if path component not a dictionary */ 124 _SCErrorSet(kSCStatusNoKey); 125 goto done; 126 } 127 128 if ((i < nElements - 1) && 129 CFDictionaryGetValueIfPresent(value, kSCResvLink, (const void **)&link)) { 130 /* 131 * if not the last path component and this 132 * element is a link 133 */ 134 CFMutableArrayRef linkElements; 135 136 if (++nLinks > MAXLINKS) { 137 /* if we are chasing our tail */ 138 _SCErrorSet(kSCStatusMaxLink); 139 goto done; 140 } 141 142 linkElements = normalizePath(link); 143 if (linkElements == NULL) { 144 /* if the link is bad */ 145 _SCErrorSet(kSCStatusNoKey); 146 goto done; 147 } 148 149 CFArrayAppendArray(linkElements, 150 elements, 151 CFRangeMake(i + 1, nElements-i - 1)); 152 CFRelease(elements); 153 elements = linkElements; 154 155 goto restart; 156 } 157 } 158 159 *entity = value; 160 ok = TRUE; 161 162 done : 163 164 CFRelease(elements); 165 return ok; 166} 167 168 169static Boolean 170setPath(SCPreferencesRef prefs, CFStringRef path, CFDictionaryRef entity) 171{ 172 CFStringRef element; 173 CFMutableArrayRef elements; 174 CFIndex i; 175 CFStringRef link; 176 CFIndex nElements; 177 CFIndex nLinks = 0; 178 CFDictionaryRef newEntity = NULL; 179 CFDictionaryRef node = NULL; 180 CFMutableArrayRef nodes = NULL; 181 Boolean ok = FALSE; 182 183 if ((entity != NULL) && !isA_CFDictionary(entity)) { 184 _SCErrorSet(kSCStatusInvalidArgument); 185 return FALSE; 186 } 187 188 elements = normalizePath(path); 189 if (elements == NULL) { 190 _SCErrorSet(kSCStatusNoKey); 191 return FALSE; 192 } 193 194 restart : 195 196 nElements = CFArrayGetCount(elements); 197 198 if (nElements < 1) { 199 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 200 201 __SCPreferencesAccess(prefs); 202 203 if (prefsPrivate->prefs != NULL) { 204 CFRelease(prefsPrivate->prefs); 205 } 206 207 if (entity == NULL) { 208 prefsPrivate->prefs = CFDictionaryCreateMutable(NULL, 209 0, 210 &kCFTypeDictionaryKeyCallBacks, 211 &kCFTypeDictionaryValueCallBacks); 212 } else { 213 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, entity); 214 } 215 216 prefsPrivate->changed = TRUE; 217 ok = TRUE; 218 goto done; 219 } 220 221 nodes = CFArrayCreateMutable(NULL, nElements - 1, &kCFTypeArrayCallBacks); 222 for (i = 0; i < nElements - 1; i++) { 223 element = CFArrayGetValueAtIndex(elements, i); 224 if (i == 0) { 225 node = SCPreferencesGetValue(prefs, element); 226 } else { 227 node = CFDictionaryGetValue(node, element); 228 229 } 230 231 if (node) { 232 /* if path component exists */ 233 CFArrayAppendValue(nodes, node); 234 } else { 235 /* if path component does not exist */ 236 node = CFDictionaryCreate(NULL, 237 NULL, 238 NULL, 239 0, 240 &kCFTypeDictionaryKeyCallBacks, 241 &kCFTypeDictionaryValueCallBacks); 242 CFArrayAppendValue(nodes, node); 243 CFRelease(node); 244 } 245 246 if (!isA_CFDictionary(node)) { 247 _SCErrorSet(kSCStatusNoKey); 248 goto done; 249 } 250 251 if ((i < nElements - 1) && 252 CFDictionaryGetValueIfPresent(node, kSCResvLink, (const void **)&link)) { 253 /* 254 * if not the last path component and this 255 * element is a link 256 */ 257 CFMutableArrayRef linkElements; 258 259 if (++nLinks > MAXLINKS) { 260 /* if we are chasing our tail */ 261 _SCErrorSet(kSCStatusMaxLink); 262 goto done; 263 } 264 265 linkElements = normalizePath(link); 266 if (linkElements == NULL) { 267 /* if the link is bad */ 268 _SCErrorSet(kSCStatusNoKey); 269 goto done; 270 } 271 272 CFArrayAppendArray(linkElements, 273 elements, 274 CFRangeMake(i + 1, nElements-i - 1)); 275 CFRelease(elements); 276 elements = linkElements; 277 278 CFRelease(nodes); 279 nodes = NULL; 280 goto restart; 281 } 282 } 283 284 /* 285 * make sure that the last component doesn't step on top 286 * of a non-dictionary component. 287 */ 288 element = CFArrayGetValueAtIndex(elements, nElements - 1); 289 if (nElements > 1) { 290 node = CFArrayGetValueAtIndex(nodes, nElements - 2); 291 node = CFDictionaryGetValue(node, element); 292 } else { 293 node = SCPreferencesGetValue(prefs, element); 294 } 295 if ((node != NULL) && !isA_CFDictionary(node)) { 296 // we won't step on a non-dictionary component 297 _SCErrorSet(kSCStatusInvalidArgument); 298 goto done; 299 } 300 301 if (entity != NULL) { 302 newEntity = CFRetain(entity); 303 } 304 for (i = nElements - 1; i >= 0; i--) { 305 element = CFArrayGetValueAtIndex(elements, i); 306 if (i == 0) { 307 if (newEntity != NULL) { 308 ok = SCPreferencesSetValue(prefs, element, newEntity); 309 } else { 310 ok = SCPreferencesRemoveValue(prefs, element); 311 } 312 } else { 313 CFMutableDictionaryRef newNode; 314 315 node = CFArrayGetValueAtIndex(nodes, i - 1); 316 newNode = CFDictionaryCreateMutableCopy(NULL, 0, node); 317 if (newEntity != NULL) { 318 CFDictionarySetValue(newNode, element, newEntity); 319 CFRelease(newEntity); 320 } else { 321 CFDictionaryRemoveValue(newNode, element); 322 if (CFDictionaryGetCount(newNode) == 0) { 323 // prune the (now empty) parent 324 CFRelease(newNode); 325 newNode = NULL; 326 } 327 } 328 newEntity = newNode; 329 } 330 } 331 if (newEntity != NULL) { 332 CFRelease(newEntity); 333 } 334 335 done : 336 337 if (nodes != NULL) CFRelease(nodes); 338 CFRelease(elements); 339 return ok; 340} 341 342 343CFStringRef 344SCPreferencesPathCreateUniqueChild(SCPreferencesRef prefs, 345 CFStringRef prefix) 346{ 347 CFStringRef child; 348 CFStringRef newPath = NULL; 349 CFDictionaryRef newDict; 350 CFUUIDRef uuid; 351 CFDictionaryRef entity; 352 353 if (prefs == NULL) { 354 /* sorry, you must provide a session */ 355 _SCErrorSet(kSCStatusNoPrefsSession); 356 return NULL; 357 } 358 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); 364 return NULL; 365 } 366 } else if (SCError() != kSCStatusNoKey) { 367 // if any error except for a missing prefix path component 368 return NULL; 369 } 370 371 uuid = CFUUIDCreate(NULL); 372 child = CFUUIDCreateString(NULL, uuid); 373 newPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, child); 374 CFRelease(child); 375 CFRelease(uuid); 376 377 /* save a new/empty dictionary */ 378 newDict = CFDictionaryCreate(NULL, 379 NULL, NULL, 0, 380 &kCFTypeDictionaryKeyCallBacks, 381 &kCFTypeDictionaryValueCallBacks); 382 assert(newDict != NULL); 383 384 if (!setPath(prefs, newPath, newDict)) { 385 CFRelease(newPath); 386 newPath = NULL; 387 } 388 CFRelease(newDict); 389 390 return newPath; 391} 392 393 394CFDictionaryRef 395SCPreferencesPathGetValue(SCPreferencesRef prefs, 396 CFStringRef path) 397{ 398 CFDictionaryRef entity; 399 CFStringRef entityLink; 400 401 if (prefs == NULL) { 402 /* sorry, you must provide a session */ 403 _SCErrorSet(kSCStatusNoPrefsSession); 404 return NULL; 405 } 406 407 if (!getPath(prefs, path, &entity)) { 408 return NULL; 409 } 410 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 */ 416 return NULL; 417 } 418 } 419 420 return entity; 421} 422 423 424CFStringRef 425SCPreferencesPathGetLink(SCPreferencesRef prefs, 426 CFStringRef path) 427{ 428 CFDictionaryRef entity; 429 CFStringRef entityLink; 430 431 if (prefs == NULL) { 432 /* sorry, you must provide a session */ 433 _SCErrorSet(kSCStatusNoPrefsSession); 434 return NULL; 435 } 436 437 if (!getPath(prefs, path, &entity)) { 438 return NULL; 439 } 440 441 if (isA_CFDictionary(entity) && 442 (CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) { 443 /* if this is a dictionary AND it is a link */ 444 return entityLink; 445 } 446 447 return NULL; 448} 449 450 451Boolean 452SCPreferencesPathSetValue(SCPreferencesRef prefs, 453 CFStringRef path, 454 CFDictionaryRef value) 455{ 456 Boolean ok; 457 458 if (prefs == NULL) { 459 /* sorry, you must provide a session */ 460 _SCErrorSet(kSCStatusNoPrefsSession); 461 return FALSE; 462 } 463 464#define NETPREF_NEEDS_REPAIR 465#ifdef NETPREF_NEEDS_REPAIR 466 if (CFEqual(path, CFSTR("/CurrentSet")) && isA_CFString(value)) { 467 static Boolean warned = FALSE; 468 if (!warned) { 469 SCPrint(TRUE, stderr, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path); 470 warned = TRUE; 471 } 472 return SCPreferencesSetValue(prefs, CFSTR("CurrentSet"), value); 473 } 474#endif // NETPREF_NEEDS_REPAIR 475 476 if (!isA_CFDictionary(value)) { 477#ifdef NETPREF_NEEDS_REPAIR 478SCPrint(TRUE, stderr, CFSTR("SCPreferencesPathSetValue(, %@, ) called with non-dictionary value\n"), path); 479#endif // NETPREF_NEEDS_REPAIR 480 _SCErrorSet(kSCStatusInvalidArgument); 481 return FALSE; 482 } 483 484 ok = setPath(prefs, path, value); 485 return ok; 486} 487 488 489Boolean 490SCPreferencesPathSetLink(SCPreferencesRef prefs, 491 CFStringRef path, 492 CFStringRef link) 493{ 494 CFMutableDictionaryRef dict; 495 CFDictionaryRef entity; 496 Boolean ok; 497 498 if (prefs == NULL) { 499 /* sorry, you must provide a session */ 500 _SCErrorSet(kSCStatusNoPrefsSession); 501 return FALSE; 502 } 503 504 if (!isA_CFString(link)) { 505 _SCErrorSet(kSCStatusInvalidArgument); 506 return FALSE; 507 } 508 509 if (!getPath(prefs, link, &entity)) { 510 // if bad link 511 return FALSE; 512 } 513 514 dict = CFDictionaryCreateMutable(NULL, 515 0, 516 &kCFTypeDictionaryKeyCallBacks, 517 &kCFTypeDictionaryValueCallBacks); 518 CFDictionarySetValue(dict, kSCResvLink, link); 519 ok = setPath(prefs, path, dict); 520 CFRelease(dict); 521 522 return ok; 523} 524 525 526Boolean 527SCPreferencesPathRemoveValue(SCPreferencesRef prefs, 528 CFStringRef path) 529{ 530 CFMutableArrayRef elements = NULL; 531 Boolean ok = FALSE; 532 CFDictionaryRef value; 533 534 if (prefs == NULL) { 535 /* sorry, you must provide a session */ 536 _SCErrorSet(kSCStatusNoPrefsSession); 537 return FALSE; 538 } 539 540 if (!getPath(prefs, path, &value)) { 541 // if no such path 542 return FALSE; 543 } 544 545 elements = normalizePath(path); 546 if (elements == NULL) { 547 _SCErrorSet(kSCStatusNoKey); 548 return FALSE; 549 } 550 551 ok = setPath(prefs, path, NULL); 552 553 CFRelease(elements); 554 return ok; 555} 556