summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2006-11-22 06:06:36 +0000
committerMarcel Holtmann <marcel@holtmann.org>2006-11-22 06:06:36 +0000
commit214cc18ef97fcaa0a4c3746e183caf5820bc811b (patch)
tree0441366702a36735f000cd462b6aa2ededb9a72e
parent4195a470a54c20d830a2faea6d617d8f67427f5e (diff)
Add first draft XML based service record registration
-rw-r--r--common/sdp-dummy.c11
-rw-r--r--common/sdp-expat.c381
-rw-r--r--common/sdp-xml.c354
-rw-r--r--common/sdp-xml.h11
-rw-r--r--hcid/dbus-api.txt11
-rw-r--r--hcid/dbus-manager.c103
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 }
};