#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPrivate.h>
#include "SCPreferencesInternal.h"
#define MAXLINKS 8
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 Boolean
getPath(SCPreferencesRef session, CFStringRef path, CFDictionaryRef *entity)
{
CFStringRef element;
CFArrayRef elements;
CFIndex i;
CFStringRef link;
CFIndex nElements;
CFIndex nLinks = 0;
Boolean ok = FALSE;
SCPreferencesPrivateRef sessionPrivate = (SCPreferencesPrivateRef)session;
CFDictionaryRef value = NULL;
elements = normalizePath(path);
if (elements == NULL) {
_SCErrorSet(kSCStatusNoKey);
return FALSE;
}
restart :
nElements = CFArrayGetCount(elements);
for (i = 0; i < nElements; i++) {
element = CFArrayGetValueAtIndex(elements, i);
if (i == 0) {
sessionPrivate->accessed = TRUE;
value = CFDictionaryGetValue(sessionPrivate->prefs,
CFArrayGetValueAtIndex(elements, 0));
} else {
value = CFDictionaryGetValue(value, element);
}
if (value == NULL) {
_SCErrorSet(kSCStatusNoKey);
goto done;
}
if (!isA_CFDictionary(value)) {
_SCErrorSet(kSCStatusNoKey);
goto done;
}
if ((i < nElements-1) &&
CFDictionaryGetValueIfPresent(value, kSCResvLink, (const void **)&link)) {
CFArrayRef linkElements;
CFMutableArrayRef newElements;
if (++nLinks > MAXLINKS) {
_SCErrorSet(kSCStatusMaxLink);
goto done;
}
linkElements = normalizePath(link);
if (linkElements == NULL) {
_SCErrorSet(kSCStatusNoKey);
goto done;
}
newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
CFArrayAppendArray(newElements,
elements,
CFRangeMake(i+1, nElements-i-1));
CFRelease(elements);
elements = newElements;
goto restart;
}
}
*entity = value;
ok = TRUE;
done :
CFRelease(elements);
return ok;
}
static Boolean
setPath(SCPreferencesRef session, CFStringRef path, CFDictionaryRef entity)
{
CFStringRef element;
CFArrayRef elements;
CFIndex i;
CFStringRef link;
CFIndex nElements;
CFIndex nLinks = 0;
CFDictionaryRef newEntity = NULL;
CFDictionaryRef node = NULL;
CFMutableArrayRef nodes;
Boolean ok = FALSE;
SCPreferencesPrivateRef sessionPrivate = (SCPreferencesPrivateRef)session;
elements = normalizePath(path);
if (elements == NULL) {
_SCErrorSet(kSCStatusNoKey);
return FALSE;
}
restart :
nElements = CFArrayGetCount(elements);
nodes = CFArrayCreateMutable(NULL, nElements-1, &kCFTypeArrayCallBacks);
for (i = 0; i < nElements - 1; i++) {
element = CFArrayGetValueAtIndex(elements, i);
if (i == 0) {
sessionPrivate->accessed = TRUE;
node = CFDictionaryGetValue(sessionPrivate->prefs, element);
} else {
node = CFDictionaryGetValue(node, element);
}
if (node) {
CFArrayAppendValue(nodes, node);
} else {
node = CFDictionaryCreate(NULL,
NULL,
NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFArrayAppendValue(nodes, node);
CFRelease(node);
}
if (!isA_CFDictionary(node)) {
_SCErrorSet(kSCStatusNoKey);
goto done;
}
if ((i < nElements-1) &&
CFDictionaryGetValueIfPresent(node, kSCResvLink, (const void **)&link)) {
CFArrayRef linkElements;
CFMutableArrayRef newElements;
if (++nLinks > MAXLINKS) {
_SCErrorSet(kSCStatusMaxLink);
goto done;
}
linkElements = normalizePath(link);
if (linkElements == NULL) {
_SCErrorSet(kSCStatusNoKey);
goto done;
}
newElements = CFArrayCreateMutableCopy(NULL, 0, linkElements);
CFArrayAppendArray(newElements,
elements,
CFRangeMake(i+1, nElements-i-1));
CFRelease(elements);
elements = newElements;
CFRelease(nodes);
goto restart;
}
}
if (entity) {
newEntity = CFRetain(entity);
}
for (i = nElements - 1; i >= 0; i--) {
element = CFArrayGetValueAtIndex(elements, i);
if (i == 0) {
if (newEntity) {
CFDictionarySetValue(sessionPrivate->prefs, element, newEntity);
} else {
CFDictionaryRemoveValue(sessionPrivate->prefs, element);
}
sessionPrivate->changed = TRUE;
ok = TRUE;
} else {
CFMutableDictionaryRef newNode;
node = CFArrayGetValueAtIndex(nodes, i-1);
newNode = CFDictionaryCreateMutableCopy(NULL, 0, node);
if (newEntity) {
CFDictionarySetValue(newNode, element, newEntity);
CFRelease(newEntity);
} else {
CFDictionaryRemoveValue(newNode, element);
}
newEntity = newNode;
}
}
if (newEntity) {
CFRelease(newEntity);
}
done :
CFRelease(nodes);
CFRelease(elements);
return ok;
}
CFStringRef
SCPreferencesPathCreateUniqueChild(SCPreferencesRef session,
CFStringRef prefix)
{
CFStringRef child;
CFStringRef newPath = NULL;
CFMutableDictionaryRef newDict = NULL;
CFUUIDRef uuid;
CFDictionaryRef entity;
if (_sc_verbose) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathCreateUniqueChild:"));
SCLog(TRUE, LOG_DEBUG, CFSTR(" prefix = %@"), prefix);
}
if (getPath(session, prefix, &entity)) {
if (CFDictionaryContainsKey(entity, kSCResvLink)) {
_SCErrorSet(kSCStatusFailed);
return NULL;
}
} else if (SCError() != kSCStatusNoKey) {
return NULL;
}
uuid = CFUUIDCreate(NULL);
child = CFUUIDCreateString(NULL, uuid);
newPath = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@/%@"), prefix, child);
CFRelease(child);
CFRelease(uuid);
newDict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (setPath(session, newPath, newDict)) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" child = %@"), newPath);
} else {
CFRelease(newPath);
newPath = NULL;
}
CFRelease(newDict);
return newPath;
}
CFDictionaryRef
SCPreferencesPathGetValue(SCPreferencesRef session,
CFStringRef path)
{
CFDictionaryRef entity;
CFStringRef entityLink;
if (_sc_verbose) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathGetValue:"));
SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
}
if (!getPath(session, path, &entity)) {
return NULL;
}
if (isA_CFDictionary(entity) &&
(CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
if (!getPath(session, entityLink, &entity)) {
return NULL;
}
}
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" value = %@"), entity);
return entity;
}
CFStringRef
SCPreferencesPathGetLink(SCPreferencesRef session,
CFStringRef path)
{
CFDictionaryRef entity;
CFStringRef entityLink;
if (_sc_verbose) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathGetLink:"));
SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
}
if (!getPath(session, path, &entity)) {
return NULL;
}
if (isA_CFDictionary(entity) &&
(CFDictionaryGetValueIfPresent(entity, kSCResvLink, (const void **)&entityLink))) {
SCLog(_sc_verbose, LOG_DEBUG, CFSTR(" link = %@"), entityLink);
return entityLink;
}
return NULL;
}
Boolean
SCPreferencesPathSetValue(SCPreferencesRef session,
CFStringRef path,
CFDictionaryRef value)
{
Boolean ok;
if (_sc_verbose) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathSetValue:"));
SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
SCLog(TRUE, LOG_DEBUG, CFSTR(" value = %@"), value);
}
if (!value) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
ok = setPath(session, path, value);
return ok;
}
Boolean
SCPreferencesPathSetLink(SCPreferencesRef session,
CFStringRef path,
CFStringRef link)
{
CFMutableDictionaryRef dict;
CFDictionaryRef entity;
Boolean ok;
if (_sc_verbose) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathSetLink:"));
SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
SCLog(TRUE, LOG_DEBUG, CFSTR(" link = %@"), link);
}
if (!link) {
_SCErrorSet(kSCStatusInvalidArgument);
return FALSE;
}
if (!getPath(session, link, &entity)) {
return FALSE;
}
dict = CFDictionaryCreateMutable(NULL,
0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionaryAddValue(dict, kSCResvLink, link);
ok = setPath(session, path, dict);
CFRelease(dict);
return ok;
}
Boolean
SCPreferencesPathRemoveValue(SCPreferencesRef session,
CFStringRef path)
{
CFArrayRef elements = NULL;
Boolean ok = FALSE;
CFDictionaryRef value;
if (_sc_verbose) {
SCLog(TRUE, LOG_DEBUG, CFSTR("SCPreferencesPathRemoveValue:"));
SCLog(TRUE, LOG_DEBUG, CFSTR(" path = %@"), path);
}
if (!getPath(session, path, &value)) {
return FALSE;
}
elements = normalizePath(path);
if (elements == NULL) {
_SCErrorSet(kSCStatusNoKey);
return FALSE;
}
ok = setPath(session, path, NULL);
CFRelease(elements);
return ok;
}