#include <SystemConfiguration/SCD.h>
#include <SystemConfiguration/SCP.h>
#include "SCPPrivate.h"
#include <SystemConfiguration/SCPreferences.h>
#include <SystemConfiguration/SCPPath.h>
static CFArrayRef
normalizePath(CFStringRef path)
{
CFArrayRef tmpElements;
CFMutableArrayRef elements;
CFIndex nElements;
CFIndex i;
if (!CFStringHasPrefix(path, CFSTR("/"))) {
return NULL;
}
tmpElements = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
elements = CFArrayCreateMutableCopy(NULL, 0, tmpElements);
CFRelease(tmpElements);
nElements = CFArrayGetCount(elements);
for (i=nElements; i>0; i--) {
CFStringRef pathElement;
pathElement = CFArrayGetValueAtIndex(elements, i-1);
if (CFStringGetLength(pathElement) == 0) {
CFArrayRemoveValueAtIndex(elements, i-1);
nElements--;
}
}
if (nElements < 1) {
CFRelease(elements);
return NULL;
}
return elements;
}
static SCPStatus
getPath(SCPSessionRef session, CFStringRef path, CFMutableDictionaryRef *entity)
{
CFArrayRef elements;
CFIndex i;
CFIndex nElements;
SCPStatus status;
CFMutableDictionaryRef value = NULL;
if (session == NULL) {
return SCP_NOSESSION;
}
elements = normalizePath(path);
if (elements == NULL) {
return SCP_NOKEY;
}
status = SCPGet(session,
CFArrayGetValueAtIndex(elements, 0),
(CFPropertyListRef *)&value);
if (status != SCP_OK) {
goto done;
}
if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
status = SCP_NOKEY;
goto done;
}
nElements = CFArrayGetCount(elements);
for (i=1; i<nElements; i++) {
CFStringRef element;
element = CFArrayGetValueAtIndex(elements, i);
value = (CFMutableDictionaryRef)CFDictionaryGetValue(value, element);
if (value == NULL) {
status = SCP_NOKEY;
goto done;
}
if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
status = SCP_NOKEY;
goto done;
}
}
*entity = value;
status = SCP_OK;
done :
CFRelease(elements);
return status;
}
SCPStatus
SCPPathCreateUniqueChild(SCPSessionRef session,
CFStringRef prefix,
CFStringRef *newPath)
{
SCPStatus status;
CFMutableDictionaryRef value;
boolean_t newValue = FALSE;
CFIndex i;
CFStringRef path;
CFMutableDictionaryRef newDict;
if (session == NULL) {
return SCP_NOSESSION;
}
status = getPath(session, prefix, &value);
switch (status) {
case SCP_OK :
break;
case SCP_NOKEY :
value = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
newValue = TRUE;
break;
default :
return status;
}
if (CFGetTypeID(value) != CFDictionaryGetTypeID()) {
status = SCP_NOKEY;
goto done;
}
if (CFDictionaryContainsKey(value, kSCResvLink)) {
status = SCP_FAILED;
goto done;
}
i = 0;
while (TRUE) {
CFStringRef pathComponent;
Boolean found;
pathComponent = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), i);
found = CFDictionaryContainsKey(value, pathComponent);
CFRelease(pathComponent);
if (!found) {
path = CFStringCreateWithFormat(NULL,
NULL,
CFSTR("%@/%i"),
prefix,
i);
break;
}
i++;
}
newDict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
status = SCPPathSetValue(session, path, newDict);
CFRelease(newDict);
if (status != SCP_OK) {
CFRelease(path);
goto done;
}
*newPath = path;
done :
if (newValue) CFRelease(value);
return status;
}
SCPStatus
SCPPathGetValue(SCPSessionRef session,
CFStringRef path,
CFDictionaryRef *value)
{
SCPStatus status;
CFMutableDictionaryRef entity;
CFStringRef entityLink;
if (session == NULL) {
return SCP_NOSESSION;
}
status = getPath(session, path, &entity);
if (status != SCP_OK) {
return status;
}
if ((CFGetTypeID(entity) == CFDictionaryGetTypeID()) &&
(CFDictionaryGetValueIfPresent(entity, kSCResvLink, (void **)&entityLink))) {
status = getPath(session, entityLink, &entity);
if (status != SCP_OK) {
return status;
}
}
*value = entity;
return status;
}
SCPStatus
SCPPathGetLink(SCPSessionRef session,
CFStringRef path,
CFStringRef *link)
{
SCPStatus status;
CFMutableDictionaryRef entity;
CFStringRef entityLink;
if (session == NULL) {
return SCP_NOSESSION;
}
status = getPath(session, path, &entity);
if (status != SCP_OK) {
return status;
}
if ((CFGetTypeID(entity) == CFDictionaryGetTypeID()) &&
(CFDictionaryGetValueIfPresent(entity, kSCResvLink, (void **)&entityLink))) {
*link = entityLink;
return status;
}
return SCP_NOKEY;
}
SCPStatus
SCPPathSetValue(SCPSessionRef session, CFStringRef path, CFDictionaryRef value)
{
CFMutableDictionaryRef element;
CFArrayRef elements = NULL;
CFIndex i;
CFIndex nElements;
boolean_t newRoot = FALSE;
CFMutableDictionaryRef root = NULL;
SCPStatus status = SCP_NOKEY;
if (session == NULL) {
return SCP_NOSESSION;
}
elements = normalizePath(path);
if (elements == NULL) {
return SCP_NOKEY;
}
status = SCPGet(session,
CFArrayGetValueAtIndex(elements, 0),
(CFPropertyListRef *)&root);
if (status != SCP_OK) {
root = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
newRoot = TRUE;
}
nElements = CFArrayGetCount(elements);
if (nElements == 1) {
if (newRoot) {
CFRelease(root);
newRoot = FALSE;
}
root = (CFMutableDictionaryRef)value;
}
element = root;
for (i=1; i<nElements-1; i++) {
CFStringRef pathComponent;
CFMutableDictionaryRef tmpElement;
pathComponent = CFArrayGetValueAtIndex(elements, i);
tmpElement = (void *)CFDictionaryGetValue(element, pathComponent);
if (tmpElement == NULL) {
tmpElement = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(element, pathComponent, tmpElement);
CFRelease(tmpElement);
}
element = tmpElement;
}
if (nElements > 1) {
CFDictionarySetValue(element,
CFArrayGetValueAtIndex(elements, nElements-1),
value);
}
status = SCPSet(session, CFArrayGetValueAtIndex(elements, 0), root);
if (newRoot) CFRelease(root);
CFRelease(elements);
return status;
}
SCPStatus
SCPPathSetLink(SCPSessionRef session, CFStringRef path, CFStringRef link)
{
CFMutableDictionaryRef dict;
SCPStatus status;
if (session == NULL) {
return SCP_NOSESSION;
}
dict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(dict, kSCResvLink, link);
status = SCPPathSetValue(session, path, dict);
CFRelease(dict);
return status;
}
SCPStatus
SCPPathRemove(SCPSessionRef session, CFStringRef path)
{
CFMutableDictionaryRef element;
CFArrayRef elements = NULL;
CFIndex i;
CFIndex nElements;
CFMutableDictionaryRef root = NULL;
SCPStatus status = SCP_NOKEY;
if (session == NULL) {
return SCP_NOSESSION;
}
elements = normalizePath(path);
if (elements == NULL) {
return SCP_NOKEY;
}
status = SCPGet(session,
CFArrayGetValueAtIndex(elements, 0),
(CFPropertyListRef *)&root);
if (status != SCP_OK) {
goto done;
}
nElements = CFArrayGetCount(elements);
if (nElements == 1) {
status = SCPRemove(session, CFArrayGetValueAtIndex(elements, 0));
goto done;
}
element = root;
for (i=1; i<nElements-1; i++) {
CFStringRef pathComponent;
CFMutableDictionaryRef tmpElement;
pathComponent = CFArrayGetValueAtIndex(elements, i);
tmpElement = (void *)CFDictionaryGetValue(element, pathComponent);
if (tmpElement == NULL) {
status = SCP_NOKEY;
goto done;
}
element = tmpElement;
}
CFDictionaryRemoveValue(element,
CFArrayGetValueAtIndex(elements, nElements-1));
status = SCPSet(session, CFArrayGetValueAtIndex(elements, 0), root);
done :
CFRelease(elements);
return status;
}