/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2005-2006 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #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, "boolean")) 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; }