From e1046e6b02c2eef72ad63f2a61949e1ca606f63f Mon Sep 17 00:00:00 2001 From: Claudio Takahasi Date: Fri, 10 Nov 2006 17:59:14 +0000 Subject: Added raw service record to xml conversion functions --- common/Makefile.am | 2 +- common/sdp-xml.c | 400 ++++++++++++++++++++++++++++++++++++++++++++++++ common/sdp-xml.h | 35 +++++ hcid/dbus-adapter.c | 15 +- hcid/dbus-sdp.c | 142 ++++++++++++++++- hcid/dbus-sdp.h | 7 +- hcid/service-record.dtd | 29 +++- 7 files changed, 606 insertions(+), 24 deletions(-) create mode 100644 common/sdp-xml.c create mode 100644 common/sdp-xml.h diff --git a/common/Makefile.am b/common/Makefile.am index 7249a51f..d71ca3b6 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -4,7 +4,7 @@ noinst_LIBRARIES = libhelper.a libhelper_a_SOURCES = oui.h oui.c list.h list.c \ textfile.h textfile.c helper.h helper.c \ glib-ectomy.h glib-ectomy.c logging.h logging.c \ - dbus.h dbus.c + dbus.h dbus.c sdp-xml.h sdp-xml.c noinst_PROGRAMS = test_textfile diff --git a/common/sdp-xml.c b/common/sdp-xml.c new file mode 100644 index 00000000..5c6dd877 --- /dev/null +++ b/common/sdp-xml.c @@ -0,0 +1,400 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-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 "sdp-xml.h" + +#include +#include +#include +#include +#include + +#define STRBUFSIZE 256 +#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, "\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, "val.uint8); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_UINT16: + appender(data, indent); + appender(data, "val.uint16); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_UINT32: + appender(data, indent); + appender(data, "val.uint32); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_UINT64: + appender(data, indent); + appender(data, "val.uint64); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_UINT128: + appender(data, indent); + appender(data, "val.uint128.data[i]); + } + + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_INT8: + appender(data, indent); + appender(data, "val.int8); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_INT16: + appender(data, indent); + appender(data, "val.int16); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_INT32: + appender(data, indent); + appender(data, "val.int32); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_INT64: + appender(data, indent); + appender(data, "val.int64); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_INT128: + appender(data, indent); + appender(data, "val.int128.data[i]); + } + appender(data, buf); + + appender(data, "\" />\n"); + break; + case SDP_UUID16: + appender(data, indent); + appender(data, "val.uuid.value.uuid16); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_UUID32: + appender(data, indent); + appender(data, "val.uuid.value.uuid32); + appender(data, buf); + appender(data, "\" />\n"); + break; + case SDP_UUID128: + appender(data, indent); + appender(data, "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, "unitSize * 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 * 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, "val.str); + appender(data, "\" />\n"); + break; + case SDP_SEQ8: + case SDP_SEQ16: + case SDP_SEQ32: + appender(data, indent); + appender(data, "\n"); + + convert_raw_data_to_xml(value->val.dataseq, + indent_level + 1, data, + appender); + + appender(data, indent); + appender(data, "\n"); + + break; + case SDP_ALT8: + case SDP_ALT16: + case SDP_ALT32: + appender(data, indent); + + appender(data, "\n"); + + convert_raw_data_to_xml(value->val.dataseq, + 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); +} + +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\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\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, "\n\n"); + appender(data, "\n"); + sdp_list_foreach(rec->attrlist, + convert_raw_attr_to_xml_func, &cd); + appender(data, "\n"); + } +} + diff --git a/common/sdp-xml.h b/common/sdp-xml.h new file mode 100644 index 00000000..fe781f57 --- /dev/null +++ b/common/sdp-xml.h @@ -0,0 +1,35 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-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 + * + */ + + +#ifndef __SDP_XML_H__ +#define __SDP_XML_H__ + +#include +#include + +void convert_sdp_record_to_xml(sdp_record_t * rec, + void *userData, + void (*appendFunc) (void *, const char *)); + +#endif /* __SDP_XML_H__ */ diff --git a/hcid/dbus-adapter.c b/hcid/dbus-adapter.c index 1aeccbc6..3edbabf2 100644 --- a/hcid/dbus-adapter.c +++ b/hcid/dbus-adapter.c @@ -1063,24 +1063,13 @@ static DBusHandlerResult adapter_set_name(DBusConnection *conn, static DBusHandlerResult adapter_get_remote_svc(DBusConnection *conn, DBusMessage *msg, void *data) { - return get_remote_svc_rec(conn, msg, data); + return get_remote_svc_rec(conn, msg, data, SDP_FORMAT_BINARY); } static DBusHandlerResult adapter_get_remote_svc_xml(DBusConnection *conn, DBusMessage *msg, void *data) { - DBusMessage *reply; - const char *result = "" - ""; - - reply = dbus_message_new_method_return(msg); - if (!reply) - return DBUS_HANDLER_RESULT_NEED_MEMORY; - - dbus_message_append_args(reply, DBUS_TYPE_STRING, &result, - DBUS_TYPE_INVALID); - - return send_message_and_unref(conn, reply); + return get_remote_svc_rec(conn, msg, data, SDP_FORMAT_XML); } static DBusHandlerResult adapter_get_remote_svc_handles(DBusConnection *conn, diff --git a/hcid/dbus-sdp.c b/hcid/dbus-sdp.c index f4f462f6..6ad33f3a 100644 --- a/hcid/dbus-sdp.c +++ b/hcid/dbus-sdp.c @@ -55,8 +55,10 @@ #include "dbus-adapter.h" #include "dbus-error.h" #include "dbus-sdp.h" +#include "sdp-xml.h" #define MAX_IDENTIFIER_LEN 29 /* "XX:XX:XX:XX:XX:XX/0xYYYYYYYY\0" */ +#define DEFAULT_XML_BUFFER_SIZE 1024 struct service_provider { char *owner; /* null for remote services or unique name if local */ @@ -98,6 +100,33 @@ typedef struct { char *info_name; } sdp_service_t; +typedef struct { + int size; + char *str; +} string_t; + +static void append_and_grow_string(void *data, const char *str) +{ + string_t *string = (string_t *)data; + char *newbuf; + + int oldlen = strlen(string->str); + int newlen = strlen(str); + + if ((oldlen + newlen + 1) > string->size) { + newbuf = (char *) malloc(string->size * 2); + if (!newbuf) + return; + + memcpy(newbuf, string->str, oldlen+1); + string->size *= 2; + free(string->str); + string->str = newbuf; + } + + strcat(string->str, str); +} + /* FIXME: move to a common file */ sdp_service_t sdp_service[] = { { "vcp", VIDEO_CONF_SVCLASS_ID, "Video Conference" }, @@ -610,6 +639,78 @@ failed: transaction_context_free(ctxt); } +static void remote_svc_rec_completed_xml_cb(uint8_t type, uint16_t err, + uint8_t *rsp, size_t size, + void *udata) +{ + struct transaction_context *ctxt = udata; + sdp_record_t *rec = NULL; + DBusMessage *reply; + const char *dst; + int scanned; + string_t result; + + if (!ctxt) + return; + + if (err == 0xffff) { + /* Check for protocol error or I/O error */ + int sdp_err = sdp_get_error(ctxt->session); + if (sdp_err < 0) { + error("search failed: Invalid session!"); + error_failed(ctxt->conn, ctxt->rq, EINVAL); + goto failed; + } + + error("search failed: %s (%d)", strerror(sdp_err), sdp_err); + error_failed(ctxt->conn, ctxt->rq, sdp_err); + goto failed; + } + + if (type == SDP_ERROR_RSP) { + error_sdp_failed(ctxt->conn, ctxt->rq, err); + goto failed; + } + + /* check response PDU ID */ + if (type != SDP_SVC_ATTR_RSP) { + error("SDP error: %s (%d)", strerror(EPROTO), EPROTO); + error_failed(ctxt->conn, ctxt->rq, EPROTO); + goto failed; + } + + dbus_message_get_args(ctxt->rq, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_INVALID); + + reply = dbus_message_new_method_return(ctxt->rq); + + result.str = 0; + + rec = sdp_extract_pdu(rsp, &scanned); + if (rec == NULL) { + error("SVC REC is null"); + goto done; + } + + result.str = malloc(sizeof(char) * DEFAULT_XML_BUFFER_SIZE); + result.size = DEFAULT_XML_BUFFER_SIZE; + + sdp_cache_append(NULL, dst, rec); + + convert_sdp_record_to_xml(rec, &result, append_and_grow_string); + + dbus_message_append_args(reply, + DBUS_TYPE_STRING, &result.str, + DBUS_TYPE_INVALID); + +done: + send_message_and_unref(ctxt->conn, reply); + free(result.str); +failed: + transaction_context_free(ctxt); +} + static void remote_svc_handles_completed_cb(uint8_t type, uint16_t err, uint8_t *rsp, size_t size, void *udata) { struct transaction_context *ctxt = udata; @@ -900,12 +1001,45 @@ fail: return err; } -DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data) +static int remote_svc_rec_conn_xml_cb(struct transaction_context *ctxt) +{ + sdp_list_t *attrids = NULL; + uint32_t range = 0x0000ffff; + const char *dst; + uint32_t handle; + int err = 0; + + if (sdp_set_notify(ctxt->session, remote_svc_rec_completed_xml_cb, ctxt) < 0) { + err = -EINVAL; + goto fail; + } + + dbus_message_get_args(ctxt->rq, NULL, + DBUS_TYPE_STRING, &dst, + DBUS_TYPE_UINT32, &handle, + DBUS_TYPE_INVALID); + + attrids = sdp_list_append(NULL, &range); + /* Create/send the search request and set the callback to indicate the request completion */ + if (sdp_service_attr_async(ctxt->session, handle, SDP_ATTR_REQ_RANGE, attrids) < 0) { + err = -sdp_get_error(ctxt->session); + goto fail; + } + +fail: + if (attrids) + sdp_list_free(attrids, NULL); + + return err; +} + +DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data, sdp_format_t format) { struct adapter *adapter = data; const char *dst; uint32_t handle; int err = 0; + connect_cb_t *cb; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &dst, @@ -916,8 +1050,12 @@ DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, voi if (find_pending_connect(dst)) return error_service_search_in_progress(conn, msg); + cb = remote_svc_rec_conn_cb; + if (format == SDP_FORMAT_XML) + cb = remote_svc_rec_conn_xml_cb; + if (!connect_request(conn, msg, adapter->dev_id, - dst, remote_svc_rec_conn_cb, &err)) { + dst, cb, &err)) { error("Search request failed: %s (%d)", strerror(err), err); return error_failed(conn, msg, err); } diff --git a/hcid/dbus-sdp.h b/hcid/dbus-sdp.h index 378a1456..4760c900 100644 --- a/hcid/dbus-sdp.h +++ b/hcid/dbus-sdp.h @@ -30,11 +30,16 @@ #define SDP_INTERFACE "org.bluez.SDP" +typedef enum { + SDP_FORMAT_XML, + SDP_FORMAT_BINARY +} sdp_format_t; + DBusHandlerResult handle_sdp_method(DBusConnection *conn, DBusMessage *msg, void *data); DBusHandlerResult get_remote_svc_handles(DBusConnection *conn, DBusMessage *msg, void *data); -DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data); +DBusHandlerResult get_remote_svc_rec(DBusConnection *conn, DBusMessage *msg, void *data, sdp_format_t format); uint16_t sdp_str2svclass(const char *str); diff --git a/hcid/service-record.dtd b/hcid/service-record.dtd index 86b2bc43..000a5c37 100644 --- a/hcid/service-record.dtd +++ b/hcid/service-record.dtd @@ -1,14 +1,15 @@ - + - + - + + @@ -31,8 +32,22 @@ - + + + + + + + + + + + - - - + + + + + + + -- cgit