From 214cc18ef97fcaa0a4c3746e183caf5820bc811b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 22 Nov 2006 06:06:36 +0000 Subject: Add first draft XML based service record registration --- common/sdp-dummy.c | 11 ++ common/sdp-expat.c | 381 +++++++++++++++++++++++++++++++++++++++++++++++++++++ common/sdp-xml.c | 354 +++++++++++++++++++++++++++++++++++++++++++++---- common/sdp-xml.h | 11 ++ 4 files changed, 731 insertions(+), 26 deletions(-) (limited to 'common') 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 #endif +#include +#include + +#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 #endif +#include +#include +#include + #include #include #include +#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 #include #include +#include #include #include +#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, "\n"); break; + case SDP_BOOL: appender(data, indent); appender(data, "val.uint8 ? "true" : "false"); appender(data, "\" />\n"); break; + case SDP_UINT8: appender(data, indent); appender(data, "\n"); break; + case SDP_UINT16: appender(data, indent); appender(data, "\n"); break; + case SDP_UINT32: appender(data, indent); appender(data, "\n"); break; + case SDP_UINT64: appender(data, indent); appender(data, "\n"); break; + case SDP_UINT128: appender(data, indent); appender(data, "\n"); break; + case SDP_INT8: appender(data, indent); appender(data, "\n"); break; + case SDP_INT16: appender(data, indent); appender(data, "\n"); break; + case SDP_INT32: appender(data, indent); appender(data, "\n"); break; + case SDP_INT64: appender(data, indent); appender(data, "\n"); break; + case SDP_INT128: appender(data, indent); appender(data, "\n"); break; + case SDP_UUID16: appender(data, indent); appender(data, "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, "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, "\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, "\n"); convert_raw_data_to_xml(value->val.dataseq, - indent_level + 1, data, - appender); + indent_level + 1, data, appender); appender(data, indent); appender(data, "\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, "\n"); convert_raw_data_to_xml(value->val.dataseq, - indent_level + 1, data, - appender); + indent_level + 1, data, appender); appender(data, indent); appender(data, "\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, "\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 +#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 */ -- cgit