dsutil.c   [plain text]


/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include <NetInfo/dsrecord.h>
#include <NetInfo/dsutil.h>
#include <NetInfo/dsx500.h>
#include <NetInfo/utf-8.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

char *
copyString(char *s)
{
	int len;
	char *t;

	if (s == NULL) return NULL;

	len = strlen(s) + 1;
	t = malloc(len);
	memmove(t, s, len);
	return t;
}

char *
concatString(char *s, char *t)
{
	int len;

	if (t == NULL) return s;

	len = strlen(s) + strlen(t) + 1;
	s = realloc(s, len);
	strcat(s, t);
	return s;
}

char **
insertString(char *s, char **l, unsigned int x)
{
	int i, len;

	if (s == NULL) return l;
	if (l == NULL) 
	{
		l = (char **)malloc(2 * sizeof(char *));
		l[0] = copyString(s);
		l[1] = NULL;
		return l;
	}

	for (i = 0; l[i] != NULL; i++);
	len = i + 1; /* count the NULL on the end of the list too! */

	l = (char **)realloc(l, (len + 1) * sizeof(char *));

	if ((x >= (len - 1)) || (x == IndexNull))
	{
		l[len - 1] = copyString(s);
		l[len] = NULL;
		return l;
	}

	for (i = len; i > x; i--) l[i] = l[i - 1];
	l[x] = copyString(s);
	return l;
}

char **
appendString(char *s, char **l)
{
	return insertString(s, l, IndexNull);
}

void
freeList(char **l)
{
	int i;

	if (l == NULL) return;
	for (i = 0; l[i] != NULL; i++)
	{
		if (l[i] != NULL) free(l[i]);
		l[i] = NULL;
	}
	free(l);
}

void
freeString(char *s)
{
	if (s == NULL) return;
	free(s);
}

unsigned int
listLength(char **l)
{
	int i;

	if (l == NULL) return 0;
	for (i = 0; l[i] != NULL; i++);
	return i;
}

unsigned int
listIndex(char *s, char **l)
{
	int i;

	if (l == NULL) return IndexNull;
	for (i = 0; l[i] != NULL; i++)
	{
		if (strcmp(s, l[i]) == 0) return i;
	}
	return IndexNull;
}

char *
prefix(char *s, char c)
{
	int i;
	char *t;

	if (s == NULL) return NULL;

	for (i = 0; ((s[i] != '\0') && (s[i] != c)); i++);
	if (i == 0) return NULL;
	if (s[i] == '\0') return copyString(s);

	t = malloc(i + 1);
	bcopy(s, t, i);
	t[i] = '\0';
	return t;
}

char *
postfix(char *s, char c)
{
	int i, len;
	char *t;

	if (s == NULL) return NULL;

	for (i = 0; ((s[i] != '\0') && (s[i] != c)); i++);
	if (s[i] == '\0') return NULL;
	len = strlen(s) - i;
	if (len == 1) return NULL;

	t = malloc(len);
	len--;
	bcopy((s + i + 1), t, len);
	t[len] = '\0';
	return t;
}
	
char *
presuffix(char *s, char c)
{
	int i, len;
	char *t;

	if (s == NULL) return NULL;

	len = strlen(s);
	for (i = len - 1; ((i >= 0) && (s[i] != c)); i--);
	if (i == 0) return NULL;
	if (s[0] == '\0') return NULL;

	t = malloc(i + 1);
	bcopy(s, t, i);
	t[i] = '\0';
	return t;
}

char *
suffix(char *s, char c)
{
	int i, len;
	char *t;

	if (s == NULL) return NULL;

	len = strlen(s);
	for (i = len - 1; ((i >= 0) && (s[i] != c)); i--);
	if (i == 0) return NULL;
	len -= i;
	if (len == 1) return NULL;
	t = malloc(len);
	len--;
	bcopy((s + i + 1), t, len);
	t[len] = '\0';
	return t;
}

char *
lowerCase(char *s)
{
	int i;
	char *t;

	if (s == NULL) return NULL;
	t = malloc(strlen(s) + 1);

	for (i = 0; s[i] != '\0'; i++) 
	{
		if ((s[i] >= 'A') && (s[i] <= 'Z')) t[i] = s[i] + 32;
		else t[i] = s[i];
	}
	t[i] = '\0';
	return t;
}

unsigned int
stringIndex(char c, char *s)
{
	int i;
	char *p;

	if (s == NULL) return IndexNull;

	for (i = 0, p = s; p[0] != '\0'; p++, i++)
	{
		if (p[0] == c) return i;
	}

	return IndexNull;
}

