| /* |
| * |
| * BlueZ - Bluetooth protocol stack for Linux |
| * |
| * Copyright (C) 2005-2007 Marcel Holtmann <marcel@holtmann.org> |
| * |
| * |
| * 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 <config.h> |
| #endif |
| |
| #include <stdlib.h> |
| |
| #include <bluetooth/sdp.h> |
| #include <bluetooth/sdp_lib.h> |
| |
| #include <expat.h> |
| |
| #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; |
| } |