diff options
Diffstat (limited to 'common/sdp-xml.c')
| -rw-r--r-- | common/sdp-xml.c | 793 | 
1 files changed, 793 insertions, 0 deletions
diff --git a/common/sdp-xml.c b/common/sdp-xml.c new file mode 100644 index 00000000..1e1e07c3 --- /dev/null +++ b/common/sdp-xml.c @@ -0,0 +1,793 @@ +/* + * + *  BlueZ - Bluetooth protocol stack for Linux + * + *  Copyright (C) 2005-2008  Marcel Holtmann <marcel@holtmann.org> + * + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> + +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include "logging.h" +#include "sdp-xml.h" + +#define STRBUFSIZE 1024 +#define MAXINDENT 64 + +static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level, +		void *data, void (*appender)(void *, const char *)) +{ +	int i, hex; +	char buf[STRBUFSIZE]; +	char indent[MAXINDENT]; +	char next_indent[MAXINDENT]; + +	if (!value) +		return; + +	if (indent_level >= MAXINDENT) +		indent_level = MAXINDENT - 2; + +	for (i = 0; i < indent_level; i++) { +		indent[i] = '\t'; +		next_indent[i] = '\t'; +	} + +	indent[i] = '\0'; +	next_indent[i] = '\t'; +	next_indent[i + 1] = '\0'; + +	buf[STRBUFSIZE - 1] = '\0'; + +	switch (value->dtd) { +	case SDP_DATA_NIL: +		appender(data, indent); +		appender(data, "<nil/>\n"); +		break; + +	case SDP_BOOL: +		appender(data, indent); +		appender(data, "<boolean value=\""); +		appender(data, value->val.uint8 ? "true" : "false"); +		appender(data, "\" />\n"); +		break; + +	case SDP_UINT8: +		appender(data, indent); +		appender(data, "<uint8 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "0x%02x", value->val.uint8); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_UINT16: +		appender(data, indent); +		appender(data, "<uint16 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uint16); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_UINT32: +		appender(data, indent); +		appender(data, "<uint32 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uint32); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_UINT64: +		appender(data, indent); +		appender(data, "<uint64 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "0x%016jx", value->val.uint64); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_UINT128: +		appender(data, indent); +		appender(data, "<uint128 value=\""); + +		for (i = 0; i < 16; i++) { +			sprintf(&buf[i * 2], "%02x", +				(unsigned char) value->val.uint128.data[i]); +		} + +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_INT8: +		appender(data, indent); +		appender(data, "<int8 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int8); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_INT16: +		appender(data, indent); +		appender(data, "<int16 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int16); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_INT32: +		appender(data, indent); +		appender(data, "<int32 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int32); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_INT64: +		appender(data, indent); +		appender(data, "<int64 value=\""); +		snprintf(buf, STRBUFSIZE - 1, "%jd", value->val.int64); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_INT128: +		appender(data, indent); +		appender(data, "<int128 value=\""); + +		for (i = 0; i < 16; i++) { +			sprintf(&buf[i * 2], "%02x", +				(unsigned char) value->val.int128.data[i]); +		} +		appender(data, buf); + +		appender(data, "\" />\n"); +		break; + +	case SDP_UUID16: +		appender(data, indent); +		appender(data, "<uuid value=\""); +		snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uuid.value.uuid16); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_UUID32: +		appender(data, indent); +		appender(data, "<uuid value=\""); +		snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uuid.value.uuid32); +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_UUID128: +		appender(data, indent); +		appender(data, "<uuid value=\""); + +		snprintf(buf, STRBUFSIZE - 1, +			 "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[0], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[1], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[2], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[3], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[4], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[5], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[6], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[7], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[8], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[9], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[10], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[11], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[12], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[13], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[14], +			 (unsigned char) value->val.uuid.value. +			 uuid128.data[15]); + +		appender(data, buf); +		appender(data, "\" />\n"); +		break; + +	case SDP_TEXT_STR8: +	case SDP_TEXT_STR16: +	case SDP_TEXT_STR32: +	{ +		hex = 0; + +		int num_chars_to_escape = 0; + +		for (i = 0; i < value->unitSize; i++) { +			if (i == (value->unitSize - 1) +			    && value->val.str[i] == '\0') +				break; +			if (!isprint(value->val.str[i])) { +				hex = 1; +				break; +			} + +			/* XML is evil, must do this... */ +			if ((value->val.str[i] == '<') || +					(value->val.str[i] == '>') || +					(value->val.str[i] == '"') || +					(value->val.str[i] == '&')) +				num_chars_to_escape++; +		} + +		appender(data, indent); + +		appender(data, "<text "); + +		char *strBuf = 0; + +		if (hex) { +			appender(data, "encoding=\"hex\" "); +			strBuf = (char *) malloc(sizeof(char) +						 * ((value->unitSize-1) * 2 + 1)); + +			/* Unit Size seems to include the size for dtd +			   It is thus off by 1 +			   This is safe for Normal strings, but not +			   hex encoded data */ +			for (i = 0; i < (value->unitSize-1); i++) +				sprintf(&strBuf[i*sizeof(char)*2], +					"%02x", +					(unsigned char) value->val.str[i]); + +			strBuf[(value->unitSize-1) * 2] = '\0'; +		} +		else { +			int j; +			/* escape the XML disallowed chars */ +			strBuf = (char *) +				malloc(sizeof(char) * +				(value->unitSize + 1 + num_chars_to_escape * 4)); +			for (i = 0, j = 0; i < value->unitSize; i++) { +				if (value->val.str[i] == '&') { +					strBuf[j++] = '&'; +					strBuf[j++] = 'a'; +					strBuf[j++] = 'm'; +					strBuf[j++] = 'p'; +				} +				else if (value->val.str[i] == '<') { +					strBuf[j++] = '&'; +					strBuf[j++] = 'l'; +					strBuf[j++] = 't'; +				} +				else if (value->val.str[i] == '>') { +					strBuf[j++] = '&'; +					strBuf[j++] = 'g'; +					strBuf[j++] = 't'; +				} +				else if (value->val.str[i] == '"') { +					strBuf[j++] = '&'; +					strBuf[j++] = 'q'; +					strBuf[j++] = 'u'; +					strBuf[j++] = 'o'; +					strBuf[j++] = 't'; +				} +				else { +					strBuf[j++] = value->val.str[i]; +				} +			} + +			strBuf[j] = '\0'; +		} + +		appender(data, "value=\""); +		appender(data, strBuf); +		appender(data, "\" />\n"); +		free(strBuf); +		break; +	} + +	case SDP_URL_STR8: +	case SDP_URL_STR16: +	case SDP_URL_STR32: +		appender(data, indent); +		appender(data, "<url value=\""); +		appender(data, value->val.str); +		appender(data, "\" />\n"); +		break; + +	case SDP_SEQ8: +	case SDP_SEQ16: +	case SDP_SEQ32: +		appender(data, indent); +		appender(data, "<sequence>\n"); + +		convert_raw_data_to_xml(value->val.dataseq, +					indent_level + 1, data, appender); + +		appender(data, indent); +		appender(data, "</sequence>\n"); + +		break; + +	case SDP_ALT8: +	case SDP_ALT16: +	case SDP_ALT32: +		appender(data, indent); + +		appender(data, "<alternate>\n"); + +		convert_raw_data_to_xml(value->val.dataseq, +					indent_level + 1, data, appender); +		appender(data, indent); + +		appender(data, "</alternate>\n"); + +		break; +	} + +	convert_raw_data_to_xml(value->next, indent_level, data, appender); +} + +struct conversion_data { +	void *data; +	void (*appender)(void *data, const char *); +}; + +static void convert_raw_attr_to_xml_func(void *val, void *data) +{ +	struct conversion_data *cd = (struct conversion_data *) data; +	sdp_data_t *value = (sdp_data_t *) val; +	char buf[STRBUFSIZE]; + +	buf[STRBUFSIZE - 1] = '\0'; +	snprintf(buf, STRBUFSIZE - 1, "\t<attribute id=\"0x%04x\">\n", +		 value->attrId); +	cd->appender(cd->data, buf); + +	if (data) +		convert_raw_data_to_xml(value, 2, cd->data, cd->appender); +	else +		cd->appender(cd->data, "\t\tNULL\n"); + +	cd->appender(cd->data, "\t</attribute>\n"); +} + +/* + * Will convert the sdp record to XML.  The appender and data can be used + * to control where to output the record (e.g. file or a data buffer).  The + * appender will be called repeatedly with data and the character buffer + * (containing parts of the generated XML) to append. + */ +void convert_sdp_record_to_xml(sdp_record_t *rec, +			void *data, void (*appender)(void *, const char *)) +{ +	struct conversion_data cd; + +	cd.data = data; +	cd.appender = appender; + +	if (rec && rec->attrlist) { +		appender(data, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n"); +		appender(data, "<record>\n"); +		sdp_list_foreach(rec->attrlist, +				 convert_raw_attr_to_xml_func, &cd); +		appender(data, "</record>\n"); +	} +} + +static sdp_data_t *sdp_xml_parse_uuid128(const char *data) +{ +	uint128_t val; +	int i; +	int j; + +	char buf[3]; + +	memset(&val, 0, sizeof(val)); + +	buf[2] = '\0'; + +	for (j = 0, i = 0; i < strlen(data);) { +		if (data[i] == '-') { +			i++; +			continue; +		} + +		buf[0] = data[i]; +		buf[1] = data[i + 1]; + +		val.data[j++] = strtoul(buf, 0, 16); +		i += 2; +	} + +	return sdp_data_alloc(SDP_UUID128, &val); +} + +sdp_data_t *sdp_xml_parse_uuid(const char *data, sdp_record_t *record) +{ +	sdp_data_t *ret; +	char *endptr; +	uint32_t val; +	uint16_t val2; +	int len; + +	len = strlen(data); + +	if (len == 36) { +		ret = sdp_xml_parse_uuid128(data); +		goto result; +	} + +	val = strtoll(data, &endptr, 16); + +	/* Couldn't parse */ +	if (*endptr != '\0') +		return NULL; + +	if (val > USHRT_MAX) { +		ret = sdp_data_alloc(SDP_UUID32, &val); +		goto result; +	} + +	val2 = val; + +	ret = sdp_data_alloc(SDP_UUID16, &val2); + +result: +	if (record && ret) +		sdp_pattern_add_uuid(record, &ret->val.uuid); + +	return ret; +} + +sdp_data_t *sdp_xml_parse_int(const char * data, uint8_t dtd) +{ +	char *endptr; +	sdp_data_t *ret = NULL; + +	switch (dtd) { +	case SDP_BOOL: +	{ +		uint8_t val = 0; + +		if (!strcmp("true", data)) { +			val = 1; +		} + +		else if (!strcmp("false", data)) { +			val = 0; +		} +		else { +			return NULL; +		} + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_INT8: +	{ +		int8_t val = strtoul(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_UINT8: +	{ +		uint8_t val = strtoul(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_INT16: +	{ +		int16_t val = strtoul(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_UINT16: +	{ +		uint16_t val = strtoul(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_INT32: +	{ +		int32_t val = strtoul(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_UINT32: +	{ +		uint32_t val = strtoul(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_INT64: +	{ +		int64_t val = strtoull(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_UINT64: +	{ +		uint64_t val = strtoull(data, &endptr, 0); + +		/* Failed to parse */ +		if ((endptr != data) && (*endptr != '\0')) +			return NULL; + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	case SDP_INT128: +	case SDP_UINT128: +	{ +		uint128_t val; +		int i = 0; +		char buf[3]; + +		buf[2] = '\0'; + +		for (; i < 32; i += 2) { +			buf[0] = data[i]; +			buf[1] = data[i + 1]; + +			val.data[i >> 1] = strtoul(buf, 0, 16); +		} + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	}; + +	return ret; +} + +static char *sdp_xml_parse_string_decode(const char *data, char encoding, uint32_t *length) +{ +	int len = strlen(data); +	char *text; + +	if (encoding == SDP_XML_ENCODING_NORMAL) { +		text = strdup(data); +		*length = len; +	} else { +		char buf[3], *decoded; +		int i; + +		decoded = malloc((len >> 1) + 1); + +		/* Ensure the string is a power of 2 */ +		len = (len >> 1) << 1; + +		buf[2] = '\0'; + +		for (i = 0; i < len; i += 2) { +			buf[0] = data[i]; +			buf[1] = data[i + 1]; + +			decoded[i >> 1] = strtoul(buf, 0, 16); +		} + +		decoded[len >> 1] = '\0'; +		text = decoded; +		*length = len >> 1; +	} + +	return text; +} + +sdp_data_t *sdp_xml_parse_url(const char *data) +{ +	uint8_t dtd = SDP_URL_STR8; +	char *url; +	uint32_t length; +	sdp_data_t *ret; + +	url = sdp_xml_parse_string_decode(data, +				SDP_XML_ENCODING_NORMAL, &length); + +	if (length > UCHAR_MAX) +		dtd = SDP_URL_STR16; + +	ret = sdp_data_alloc_with_length(dtd, url, length); + +	debug("URL size %d length %d: -->%s<--", ret->unitSize, length, url); + +	free(url); + +	return ret; +} + +sdp_data_t *sdp_xml_parse_text(const char *data, char encoding) +{ +	uint8_t dtd = SDP_TEXT_STR8; +	char *text; +	uint32_t length; +	sdp_data_t *ret; + +	text = sdp_xml_parse_string_decode(data, encoding, &length); + +	if (length > UCHAR_MAX) +		dtd = SDP_TEXT_STR16; + +	ret = sdp_data_alloc_with_length(dtd, text, length); + +	debug("Text size %d length %d: -->%s<--", ret->unitSize, length, text); + +	free(text); + +	return ret; +} + +sdp_data_t *sdp_xml_parse_nil(const char *data) +{ +	return sdp_data_alloc(SDP_DATA_NIL, 0); +} + +#define DEFAULT_XML_DATA_SIZE 1024 + +struct sdp_xml_data *sdp_xml_data_alloc() +{ +	struct sdp_xml_data *elem; + +	elem = malloc(sizeof(struct sdp_xml_data)); +	if (!elem) +		return NULL; + +	memset(elem, 0, sizeof(struct sdp_xml_data)); + +	/* Null terminate the text */ +	elem->size = DEFAULT_XML_DATA_SIZE; +	elem->text = malloc(DEFAULT_XML_DATA_SIZE); +	elem->text[0] = '\0'; + +	return elem; +} + +void sdp_xml_data_free(struct sdp_xml_data *elem) +{ +	if (elem->data) +		sdp_data_free(elem->data); + +	if (elem->name) +		free(elem->name); + +	if (elem->text) + +		free(elem->text); +	free(elem); +} + +struct sdp_xml_data *sdp_xml_data_expand(struct sdp_xml_data *elem) +{ +	char *newbuf; + +	newbuf = malloc(elem->size * 2); +	if (!newbuf) +		return NULL; + +	memcpy(newbuf, elem->text, elem->size); +	elem->size *= 2; +	free(elem->text); + +	elem->text = newbuf; + +	return elem; +} + +sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem, +							sdp_record_t *record) +{ +	const char *data = elem->text; + +	if (!strcmp(el, "boolean")) +		return sdp_xml_parse_int(data, SDP_BOOL); +	else if (!strcmp(el, "uint8")) +		return sdp_xml_parse_int(data, SDP_UINT8); +	else if (!strcmp(el, "uint16")) +		return sdp_xml_parse_int(data, SDP_UINT16); +	else if (!strcmp(el, "uint32")) +		return sdp_xml_parse_int(data, SDP_UINT32); +	else if (!strcmp(el, "uint64")) +		return sdp_xml_parse_int(data, SDP_UINT64); +	else if (!strcmp(el, "uint128")) +		return sdp_xml_parse_int(data, SDP_UINT128); +	else if (!strcmp(el, "int8")) +		return sdp_xml_parse_int(data, SDP_INT8); +	else if (!strcmp(el, "int16")) +		return sdp_xml_parse_int(data, SDP_INT16); +	else if (!strcmp(el, "int32")) +		return sdp_xml_parse_int(data, SDP_INT32); +	else if (!strcmp(el, "int64")) +		return sdp_xml_parse_int(data, SDP_INT64); +	else if (!strcmp(el, "int128")) +		return sdp_xml_parse_int(data, SDP_INT128); +	else if (!strcmp(el, "uuid")) +		return sdp_xml_parse_uuid(data, record); +	else if (!strcmp(el, "url")) +		return sdp_xml_parse_url(data); +	else if (!strcmp(el, "text")) +		return sdp_xml_parse_text(data, elem->type); +	else if (!strcmp(el, "nil")) +		return sdp_xml_parse_nil(data); + +	return NULL; +}  | 