char **
explode(char *s, char *delim)
{
	char **l = NULL;
	char *p, *t;
	int i, n;

	if (s == NULL) return NULL;

	p = s;
	while (p[0] != '\0')
	{
		for (i = 0; ((p[i] != '\0') && (stringIndex(p[i], delim) == IndexNull)); i++);
		n = i;
		t = malloc(n + 1);
		for (i = 0; i < n; i++) t[i] = p[i];
		t[n] = '\0';
		l = appendString(t, l);
		free(t);
		t = NULL;
		if (p[i] == '\0') return l;
		if (p[i + 1] == '\0') l = appendString("", l);
		p = p + i + 1;
	}
	return l;
}

char *
itoa(int n)
{
	char s[64];

	sprintf(s, "%d", n);
	return copyString(s);
}

dsrecord *
dsutil_parse_netinfo_string_path(char *path)
{
	dsrecord *p;
	char *c, *s, *eq;
	u_int32_t i, n, m;
	dsdata *k, *v;
	dsattribute *a;
	
	if (path == NULL) return NULL;
	
	p = dsrecord_new();
	
	c = path;

	/* Skip leading slashes */
	while (c[0] == '/') c++;

	while (c[0] != '\0')
	{
		/* find the next slash (skip escaped characters) */
		for (i = 0; c[i] != '\0'; i++)
		{
			if (c[i] == '\\')
			{
				if (c [i+1] == '\0') c[i] = '\0';
				else i++;
				continue;
			}

			if (c[i] == '/') break;
		}

		if (i == 0) break;

		s = malloc(i + 1);
		m = 0;
		eq = NULL;

		for (n = 0; n < i; n++)
		{
			if (c[n] == '\\')
			{
				if (c[n+1] == '\\') s[m++] = c[n];
				continue;
			}

			if (c[n] == '=') eq = s + m;
			s[m++] = c[n];
		}
		s[m] = '\0';

		if (eq != NULL)
		{
			*eq = '\0';
			k = cstring_to_dsdata(s);
			v = cstring_to_dsdata(eq+1);
		}
		else
		{
			k = cstring_to_dsdata("name");
			v = cstring_to_dsdata(s);
		}
		free(s);

		a = dsattribute_new(k);
		dsattribute_append(a, v);
		dsdata_release(k);
		dsdata_release(v);

		dsrecord_append_attribute(p, a, SELECT_ATTRIBUTE);
		dsattribute_release(a);

		c += i;
		while (c[0] == '/') c++;
	}

	return p;
}

/*
 * Escape characters in an RDN.
 */
char *
escape_rdn(dsdata *dsrdn)
{
	char *p, *q;
	u_int32_t escapes = 0;
	char *rdn;
	char *escaped;

	if (dsrdn == NULL) return copyString("");
	if (dsrdn->length == 0) return copyString("");

	rdn = dsrdn->data;
	if (IsStringDataType(dsrdn->type) == 0) return copyString("");

	for (p = rdn; *p != '\0'; DSUTIL_UTF8_INCR(p))
	{
		if (NeedEscapeAVA(*p)) escapes++;
	}

	if (escapes == 0) return copyString(rdn);

	escaped = (char *)malloc(escapes + dsrdn->length);
	q = escaped;

	for (p = rdn; *p != '\0'; p++)
	{
		if (NeedEscapeAVA(*p)) *q++ = '\\';
		*q++ = *p;
	}
	*q = '\0';

	return escaped;
}

dsstatus
unescape_rdn(char *rdn, dsdata **key, dsdata **value)
{
	char *p, *q;
	u_int32_t in_escape = 0;
	dsstatus status = DSStatusOK;
	enum { RDNKey, RDNValue } sel;
	u_int32_t len, offset;

	sel = RDNKey;
	len = strlen(rdn);

	*key = dsdata_alloc(len + 1);
	if ((*key == NULL) || ((*key)->data == NULL))
	{
		return DSStatusFailed;
	}

	(*key)->type = DataTypeCaseUTF8Str;
	(*key)->retain = 1;

	*value = dsdata_alloc(len + 1);
	if ((*value == NULL) || ((*value)->data == NULL))
	{
		dsdata_release(*key);
		return DSStatusFailed;
	}

	(*value)->type = DataTypeCaseUTF8Str;
	(*value)->retain = 1;

	q = (*key)->data;
	p = rdn;
	offset = 0;
	len = 0;

	do
	{
		p += offset;
		offset = 1;

		if (p[0] == '\\' && p[1] != '\0')
		{
			in_escape = 1;
		}
		else
		{
			if (in_escape == 0)
			{
				if (*p == '+')
				{
					/*
					 * Unescaped '+' signs signify multi-
					 * valued RDNs which are unsupported.
					 */
					status = DSStatusInvalidPath;
					break;
				}
				else if ((*p == '=') && (sel == RDNKey))
				{
					/*
					 * An unescaped '=' sign means move onto
					 * the RHS of the RDN (the value).
					 */
					(*key)->length = len + 1;
					*q = '\0';
					len = 0;

					sel = RDNValue;
					q = (*value)->data;
					p++; /* over equals */
				}
			}
			else
			{
				/*
				 * Do not unescape unrecognised escape sequences.
				 */
				if (NeedEscapeAVA(*p) == 0)
				{
					*q++ = '\\';
					len++;
				}
				in_escape = 0;
			}
			*q++ = *p;
			len++;
		}
	} while (*p != '\0');

	(*value)->length = len;
	*q = '\0';

	if (status != DSStatusOK)
	{
		dsdata_release(*value);
		dsdata_release(*key);
	}

	return status;
}

