diff options
| author | Marcel Holtmann <marcel@holtmann.org> | 2006-11-22 06:06:36 +0000 | 
|---|---|---|
| committer | Marcel Holtmann <marcel@holtmann.org> | 2006-11-22 06:06:36 +0000 | 
| commit | 214cc18ef97fcaa0a4c3746e183caf5820bc811b (patch) | |
| tree | 0441366702a36735f000cd462b6aa2ededb9a72e | |
| parent | 4195a470a54c20d830a2faea6d617d8f67427f5e (diff) | |
Add first draft XML based service record registration
| -rw-r--r-- | common/sdp-dummy.c | 11 | ||||
| -rw-r--r-- | common/sdp-expat.c | 381 | ||||
| -rw-r--r-- | common/sdp-xml.c | 354 | ||||
| -rw-r--r-- | common/sdp-xml.h | 11 | ||||
| -rw-r--r-- | hcid/dbus-api.txt | 11 | ||||
| -rw-r--r-- | hcid/dbus-manager.c | 103 | 
6 files changed, 845 insertions, 26 deletions
| diff --git a/common/sdp-dummy.c b/common/sdp-dummy.c index 8ed637e7..b1f74df7 100644 --- a/common/sdp-dummy.c +++ b/common/sdp-dummy.c @@ -25,4 +25,15 @@  #include <config.h>  #endif +#include <stdio.h> +#include <errno.h> + +#include "logging.h"  #include "sdp-xml.h" + +sdp_record_t *sdp_xml_parse_record(const char *data, int size) +{ +	error("No XML parser available"); + +	return -1; +} 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; +} diff --git a/common/sdp-xml.c b/common/sdp-xml.c index ee5601ee..8b290b88 100644 --- a/common/sdp-xml.c +++ b/common/sdp-xml.c @@ -31,17 +31,20 @@  #include <malloc.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 256 +#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 *)) +		void *data, void (*appender)(void *, const char *))  {  	int i, hex;  	char buf[STRBUFSIZE]; @@ -70,12 +73,14 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		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=\""); @@ -83,6 +88,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_UINT16:  		appender(data, indent);  		appender(data, "<uint16 value=\""); @@ -90,6 +96,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_UINT32:  		appender(data, indent);  		appender(data, "<uint32 value=\""); @@ -97,6 +104,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_UINT64:  		appender(data, indent);  		appender(data, "<uint64 value=\""); @@ -104,6 +112,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_UINT128:  		appender(data, indent);  		appender(data, "<uint128 value=\""); @@ -116,6 +125,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_INT8:  		appender(data, indent);  		appender(data, "<int8 value=\""); @@ -123,6 +133,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_INT16:  		appender(data, indent);  		appender(data, "<int16 value=\""); @@ -130,6 +141,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_INT32:  		appender(data, indent);  		appender(data, "<int32 value=\""); @@ -137,6 +149,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_INT64:  		appender(data, indent);  		appender(data, "<int64 value=\""); @@ -144,6 +157,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_INT128:  		appender(data, indent);  		appender(data, "<int128 value=\""); @@ -156,22 +170,23 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		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); +		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); +		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=\""); @@ -214,6 +229,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, buf);  		appender(data, "\" />\n");  		break; +  	case SDP_TEXT_STR8:  	case SDP_TEXT_STR16:  	case SDP_TEXT_STR32: @@ -256,7 +272,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  			   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], +				sprintf(&strBuf[i*sizeof(char)*2],  					"%02x",  					(unsigned char) value->val.str[i]); @@ -306,6 +322,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		free(strBuf);  		break;  	} +  	case SDP_URL_STR8:  	case SDP_URL_STR16:  	case SDP_URL_STR32: @@ -314,6 +331,7 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, value->val.str);  		appender(data, "\" />\n");  		break; +  	case SDP_SEQ8:  	case SDP_SEQ16:  	case SDP_SEQ32: @@ -321,13 +339,13 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, "<sequence>\n");  		convert_raw_data_to_xml(value->val.dataseq, -					indent_level + 1, data, -					appender); +					indent_level + 1, data, appender);  		appender(data, indent);  		appender(data, "</sequence>\n");  		break; +  	case SDP_ALT8:  	case SDP_ALT16:  	case SDP_ALT32: @@ -336,25 +354,20 @@ static void convert_raw_data_to_xml(sdp_data_t *value, int indent_level,  		appender(data, "<alternate>\n");  		convert_raw_data_to_xml(value->val.dataseq, -					indent_level + 1, data, -					appender); +					indent_level + 1, data, appender);  		appender(data, indent);  		appender(data, "</alternate>\n");  		break; -	default: -		break;  	} -	convert_raw_data_to_xml(value->next, indent_level, data, -				appender); +	convert_raw_data_to_xml(value->next, indent_level, data, appender);  } -struct conversion_data -{ +struct conversion_data {  	void *data; -	void (*appender) (void *data, const char *); +	void (*appender)(void *data, const char *);  };  static void convert_raw_attr_to_xml_func(void *val, void *data) @@ -369,8 +382,7 @@ static void convert_raw_attr_to_xml_func(void *val, void *data)  	cd->appender(cd->data, buf);  	if (data) -		convert_raw_data_to_xml(value, 2, cd->data, -					cd->appender); +		convert_raw_data_to_xml(value, 2, cd->data, cd->appender);  	else  		cd->appender(cd->data, "\t\tNULL\n"); @@ -378,13 +390,13 @@ static void convert_raw_attr_to_xml_func(void *val, void *data)  }  /* -    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. -*/ + * 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 *)) +			void *data, void (*appender)(void *, const char *))  {  	struct conversion_data cd; @@ -399,3 +411,293 @@ void convert_sdp_record_to_xml(sdp_record_t *rec,  		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) +{ +	int len; +	char *endptr; +	uint32_t val; +	uint16_t val2 = val; + +	len = strlen(data); + +	if (len == 36) +		return sdp_xml_parse_uuid128(data); + +	val = strtoll(data, &endptr, 16); + +	/* Couldn't parse */ +	if (*endptr != '\0') +		return NULL; + +	if (val > USHRT_MAX) +		return sdp_data_alloc(SDP_UUID32, &val); + +	return sdp_data_alloc(SDP_UUID16, &val2); + +	/* Should never get here */ +	return NULL; +} + +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] = strtoul(buf, 0, 16); +		} + +		ret = sdp_data_alloc(dtd, &val); +		break; +	} + +	}; + +	return ret; +} + +static sdp_data_t *sdp_xml_parse_url_with_size(const char *data, uint8_t dtd) +{ +	return sdp_data_alloc(dtd, data); +} + +sdp_data_t *sdp_xml_parse_url(const char *data) +{ +	uint8_t dtd = SDP_URL_STR8; + +	if (strlen(data) > UCHAR_MAX) +		dtd = SDP_URL_STR16; + +	return sdp_xml_parse_url_with_size(data, dtd); +} + +static char *sdp_xml_parse_text_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; +} + +#if 0 +static sdp_data_t *sdp_xml_parse_text_with_size(const char *data, char encoding, uint8_t dtd) +{ +	char *text; +	uint32_t length; +	sdp_data_t *ret; + +	text = sdp_xml_parse_text_decode(data, encoding, &length); +	ret = sdp_data_alloc_with_length(dtd, text, length); + +	debug("Unit size %d length %d: -->%s<--\n", ret->unitSize, length, text); + +	return ret; +} +#endif + +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_text_decode(data, encoding, &length); + +	if (length > UCHAR_MAX) +		dtd = SDP_TEXT_STR16; + +	ret = sdp_data_alloc_with_length(dtd, text, length); + +	debug("Unit size %d length %d: -->%s<--\n", ret->unitSize, length, text); + +	return ret; +} + +sdp_data_t *sdp_xml_parse_nil(const char *data) +{ +	return sdp_data_alloc(SDP_DATA_NIL, 0); +} diff --git a/common/sdp-xml.h b/common/sdp-xml.h index 5e6bb7ce..16bea948 100644 --- a/common/sdp-xml.h +++ b/common/sdp-xml.h @@ -27,7 +27,18 @@  #include <bluetooth/sdp.h> +#define SDP_XML_ENCODING_NORMAL	0 +#define SDP_XML_ENCODING_HEX	1 +  void convert_sdp_record_to_xml(sdp_record_t *rec,  		void *user_data, void (*append_func) (void *, const char *)); +sdp_data_t *sdp_xml_parse_nil(const char *data); +sdp_data_t *sdp_xml_parse_text(const char *data, char encoding); +sdp_data_t *sdp_xml_parse_url(const char *data); +sdp_data_t *sdp_xml_parse_int(const char *data, uint8_t dtd); +sdp_data_t *sdp_xml_parse_uuid(const char *data); + +sdp_record_t *sdp_xml_parse_record(const char *data, int size); +		  #endif /* __SDP_XML_H */ diff --git a/hcid/dbus-api.txt b/hcid/dbus-api.txt index da2bd54d..c9661616 100644 --- a/hcid/dbus-api.txt +++ b/hcid/dbus-api.txt @@ -191,6 +191,17 @@ Methods		uint32 InterfaceVersion()  			registered, otherwise the record will be available  			when the service agent Start method is called. +		uint32 AddServiceRecordAsXML(string path, string record) + +			Add a new service record to the service agent +			and returns the assigned handle. + +			The path parameter is the object path of the +			service agent. The record parameter is the XML +			representation of a service record. If the service +			agent is running the service record will be automatically +			registered, otherwise the record will be available +			when the service agent Start method is called.  		void  RemoveServiceRecord(string path, uint32 handle) diff --git a/hcid/dbus-manager.c b/hcid/dbus-manager.c index b3e79150..9ac6b0e4 100644 --- a/hcid/dbus-manager.c +++ b/hcid/dbus-manager.c @@ -28,12 +28,15 @@  #include <stdio.h>  #include <errno.h>  #include <unistd.h> +#include <malloc.h>  #include <sys/ioctl.h>  #include <sys/socket.h>  #include <bluetooth/bluetooth.h>  #include <bluetooth/hci.h>  #include <bluetooth/hci_lib.h> +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h>  #include <dbus/dbus.h> @@ -45,6 +48,7 @@  #include "dbus-security.h"  #include "dbus-service.h"  #include "dbus-manager.h" +#include "sdp-xml.h"  static int default_adapter_id = -1;  static int autostart = 1; @@ -473,6 +477,104 @@ fail:  	return error_failed(conn, msg, err);  } +static DBusHandlerResult add_service_record_xml(DBusConnection *conn, +						DBusMessage *msg, void *data) +{ +	struct service_agent *agent; +	DBusMessage *reply; +	struct binary_record *rec; +	sdp_record_t *sdp_rec; +	const char *path; +	const char *record; +	int err; + +	if (!dbus_message_get_args(msg, NULL, +				DBUS_TYPE_STRING, &path, +				DBUS_TYPE_STRING, &record, +				DBUS_TYPE_INVALID)) +		return error_invalid_arguments(conn, msg); + +	if (!dbus_connection_get_object_path_data(conn, path, +						(void *) &agent)) { +		/* If failed the path is invalid! */ +		return error_invalid_arguments(conn, msg); +	} + +	if (!agent || strcmp(dbus_message_get_sender(msg), agent->id)) +		return error_not_authorized(conn, msg); + +	reply = dbus_message_new_method_return(msg); +	if (!reply) +		return DBUS_HANDLER_RESULT_NEED_MEMORY; + +	sdp_rec = sdp_xml_parse_record(record, strlen(record)); +	if (!sdp_rec) { +		error("Parsing of XML service record failed"); +		dbus_message_unref(reply); +		return error_invalid_arguments(conn, msg); +	} + +        /* TODO: Is this correct? We remove the record handle attribute +	   (if it exists) so SDP server assigns a new one */ +        sdp_attr_remove(sdp_rec, 0x0); + +	rec = binary_record_new(); +	if (!rec) { +		sdp_record_free(sdp_rec); +		dbus_message_unref(reply); +		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	} + +	rec->buf = malloc(sizeof(sdp_buf_t)); + +	if (!rec->buf) { +		sdp_record_free(sdp_rec); +		binary_record_free(rec); +		dbus_message_unref(reply); +		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	} + +	/* Generate binary record */ +	if (sdp_gen_record_pdu(sdp_rec, rec->buf) != 0) { +		sdp_record_free(sdp_rec); +		binary_record_free(rec); +		dbus_message_unref(reply); +		return DBUS_HANDLER_RESULT_NEED_MEMORY; +	} + +	sdp_record_free(sdp_rec); + +	/* Assign a new handle */ +	rec->ext_handle = next_handle++; + +	if (agent->running) { +		uint32_t handle = 0; + +		if (register_sdp_record(rec->buf->data,	rec->buf->data_size, &handle) < 0) { +			err = errno; +			error("Service record registration failed: %s (%d)", +							strerror(err), err); +			goto fail; +		} + +		rec->handle = handle; +	} + +	agent->records = slist_append(agent->records, rec); + +	dbus_message_append_args(reply, +				DBUS_TYPE_UINT32, &rec->ext_handle, +				DBUS_TYPE_INVALID); + +	return send_message_and_unref(conn, reply); + +fail: +	binary_record_free(rec); +	dbus_message_unref(reply); + +	return error_failed(conn, msg, err); +} +  static DBusHandlerResult remove_service_record(DBusConnection *conn,  						DBusMessage *msg, void *data)  { @@ -532,6 +634,7 @@ static struct service_data methods[] = {  	{ "RegisterService",	register_service			},  	{ "UnregisterService",	unregister_service			},  	{ "AddServiceRecord",		add_service_record		}, +	{ "AddServiceRecordAsXML", 	add_service_record_xml		},  	{ "RemoveServiceRecord",	remove_service_record		},  	{ NULL, NULL }  }; | 
