/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2005-2007 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 "logging.h" #include "sdp-xml.h" 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; } /* Expat specific implementation of the context struct */ 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 */ }; 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 = NULL; } 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 { /* 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(el, context->stack_head, context->sdprec); /* Could not parse an entry */ if (context->stack_head->data == NULL) XML_StopParser(context->parser, 0); } } 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 = NULL; sdp_xml_data_free(context->stack_head); context->stack_head = NULL; } 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 = NULL; 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; }