mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-26 05:34:23 +08:00
Add first draft XML based service record registration
This commit is contained in:
parent
4195a470a5
commit
214cc18ef9
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
354
common/sdp-xml.c
354
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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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 }
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user