/*
 * Parse a distinguished name into a hierarchical name.
 * Multi-valued RDNs are not supported.
 */
dsrecord *
dsutil_parse_x500_string_path(char *path)
{
	dsrecord *r;
	char **exploded;
	u_int32_t	max;
	int i;
	dsdata *rdnKey, *rdnValue;
	dsattribute *a;

	if (path == NULL) return NULL;

	exploded = dsx500_explode_dn(path, 0);
	if (exploded == NULL) return NULL;

	r = dsrecord_new();
	max = listLength(exploded);
	if (max > 0) max--;

	for (i = max; i >= 0; --i)
	{
		if (unescape_rdn(exploded[i], &rdnKey, &rdnValue) != DSStatusOK)
		{
			freeList(exploded);
			dsrecord_release(r);
			return NULL;
		}

		a = dsattribute_new(rdnKey);
		dsattribute_append(a, rdnValue);
		dsdata_release(rdnKey);
		dsdata_release(rdnValue);

		dsrecord_append_attribute(r, a, SELECT_ATTRIBUTE);
		dsattribute_release(a);
	}

	freeList(exploded);

	return r;
}

/*
 * Create an attribute with a cstring key
 * and a variable number of cstring args.
 * The list of values MUST end with a NULL.
 */
dsattribute *
dsattribute_from_cstrings(char *key, ...)
{
	dsattribute *a;
	dsdata *d;
	char *s;
	va_list ap;

	if (key == NULL) return NULL;

	d = cstring_to_dsdata(key);
	a = dsattribute_new(d);
	dsdata_release(d);

	va_start(ap, key);

	while (NULL != (s = va_arg(ap, char *)))
	{
		d = cstring_to_dsdata(s);
		dsattribute_append(a, d);
		dsdata_release(d);
	}

	va_end(ap);
	
	return a;
}

void
dsattribute_append_cstring_value(dsattribute *a, char *v)
{
	dsdata *d;

	if (a == NULL) return;
	if (v == NULL) return;

	d = cstring_to_dsdata(v);
	dsattribute_append(a, d);
	dsdata_release(d);
}

void
dsattribute_merge_cstring_value(dsattribute *a, char *v)
{
	dsdata *d;

	if (a == NULL) return;
	if (v == NULL) return;

	d = cstring_to_dsdata(v);
	dsattribute_merge(a, d);
	dsdata_release(d);
}

void
dsattribute_remove_cstring_value(dsattribute *a, char *v)
{
	dsdata *d;
	u_int32_t i;

	if (a == NULL) return;
	if (v == NULL) return;

	d = cstring_to_dsdata(v);
	i = dsattribute_index(a, d);
	if (i != IndexNull) dsattribute_remove(a, i);
	dsdata_release(d);
}

dsattribute *
dsrecord_cstring_attribute(dsrecord *r, char *k)
{
	dsdata *d;
	dsattribute *a;

	if (r == NULL) return NULL;
	if (k == NULL) return NULL;

	d = cstring_to_dsdata(k);
	a = dsrecord_attribute(r, d, SELECT_ATTRIBUTE);
	dsdata_release(d);
	return a;
}

dsattribute *
dsrecord_cstring_meta_attribute(dsrecord *r, char *k)
{
	dsdata *d;
	dsattribute *a;

	if (r == NULL) return NULL;
	if (k == NULL) return NULL;

	d = cstring_to_dsdata(k);
	a = dsrecord_attribute(r, d, SELECT_META_ATTRIBUTE);
	dsdata_release(d);
	return a;
}

void
dsrecord_merge_cstring_attribute(dsrecord *r, char *k, ...)
{
	dsdata *d;
	dsattribute *a;
	char *s;
	va_list ap;

	if (r == NULL) return;
	if (k == NULL) return;

	d = cstring_to_dsdata(k);
	a = dsrecord_attribute(r, d, SELECT_ATTRIBUTE);
	if (a == NULL)
	{
		a = dsattribute_new(d);
		dsrecord_append_attribute(r, a, SELECT_ATTRIBUTE);
	}

	dsdata_release(d);

	va_start(ap, k);

	while (NULL != (s = va_arg(ap, char *)))
	{
		d = cstring_to_dsdata(s);
		dsattribute_merge(a, d);
		dsdata_release(d);
	}

	va_end(ap);

	dsattribute_release(a);
}