diff options
author | Marcel Holtmann <marcel@holtmann.org> | 2008-07-26 19:00:53 +0200 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2008-07-26 19:00:53 +0200 |
commit | d6ae1c3f777832f8e32702f81fe64e33a1396928 (patch) | |
tree | 159a1e59f3929c9d795dbd1f3edd84d9dccba048 /common/sdp-xml.c | |
parent | b8e5fea8d31fbcd3d1c044385f8217dbf39892bb (diff) | |
parent | 3382af9114a9b2e657c7ddd0a5511edda6a37a90 (diff) |
Import bluez-utils-3.36 revision history
Diffstat (limited to 'common/sdp-xml.c')
-rw-r--r-- | common/sdp-xml.c | 793 |
1 files changed, 793 insertions, 0 deletions
diff --git a/common/sdp-xml.c b/common/sdp-xml.c new file mode 100644 index 00000000..1e1e07c3 --- /dev/null +++ b/common/sdp-xml.c @@ -0,0 +1,793 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2005-2008 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 <stdio.h> +#include <errno.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> + +#include <bluetooth/sdp.h> +#include <bluetooth/sdp_lib.h> + +#include "logging.h" +#include "sdp-xml.h" + +#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 *)) +{ + int i, hex; + char buf[STRBUFSIZE]; + char indent[MAXINDENT]; + char next_indent[MAXINDENT]; + + if (!value) + return; + + if (indent_level >= MAXINDENT) + indent_level = MAXINDENT - 2; + + for (i = 0; i < indent_level; i++) { + indent[i] = '\t'; + next_indent[i] = '\t'; + } + + indent[i] = '\0'; + next_indent[i] = '\t'; + next_indent[i + 1] = '\0'; + + buf[STRBUFSIZE - 1] = '\0'; + + switch (value->dtd) { + case SDP_DATA_NIL: + appender(data, indent); + appender(data, "<nil/>\n"); + break; + + case SDP_BOOL: + appender(data, indent); + appender(data, "<boolean value=\""); + appender(data, value->val.uint8 ? "true" : "false"); + appender(data, "\" />\n"); + break; + + case SDP_UINT8: + appender(data, indent); + appender(data, "<uint8 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%02x", value->val.uint8); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT16: + appender(data, indent); + appender(data, "<uint16 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%04x", value->val.uint16); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT32: + appender(data, indent); + appender(data, "<uint32 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%08x", value->val.uint32); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT64: + appender(data, indent); + appender(data, "<uint64 value=\""); + snprintf(buf, STRBUFSIZE - 1, "0x%016jx", value->val.uint64); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_UINT128: + appender(data, indent); + appender(data, "<uint128 value=\""); + + for (i = 0; i < 16; i++) { + sprintf(&buf[i * 2], "%02x", + (unsigned char) value->val.uint128.data[i]); + } + + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT8: + appender(data, indent); + appender(data, "<int8 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int8); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT16: + appender(data, indent); + appender(data, "<int16 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int16); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT32: + appender(data, indent); + appender(data, "<int32 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%d", value->val.int32); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT64: + appender(data, indent); + appender(data, "<int64 value=\""); + snprintf(buf, STRBUFSIZE - 1, "%jd", value->val.int64); + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_INT128: + appender(data, indent); + appender(data, "<int128 value=\""); + + for (i = 0; i < 16; i++) { + sprintf(&buf[i * 2], "%02x", + (unsigned char) value->val.int128.data[i]); + } + appender(data, buf); + + appender(data, "\" />\n"); + break; + + case SDP_UUID16: + appender(data, indent); + appender(data, "<uuid value=\""); + 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, "<uuid value=\""); + 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, "<uuid value=\""); + + snprintf(buf, STRBUFSIZE - 1, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", + (unsigned char) value->val.uuid.value. + uuid128.data[0], + (unsigned char) value->val.uuid.value. + uuid128.data[1], + (unsigned char) value->val.uuid.value. + uuid128.data[2], + (unsigned char) value->val.uuid.value. + uuid128.data[3], + (unsigned char) value->val.uuid.value. + uuid128.data[4], + (unsigned char) value->val.uuid.value. + uuid128.data[5], + (unsigned char) value->val.uuid.value. + uuid128.data[6], + (unsigned char) value->val.uuid.value. + uuid128.data[7], + (unsigned char) value->val.uuid.value. + uuid128.data[8], + (unsigned char) value->val.uuid.value. + uuid128.data[9], + (unsigned char) value->val.uuid.value. + uuid128.data[10], + (unsigned char) value->val.uuid.value. + uuid128.data[11], + (unsigned char) value->val.uuid.value. + uuid128.data[12], + (unsigned char) value->val.uuid.value. + uuid128.data[13], + (unsigned char) value->val.uuid.value. + uuid128.data[14], + (unsigned char) value->val.uuid.value. + uuid128.data[15]); + + appender(data, buf); + appender(data, "\" />\n"); + break; + + case SDP_TEXT_STR8: + case SDP_TEXT_STR16: + case SDP_TEXT_STR32: + { + hex = 0; + + int num_chars_to_escape = 0; + + for (i = 0; i < value->unitSize; i++) { + if (i == (value->unitSize - 1) + && value->val.str[i] == '\0') + break; + if (!isprint(value->val.str[i])) { + hex = 1; + break; + } + + /* XML is evil, must do this... */ + if ((value->val.str[i] == '<') || + (value->val.str[i] == '>') || + (value->val.str[i] == '"') || + (value->val.str[i] == '&')) + num_chars_to_escape++; + } + + appender(data, indent); + + appender(data, "<text "); + + char *strBuf = 0; + + if (hex) { + appender(data, "encoding=\"hex\" "); + strBuf = (char *) malloc(sizeof(char) + * ((value->unitSize-1) * 2 + 1)); + + /* Unit Size seems to include the size for dtd + It is thus off by 1 + 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], + "%02x", + (unsigned char) value->val.str[i]); + + strBuf[(value->unitSize-1) * 2] = '\0'; + } + else { + int j; + /* escape the XML disallowed chars */ + strBuf = (char *) + malloc(sizeof(char) * + (value->unitSize + 1 + num_chars_to_escape * 4)); + for (i = 0, j = 0; i < value->unitSize; i++) { + if (value->val.str[i] == '&') { + strBuf[j++] = '&'; + strBuf[j++] = 'a'; + strBuf[j++] = 'm'; + strBuf[j++] = 'p'; + } + else if (value->val.str[i] == '<') { + strBuf[j++] = '&'; + strBuf[j++] = 'l'; + strBuf[j++] = 't'; + } + else if (value->val.str[i] == '>') { + strBuf[j++] = '&'; + strBuf[j++] = 'g'; + strBuf[j++] = 't'; + } + else if (value->val.str[i] == '"') { + strBuf[j++] = '&'; + strBuf[j++] = 'q'; + strBuf[j++] = 'u'; + strBuf[j++] = 'o'; + strBuf[j++] = 't'; + } + else { + strBuf[j++] = value->val.str[i]; + } + } + + strBuf[j] = '\0'; + } + + appender(data, "value=\""); + appender(data, strBuf); + appender(data, "\" />\n"); + free(strBuf); + break; + } + + case SDP_URL_STR8: + case SDP_URL_STR16: + case SDP_URL_STR32: + appender(data, indent); + appender(data, "<url value=\""); + appender(data, value->val.str); + appender(data, "\" />\n"); + break; + + case SDP_SEQ8: + case SDP_SEQ16: + case SDP_SEQ32: + appender(data, indent); + appender(data, "<sequence>\n"); + + convert_raw_data_to_xml(value->val.dataseq, + indent_level + 1, data, appender); + + appender(data, indent); + appender(data, "</sequence>\n"); + + break; + + case SDP_ALT8: + case SDP_ALT16: + case SDP_ALT32: + appender(data, indent); + + appender(data, "<alternate>\n"); + + convert_raw_data_to_xml(value->val.dataseq, + indent_level + 1, data, appender); + appender(data, indent); + + appender(data, "</alternate>\n"); + + break; + } + + convert_raw_data_to_xml(value->next, indent_level, data, appender); +} + +struct conversion_data { + void *data; + void (*appender)(void *data, const char *); +}; + +static void convert_raw_attr_to_xml_func(void *val, void *data) +{ + struct conversion_data *cd = (struct conversion_data *) data; + sdp_data_t *value = (sdp_data_t *) val; + char buf[STRBUFSIZE]; + + buf[STRBUFSIZE - 1] = '\0'; + snprintf(buf, STRBUFSIZE - 1, "\t<attribute id=\"0x%04x\">\n", + value->attrId); + cd->appender(cd->data, buf); + + if (data) + convert_raw_data_to_xml(value, 2, cd->data, cd->appender); + else + cd->appender(cd->data, "\t\tNULL\n"); + + cd->appender(cd->data, "\t</attribute>\n"); +} + +/* + * 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 *)) +{ + struct conversion_data cd; + + cd.data = data; + cd.appender = appender; + + if (rec && rec->attrlist) { + appender(data, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n\n"); + appender(data, "<record>\n"); + sdp_list_foreach(rec->attrlist, + convert_raw_attr_to_xml_func, &cd); + appender(data, "</record>\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, sdp_record_t *record) +{ + sdp_data_t *ret; + char *endptr; + uint32_t val; + uint16_t val2; + int len; + + len = strlen(data); + + if (len == 36) { + ret = sdp_xml_parse_uuid128(data); + goto result; + } + + val = strtoll(data, &endptr, 16); + + /* Couldn't parse */ + if (*endptr != '\0') + return NULL; + + if (val > USHRT_MAX) { + ret = sdp_data_alloc(SDP_UUID32, &val); + goto result; + } + + val2 = val; + + ret = sdp_data_alloc(SDP_UUID16, &val2); + +result: + if (record && ret) + sdp_pattern_add_uuid(record, &ret->val.uuid); + + return ret; +} + +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 >> 1] = strtoul(buf, 0, 16); + } + + ret = sdp_data_alloc(dtd, &val); + break; + } + + }; + + return ret; +} + +static char *sdp_xml_parse_string_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; +} + +sdp_data_t *sdp_xml_parse_url(const char *data) +{ + uint8_t dtd = SDP_URL_STR8; + char *url; + uint32_t length; + sdp_data_t *ret; + + url = sdp_xml_parse_string_decode(data, + SDP_XML_ENCODING_NORMAL, &length); + + if (length > UCHAR_MAX) + dtd = SDP_URL_STR16; + + ret = sdp_data_alloc_with_length(dtd, url, length); + + debug("URL size %d length %d: -->%s<--", ret->unitSize, length, url); + + free(url); + + return ret; +} + +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_string_decode(data, encoding, &length); + + if (length > UCHAR_MAX) + dtd = SDP_TEXT_STR16; + + ret = sdp_data_alloc_with_length(dtd, text, length); + + debug("Text size %d length %d: -->%s<--", ret->unitSize, length, text); + + free(text); + + return ret; +} + +sdp_data_t *sdp_xml_parse_nil(const char *data) +{ + return sdp_data_alloc(SDP_DATA_NIL, 0); +} + +#define DEFAULT_XML_DATA_SIZE 1024 + +struct sdp_xml_data *sdp_xml_data_alloc() +{ + struct sdp_xml_data *elem; + + elem = malloc(sizeof(struct sdp_xml_data)); + if (!elem) + return NULL; + + memset(elem, 0, 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'; + + return elem; +} + +void sdp_xml_data_free(struct sdp_xml_data *elem) +{ + if (elem->data) + sdp_data_free(elem->data); + + if (elem->name) + free(elem->name); + + if (elem->text) + + free(elem->text); + free(elem); +} + +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; +} + +sdp_data_t *sdp_xml_parse_datatype(const char *el, struct sdp_xml_data *elem, + sdp_record_t *record) +{ + const char *data = elem->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, record); + else if (!strcmp(el, "url")) + return sdp_xml_parse_url(data); + else if (!strcmp(el, "text")) + return sdp_xml_parse_text(data, elem->type); + else if (!strcmp(el, "nil")) + return sdp_xml_parse_nil(data); + + return NULL; +} |