summaryrefslogtreecommitdiffstats
path: root/common/sdp-expat.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/sdp-expat.c')
-rw-r--r--common/sdp-expat.c381
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;
+}