diff options
Diffstat (limited to 'common/sdp-expat.c')
| -rw-r--r-- | common/sdp-expat.c | 381 | 
1 files changed, 381 insertions, 0 deletions
diff --git a/common/sdp-expat.c b/common/sdp-expat.c index 0ab15d7a..cc2ff575 100644 --- a/common/sdp-expat.c +++ b/common/sdp-expat.c @@ -25,9 +25,390 @@  #include <config.h>  #endif +#include <stdio.h> +#include <errno.h> +#include <malloc.h> +  #include <bluetooth/sdp.h>  #include <bluetooth/sdp_lib.h>  #include <expat.h> +#include "logging.h"  #include "sdp-xml.h" + +/* Expat specific implementation of the context struct */ + +struct sdp_xml_data { +	char *text;			/* Pointer to the current buffer */ +	int size;			/* Size of the current buffer */ +	sdp_data_t *data;		/* The current item being built */ +	struct sdp_xml_data *next;	/* Next item on the stack */ +	char type;			/* 0 = Text or Hexadecimal */ +	char *name;			/* Name, optional in the dtd */ +	/* TODO: What is it used for? */ +}; + +struct sdp_xml_context { +	XML_Parser parser;			/* Parser object being used */ +	sdp_record_t *sdprec;			/* SDP Record being built */ +	struct sdp_xml_data *stack_head;	/* Top of the stack of attributes */ +	int attrId;				/* Id of the most recently processed attribute */ +}; + +#define DEFAULT_XML_DATA_SIZE 1024 + +static struct sdp_xml_data *sdp_xml_data_alloc() +{ +	struct sdp_xml_data *elem; + +	elem = malloc(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'; + +	elem->next = 0; +	elem->data = 0; + +	elem->type = 0; +	elem->name = 0; + +	return elem; +} + +static void sdp_xml_data_free(struct sdp_xml_data *elem) +{ +	if (elem->data) +		sdp_data_free(elem->data); + +	if (elem->name) +		free(elem->name); + +	free(elem->text); +	free(elem); +} + +static 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; +} + +static sdp_data_t *sdp_xml_parse_datatype(const char *el, +					struct sdp_xml_context *context) +{ +	const char *data = context->stack_head->text; + +	if (!strcmp(el, "bool")) +		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); +	else if (!strcmp(el, "url")) +		return sdp_xml_parse_url(data); +	else if (!strcmp(el, "text")) +		return sdp_xml_parse_text(data, context->stack_head->type); +	else if (!strcmp(el, "nil")) +		return sdp_xml_parse_nil(data); + +	return NULL; +} + +static void convert_xml_to_sdp_start(void *data, const char *el, const char **attr) +{ +	struct sdp_xml_context *context = data; +	int i; + +	if (!strcmp(el, "record")) +		return; + +	if (!strcmp(el, "attribute")) { +		/* Get the ID */ +		for (i = 0; attr[i]; i += 1) { +			if (!strcmp(attr[i], "id")) { +				context->attrId = +				    strtol(attr[i + 1], 0, 0); +				break; +			} +		} + +		return; +	} + +	/* Assume every other tag is an element of some sort */ +	if (context->stack_head) { +		struct sdp_xml_data *newelem = sdp_xml_data_alloc(); +		newelem->next = context->stack_head; +		context->stack_head = newelem; +	} else { +		context->stack_head = sdp_xml_data_alloc(); +		context->stack_head->next = 0; +	} + +	if (!strcmp(el, "sequence")) +		context->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL); +	else if (!strcmp(el, "alternate")) +		context->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL); +	else { +		const char *type = el; +		/* Parse value, name, encoding */ +		for (i = 0; attr[i]; i += 2) { +			if (!strcmp(attr[i], "value")) { +				int curlen = +				    strlen(context->stack_head->text); +				int attrlen = strlen(attr[i + 1]); + +				/* Ensure we're big enough */ +				while ((curlen + 1 + attrlen) > +				       context->stack_head->size) { +					sdp_xml_data_expand(context-> +							    stack_head); +				} + +				memcpy(&context->stack_head->text[curlen], +				       attr[i + 1], attrlen); +				context->stack_head->text[curlen + +							  attrlen] = '\0'; +			} + +			if (!strcmp(attr[i], "encoding")) { +				if (!strcmp(attr[i + 1], "hex")) +					context->stack_head->type = 1; +			} + +			if (!strcmp(attr[i], "name")) { +				context->stack_head->name = +				    strdup(attr[i + 1]); +			} +		} + +		context->stack_head->data = +		    sdp_xml_parse_datatype(type, context); + +		/* Could not parse an entry */ +		if (context->stack_head->data == NULL) +			XML_StopParser(context->parser, 0); +	} +} + +static int compute_seq_size(sdp_data_t *data) +{ +	int unit_size = data->unitSize; +	sdp_data_t *seq = data->val.dataseq; + +	for (; seq; seq = seq->next) +		unit_size += seq->unitSize; + +	return unit_size; +} + +static void convert_xml_to_sdp_end(void *data, const char *el) +{ +	struct sdp_xml_context *context = data; +	struct sdp_xml_data *elem; + +	if (!strcmp(el, "record")) +		return; + +	if (!strcmp(el, "attribute")) { +		if (context->stack_head && context->stack_head->data) { +			int ret = sdp_attr_add(context->sdprec, +					       context->attrId, +					       context->stack_head->data); +			if (ret == -1) +				debug("Trouble adding attribute\n"); + +			context->stack_head->data = 0; +			sdp_xml_data_free(context->stack_head); +			context->stack_head = 0; +		} else { +			debug("No Data for attribute: %d\n", +			      context->attrId); +		} + +		return; +	} else if (!strcmp(el, "sequence")) { +		context->stack_head->data->unitSize = +		    compute_seq_size(context->stack_head->data); + +		if (context->stack_head->data->unitSize > USHRT_MAX) { +			context->stack_head->data->unitSize += +			    sizeof(uint32_t); +			context->stack_head->data->dtd = SDP_SEQ32; +		} else if (context->stack_head->data->unitSize > UCHAR_MAX) { +			context->stack_head->data->unitSize += +			    sizeof(uint16_t); +			context->stack_head->data->dtd = SDP_SEQ16; +		} else { +			context->stack_head->data->unitSize += +			    sizeof(uint8_t); +		} +	} else if (!strcmp(el, "alternate")) { +		context->stack_head->data->unitSize = +		    compute_seq_size(context->stack_head->data); + +		if (context->stack_head->data->unitSize > USHRT_MAX) { +			context->stack_head->data->unitSize += +			    sizeof(uint32_t); +			context->stack_head->data->dtd = SDP_ALT32; +		} else if (context->stack_head->data->unitSize > UCHAR_MAX) { +			context->stack_head->data->unitSize += +			    sizeof(uint16_t); +			context->stack_head->data->dtd = SDP_ALT16; +		} else { +			context->stack_head->data->unitSize += +			    sizeof(uint8_t); +		} +	} + +	/* If we're not inside a seq or alt, then we're inside an attribute +	   which will be taken care of later +	 */ +	if (context->stack_head->next && +		context->stack_head->data && context->stack_head->next->data) { +		switch (context->stack_head->next->data->dtd) { +		case SDP_SEQ8: +		case SDP_SEQ16: +		case SDP_SEQ32: +		case SDP_ALT8: +		case SDP_ALT16: +		case SDP_ALT32: +			context->stack_head->next->data->val. +			    dataseq = +			    sdp_seq_append(context-> +					   stack_head-> +					   next->data-> +					   val.dataseq, +					   context->stack_head->data); +			context->stack_head->data = 0; +			break; +		} + +		elem = context->stack_head; +		context->stack_head = context->stack_head->next; + +		sdp_xml_data_free(elem); +	} +} + +static struct sdp_xml_context *sdp_xml_init_context() +{ +	struct sdp_xml_context *context; + +	context = malloc(sizeof(struct sdp_xml_context)); + +	if (!context) +		return NULL; + +	context->parser = 0; +	context->sdprec = 0; +	context->stack_head = 0; + +	context->parser = XML_ParserCreate(NULL); +	XML_SetElementHandler(context->parser, convert_xml_to_sdp_start, +			      convert_xml_to_sdp_end); +	XML_SetUserData(context->parser, context); + +	if (!context->parser) +		goto fail; + +	context->sdprec = sdp_record_alloc(); + +	if (!context->sdprec) +		goto fail; + +	return context; + +fail: +	if (context->parser) +		free(context->parser); + +	if (context->sdprec) +		sdp_record_free(context->sdprec); + +	if (context) +		free(context); + +	return NULL; +} + +static void sdp_xml_free_context(struct sdp_xml_context *context) +{ +	struct sdp_xml_data *elem; + +	/* Free the stack */ +	while (context->stack_head) { +		elem = context->stack_head; +		context->stack_head = elem->next; +		sdp_xml_data_free(elem); +	} + +	XML_ParserFree(context->parser); + +	free(context); +} + +static int sdp_xml_parse_chunk(struct sdp_xml_context *context, +					const char *data, int size, int final) +{ +	if (!XML_Parse(context->parser, data, size, final)) { +		error("Parse error at line %d: %s\n", +			XML_GetCurrentLineNumber(context->parser), +			XML_ErrorString(XML_GetErrorCode(context->parser))); +		return -1; +	} + +	return 0; +} + +sdp_record_t *sdp_xml_parse_record(const char *data, int size) +{ +	struct sdp_xml_context *context; +	sdp_record_t *record; + +	context = sdp_xml_init_context(); + +	if (sdp_xml_parse_chunk(context, data, size, 1) < 0) { +		sdp_record_free(context->sdprec); +		sdp_xml_free_context(context); +		return NULL; +	} + +	record = context->sdprec; + +	sdp_xml_free_context(context); + +	return record; +}  | 
