mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-16 08:44:38 +08:00
864 lines
21 KiB
C
864 lines
21 KiB
C
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2006-2007 Nokia Corporation
|
|
* Copyright (C) 2004-2009 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 <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <bluetooth/bluetooth.h>
|
|
#include <bluetooth/hci.h>
|
|
#include <bluetooth/hci_lib.h>
|
|
#include <bluetooth/sdp.h>
|
|
#include <bluetooth/sdp_lib.h>
|
|
|
|
#include <gdbus.h>
|
|
|
|
#include "sdpd.h"
|
|
#include "sdp-xml.h"
|
|
#include "plugin.h"
|
|
#include "adapter.h"
|
|
#include "error.h"
|
|
#include "logging.h"
|
|
|
|
#define SERVICE_INTERFACE "org.bluez.Service"
|
|
|
|
static DBusConnection *connection;
|
|
|
|
struct record_data {
|
|
uint32_t handle;
|
|
char *sender;
|
|
guint listener_id;
|
|
struct service_adapter *serv_adapter;
|
|
};
|
|
|
|
struct context_data {
|
|
sdp_record_t *record;
|
|
sdp_data_t attr_data;
|
|
struct sdp_xml_data *stack_head;
|
|
uint16_t attr_id;
|
|
};
|
|
|
|
struct pending_auth {
|
|
DBusConnection *conn;
|
|
DBusMessage *msg;
|
|
char *sender;
|
|
bdaddr_t dst;
|
|
char uuid[MAX_LEN_UUID_STR];
|
|
};
|
|
|
|
struct service_adapter {
|
|
struct btd_adapter *adapter;
|
|
GSList *pending_list;
|
|
GSList *records;
|
|
};
|
|
|
|
static struct service_adapter *serv_adapter_any = NULL;
|
|
|
|
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 element_start(GMarkupParseContext *context,
|
|
const gchar *element_name, const gchar **attribute_names,
|
|
const gchar **attribute_values, gpointer user_data, GError **err)
|
|
{
|
|
struct context_data *ctx_data = user_data;
|
|
|
|
if (!strcmp(element_name, "record"))
|
|
return;
|
|
|
|
if (!strcmp(element_name, "attribute")) {
|
|
int i;
|
|
for (i = 0; attribute_names[i]; i++) {
|
|
if (!strcmp(attribute_names[i], "id")) {
|
|
ctx_data->attr_id = strtol(attribute_values[i], 0, 0);
|
|
break;
|
|
}
|
|
}
|
|
debug("New attribute 0x%04x", ctx_data->attr_id);
|
|
return;
|
|
}
|
|
|
|
if (ctx_data->stack_head) {
|
|
struct sdp_xml_data *newelem = sdp_xml_data_alloc();
|
|
newelem->next = ctx_data->stack_head;
|
|
ctx_data->stack_head = newelem;
|
|
} else {
|
|
ctx_data->stack_head = sdp_xml_data_alloc();
|
|
ctx_data->stack_head->next = NULL;
|
|
}
|
|
|
|
if (!strcmp(element_name, "sequence"))
|
|
ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL);
|
|
else if (!strcmp(element_name, "alternate"))
|
|
ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL);
|
|
else {
|
|
int i;
|
|
/* Parse value, name, encoding */
|
|
for (i = 0; attribute_names[i]; i++) {
|
|
if (!strcmp(attribute_names[i], "value")) {
|
|
int curlen = strlen(ctx_data->stack_head->text);
|
|
int attrlen = strlen(attribute_values[i]);
|
|
|
|
/* Ensure we're big enough */
|
|
while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) {
|
|
sdp_xml_data_expand(ctx_data->stack_head);
|
|
}
|
|
|
|
memcpy(ctx_data->stack_head->text + curlen,
|
|
attribute_values[i], attrlen);
|
|
ctx_data->stack_head->text[curlen + attrlen] = '\0';
|
|
}
|
|
|
|
if (!strcmp(attribute_names[i], "encoding")) {
|
|
if (!strcmp(attribute_values[i], "hex"))
|
|
ctx_data->stack_head->type = 1;
|
|
}
|
|
|
|
if (!strcmp(attribute_names[i], "name")) {
|
|
ctx_data->stack_head->name = strdup(attribute_values[i]);
|
|
}
|
|
}
|
|
|
|
ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name,
|
|
ctx_data->stack_head, ctx_data->record);
|
|
|
|
if (ctx_data->stack_head->data == NULL)
|
|
error("Can't parse element %s", element_name);
|
|
}
|
|
}
|
|
|
|
static void element_end(GMarkupParseContext *context,
|
|
const gchar *element_name, gpointer user_data, GError **err)
|
|
{
|
|
struct context_data *ctx_data = user_data;
|
|
struct sdp_xml_data *elem;
|
|
|
|
if (!strcmp(element_name, "record"))
|
|
return;
|
|
|
|
if (!strcmp(element_name, "attribute")) {
|
|
if (ctx_data->stack_head && ctx_data->stack_head->data) {
|
|
int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id,
|
|
ctx_data->stack_head->data);
|
|
if (ret == -1)
|
|
debug("Trouble adding attribute\n");
|
|
|
|
ctx_data->stack_head->data = NULL;
|
|
sdp_xml_data_free(ctx_data->stack_head);
|
|
ctx_data->stack_head = NULL;
|
|
} else {
|
|
debug("No data for attribute 0x%04x\n", ctx_data->attr_id);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!strcmp(element_name, "sequence")) {
|
|
ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
|
|
|
|
if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
|
|
ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
|
|
ctx_data->stack_head->data->dtd = SDP_SEQ32;
|
|
} else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
|
|
ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
|
|
ctx_data->stack_head->data->dtd = SDP_SEQ16;
|
|
} else {
|
|
ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
|
|
}
|
|
} else if (!strcmp(element_name, "alternate")) {
|
|
ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
|
|
|
|
if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
|
|
ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
|
|
ctx_data->stack_head->data->dtd = SDP_ALT32;
|
|
} else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
|
|
ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
|
|
ctx_data->stack_head->data->dtd = SDP_ALT16;
|
|
} else {
|
|
ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
|
|
}
|
|
}
|
|
|
|
if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
|
|
ctx_data->stack_head->next->data) {
|
|
switch (ctx_data->stack_head->next->data->dtd) {
|
|
case SDP_SEQ8:
|
|
case SDP_SEQ16:
|
|
case SDP_SEQ32:
|
|
case SDP_ALT8:
|
|
case SDP_ALT16:
|
|
case SDP_ALT32:
|
|
ctx_data->stack_head->next->data->val.dataseq =
|
|
sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq,
|
|
ctx_data->stack_head->data);
|
|
ctx_data->stack_head->data = NULL;
|
|
break;
|
|
}
|
|
|
|
elem = ctx_data->stack_head;
|
|
ctx_data->stack_head = ctx_data->stack_head->next;
|
|
|
|
sdp_xml_data_free(elem);
|
|
}
|
|
}
|
|
|
|
static GMarkupParser parser = {
|
|
element_start, element_end, NULL, NULL, NULL
|
|
};
|
|
|
|
static sdp_record_t *sdp_xml_parse_record(const char *data, int size)
|
|
{
|
|
GMarkupParseContext *ctx;
|
|
struct context_data *ctx_data;
|
|
sdp_record_t *record;
|
|
|
|
ctx_data = malloc(sizeof(*ctx_data));
|
|
if (!ctx_data)
|
|
return NULL;
|
|
|
|
record = sdp_record_alloc();
|
|
if (!record) {
|
|
free(ctx_data);
|
|
return NULL;
|
|
}
|
|
|
|
memset(ctx_data, 0, sizeof(*ctx_data));
|
|
ctx_data->record = record;
|
|
|
|
ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL);
|
|
|
|
if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
|
|
error("XML parsing error");
|
|
g_markup_parse_context_free(ctx);
|
|
sdp_record_free(record);
|
|
free(ctx_data);
|
|
return NULL;
|
|
}
|
|
|
|
g_markup_parse_context_free(ctx);
|
|
|
|
free(ctx_data);
|
|
|
|
return record;
|
|
}
|
|
|
|
static struct record_data *find_record(struct service_adapter *serv_adapter,
|
|
uint32_t handle, const char *sender)
|
|
{
|
|
GSList *list;
|
|
|
|
for (list = serv_adapter->records; list; list = list->next) {
|
|
struct record_data *data = list->data;
|
|
if (handle == data->handle && !strcmp(sender, data->sender))
|
|
return data;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct pending_auth *next_pending(struct service_adapter *serv_adapter)
|
|
{
|
|
GSList *l = serv_adapter->pending_list;
|
|
|
|
if (l) {
|
|
struct pending_auth *auth = l->data;
|
|
return auth;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct pending_auth *find_pending_by_sender(
|
|
struct service_adapter *serv_adapter,
|
|
const char *sender)
|
|
{
|
|
GSList *l = serv_adapter->pending_list;
|
|
|
|
for (; l; l = l->next) {
|
|
struct pending_auth *auth = l->data;
|
|
if (g_str_equal(auth->sender, sender))
|
|
return auth;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void exit_callback(DBusConnection *conn, void *user_data)
|
|
{
|
|
struct record_data *user_record = user_data;
|
|
struct service_adapter *serv_adapter = user_record->serv_adapter;
|
|
struct pending_auth *auth;
|
|
|
|
debug("remove record");
|
|
|
|
serv_adapter->records = g_slist_remove(serv_adapter->records,
|
|
user_record);
|
|
|
|
auth = find_pending_by_sender(serv_adapter, user_record->sender);
|
|
if (auth) {
|
|
serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
|
|
auth);
|
|
g_free(auth);
|
|
}
|
|
|
|
remove_record_from_server(user_record->handle);
|
|
|
|
g_free(user_record->sender);
|
|
g_free(user_record);
|
|
}
|
|
|
|
static inline DBusMessage *invalid_arguments(DBusMessage *msg)
|
|
{
|
|
return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
|
|
"Invalid arguments in method call");
|
|
}
|
|
|
|
static inline DBusMessage *not_available(DBusMessage *msg)
|
|
{
|
|
return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable",
|
|
"Not Available");
|
|
}
|
|
|
|
static inline DBusMessage *failed(DBusMessage *msg)
|
|
{
|
|
return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "Failed");
|
|
}
|
|
|
|
static inline DBusMessage *failed_strerror(DBusMessage *msg, int err)
|
|
{
|
|
return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
|
|
strerror(err));
|
|
}
|
|
|
|
static inline DBusMessage *not_authorized(DBusMessage *msg)
|
|
{
|
|
return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAuthorized",
|
|
"Not Authorized");
|
|
}
|
|
|
|
static inline DBusMessage *does_not_exist(DBusMessage *msg)
|
|
{
|
|
return g_dbus_create_error(msg, ERROR_INTERFACE ".DoesNotExist",
|
|
"Does Not Exist");
|
|
}
|
|
|
|
static int add_xml_record(DBusConnection *conn, const char *sender,
|
|
struct service_adapter *serv_adapter,
|
|
const char *record, dbus_uint32_t *handle)
|
|
{
|
|
struct record_data *user_record;
|
|
sdp_record_t *sdp_record;
|
|
bdaddr_t src;
|
|
|
|
sdp_record = sdp_xml_parse_record(record, strlen(record));
|
|
if (!sdp_record) {
|
|
error("Parsing of XML service record failed");
|
|
return -EIO;
|
|
}
|
|
|
|
if (serv_adapter->adapter)
|
|
adapter_get_address(serv_adapter->adapter, &src);
|
|
else
|
|
bacpy(&src, BDADDR_ANY);
|
|
|
|
if (add_record_to_server(&src, sdp_record) < 0) {
|
|
error("Failed to register service record");
|
|
sdp_record_free(sdp_record);
|
|
return -EIO;
|
|
}
|
|
|
|
user_record = g_new0(struct record_data, 1);
|
|
user_record->handle = sdp_record->handle;
|
|
user_record->sender = g_strdup(sender);
|
|
user_record->serv_adapter = serv_adapter;
|
|
user_record->listener_id = g_dbus_add_disconnect_watch(conn, sender,
|
|
exit_callback, user_record, NULL);
|
|
|
|
serv_adapter->records = g_slist_append(serv_adapter->records,
|
|
user_record);
|
|
|
|
debug("listener_id %d", user_record->listener_id);
|
|
|
|
*handle = user_record->handle;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static DBusMessage *update_record(DBusConnection *conn, DBusMessage *msg,
|
|
struct service_adapter *serv_adapter,
|
|
dbus_uint32_t handle, sdp_record_t *sdp_record)
|
|
{
|
|
bdaddr_t src;
|
|
int err;
|
|
|
|
if (remove_record_from_server(handle) < 0) {
|
|
sdp_record_free(sdp_record);
|
|
return g_dbus_create_error(msg,
|
|
ERROR_INTERFACE ".NotAvailable",
|
|
"Not Available");
|
|
}
|
|
|
|
if (serv_adapter->adapter)
|
|
adapter_get_address(serv_adapter->adapter, &src);
|
|
else
|
|
bacpy(&src, BDADDR_ANY);
|
|
|
|
sdp_record->handle = handle;
|
|
err = add_record_to_server(&src, sdp_record);
|
|
if (err < 0) {
|
|
sdp_record_free(sdp_record);
|
|
error("Failed to update the service record");
|
|
return g_dbus_create_error(msg,
|
|
ERROR_INTERFACE ".Failed",
|
|
strerror(EIO));
|
|
}
|
|
|
|
return dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static DBusMessage *update_xml_record(DBusConnection *conn,
|
|
DBusMessage *msg,
|
|
struct service_adapter *serv_adapter)
|
|
{
|
|
struct record_data *user_record;
|
|
sdp_record_t *sdp_record;
|
|
const char *record;
|
|
dbus_uint32_t handle;
|
|
int len;
|
|
|
|
if (dbus_message_get_args(msg, NULL,
|
|
DBUS_TYPE_UINT32, &handle,
|
|
DBUS_TYPE_STRING, &record,
|
|
DBUS_TYPE_INVALID) == FALSE)
|
|
return NULL;
|
|
|
|
len = (record ? strlen(record) : 0);
|
|
if (len == 0)
|
|
return invalid_arguments(msg);
|
|
|
|
user_record = find_record(serv_adapter, handle,
|
|
dbus_message_get_sender(msg));
|
|
if (!user_record)
|
|
return g_dbus_create_error(msg,
|
|
ERROR_INTERFACE ".NotAvailable",
|
|
"Not Available");
|
|
|
|
sdp_record = sdp_xml_parse_record(record, len);
|
|
if (!sdp_record) {
|
|
error("Parsing of XML service record failed");
|
|
sdp_record_free(sdp_record);
|
|
return g_dbus_create_error(msg,
|
|
ERROR_INTERFACE ".Failed",
|
|
strerror(EIO));
|
|
}
|
|
|
|
return update_record(conn, msg, serv_adapter, handle, sdp_record);
|
|
}
|
|
|
|
static int remove_record(DBusConnection *conn, const char *sender,
|
|
struct service_adapter *serv_adapter,
|
|
dbus_uint32_t handle)
|
|
{
|
|
struct record_data *user_record;
|
|
|
|
debug("remove record 0x%x", handle);
|
|
|
|
user_record = find_record(serv_adapter, handle, sender);
|
|
if (!user_record)
|
|
return -1;
|
|
|
|
debug("listner_id %d", user_record->listener_id);
|
|
|
|
g_dbus_remove_watch(conn, user_record->listener_id);
|
|
|
|
exit_callback(conn, user_record);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static DBusMessage *add_service_record(DBusConnection *conn,
|
|
DBusMessage *msg, void *data)
|
|
{
|
|
struct service_adapter *serv_adapter = data;
|
|
DBusMessage *reply;
|
|
const char *sender, *record;
|
|
dbus_uint32_t handle;
|
|
int err;
|
|
|
|
if (dbus_message_get_args(msg, NULL,
|
|
DBUS_TYPE_STRING, &record, DBUS_TYPE_INVALID) == FALSE)
|
|
return NULL;
|
|
|
|
sender = dbus_message_get_sender(msg);
|
|
err = add_xml_record(conn, sender, serv_adapter, record, &handle);
|
|
if (err < 0)
|
|
return failed_strerror(msg, err);
|
|
|
|
reply = dbus_message_new_method_return(msg);
|
|
if (!reply)
|
|
return NULL;
|
|
|
|
dbus_message_append_args(reply, DBUS_TYPE_UINT32, &handle,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
return reply;
|
|
}
|
|
|
|
static DBusMessage *update_service_record(DBusConnection *conn,
|
|
DBusMessage *msg, void *data)
|
|
{
|
|
struct service_adapter *serv_adapter = data;
|
|
|
|
return update_xml_record(conn, msg, serv_adapter);
|
|
}
|
|
|
|
static DBusMessage *remove_service_record(DBusConnection *conn,
|
|
DBusMessage *msg, void *data)
|
|
{
|
|
struct service_adapter *serv_adapter = data;
|
|
dbus_uint32_t handle;
|
|
const char *sender;
|
|
|
|
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
|
|
DBUS_TYPE_INVALID) == FALSE)
|
|
return NULL;
|
|
|
|
sender = dbus_message_get_sender(msg);
|
|
|
|
if (remove_record(conn, sender, serv_adapter, handle) < 0)
|
|
return not_available(msg);
|
|
|
|
return dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static void auth_cb(DBusError *derr, void *user_data)
|
|
{
|
|
struct service_adapter *serv_adapter = user_data;
|
|
DBusMessage *reply;
|
|
struct pending_auth *auth;
|
|
bdaddr_t src;
|
|
|
|
auth = next_pending(serv_adapter);
|
|
if (auth == NULL) {
|
|
info("Authorization cancelled: Client exited");
|
|
return;
|
|
}
|
|
|
|
if (derr) {
|
|
error("Access denied: %s", derr->message);
|
|
|
|
reply = not_authorized(auth->msg);
|
|
dbus_message_unref(auth->msg);
|
|
g_dbus_send_message(auth->conn, reply);
|
|
goto done;
|
|
}
|
|
|
|
g_dbus_send_reply(auth->conn, auth->msg,
|
|
DBUS_TYPE_INVALID);
|
|
|
|
done:
|
|
dbus_connection_unref(auth->conn);
|
|
|
|
serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
|
|
auth);
|
|
g_free(auth);
|
|
|
|
auth = next_pending(serv_adapter);
|
|
if (auth == NULL)
|
|
return;
|
|
|
|
if (serv_adapter->adapter)
|
|
adapter_get_address(serv_adapter->adapter, &src);
|
|
else
|
|
bacpy(&src, BDADDR_ANY);
|
|
|
|
btd_request_authorization(&src, &auth->dst,
|
|
auth->uuid, auth_cb, serv_adapter);
|
|
}
|
|
|
|
static DBusMessage *request_authorization(DBusConnection *conn,
|
|
DBusMessage *msg, void *data)
|
|
{
|
|
struct record_data *user_record;
|
|
struct service_adapter *serv_adapter = data;
|
|
sdp_record_t *record;
|
|
sdp_list_t *services;
|
|
const char *sender;
|
|
dbus_uint32_t handle;
|
|
const char *address;
|
|
struct pending_auth *auth;
|
|
char uuid_str[MAX_LEN_UUID_STR];
|
|
uuid_t *uuid, *uuid128;
|
|
bdaddr_t src;
|
|
|
|
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
|
|
DBUS_TYPE_UINT32, &handle,
|
|
DBUS_TYPE_INVALID) == FALSE)
|
|
return NULL;
|
|
|
|
sender = dbus_message_get_sender(msg);
|
|
if (find_pending_by_sender(serv_adapter, sender))
|
|
return failed(msg);
|
|
|
|
user_record = find_record(serv_adapter, handle, sender);
|
|
if (!user_record) {
|
|
user_record = find_record(serv_adapter_any, handle, sender);
|
|
if (!user_record)
|
|
return not_authorized(msg);
|
|
}
|
|
|
|
record = sdp_record_find(user_record->handle);
|
|
|
|
if (sdp_get_service_classes(record, &services) < 0) {
|
|
sdp_record_free(record);
|
|
return not_authorized(msg);
|
|
}
|
|
|
|
if (services == NULL)
|
|
return not_authorized(msg);
|
|
|
|
uuid = services->data;
|
|
uuid128 = sdp_uuid_to_uuid128(uuid);
|
|
|
|
sdp_list_free(services, bt_free);
|
|
|
|
if (sdp_uuid2strn(uuid128, uuid_str, MAX_LEN_UUID_STR) < 0) {
|
|
bt_free(uuid128);
|
|
return not_authorized(msg);
|
|
}
|
|
bt_free(uuid128);
|
|
|
|
auth = g_new0(struct pending_auth, 1);
|
|
auth->msg = dbus_message_ref(msg);
|
|
auth->conn = dbus_connection_ref(connection);
|
|
auth->sender = user_record->sender;
|
|
memcpy(auth->uuid, uuid_str, MAX_LEN_UUID_STR);
|
|
str2ba(address, &auth->dst);
|
|
|
|
serv_adapter->pending_list = g_slist_append(serv_adapter->pending_list,
|
|
auth);
|
|
|
|
auth = next_pending(serv_adapter);
|
|
if (auth == NULL)
|
|
return does_not_exist(msg);
|
|
|
|
if (serv_adapter->adapter)
|
|
adapter_get_address(serv_adapter->adapter, &src);
|
|
else
|
|
bacpy(&src, BDADDR_ANY);
|
|
|
|
if (btd_request_authorization(&src, &auth->dst, auth->uuid, auth_cb,
|
|
serv_adapter) < 0) {
|
|
serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
|
|
auth);
|
|
g_free(auth);
|
|
return not_authorized(msg);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static DBusMessage *cancel_authorization(DBusConnection *conn,
|
|
DBusMessage *msg, void *data)
|
|
{
|
|
DBusMessage *reply;
|
|
struct service_adapter *serv_adapter = data;
|
|
struct pending_auth *auth;
|
|
const gchar *sender;
|
|
bdaddr_t src;
|
|
|
|
sender = dbus_message_get_sender(msg);
|
|
|
|
auth = find_pending_by_sender(serv_adapter, sender);
|
|
if (auth == NULL)
|
|
return does_not_exist(msg);
|
|
|
|
if (serv_adapter->adapter)
|
|
adapter_get_address(serv_adapter->adapter, &src);
|
|
else
|
|
bacpy(&src, BDADDR_ANY);
|
|
|
|
btd_cancel_authorization(&src, &auth->dst);
|
|
|
|
reply = not_authorized(auth->msg);
|
|
dbus_message_unref(auth->msg);
|
|
g_dbus_send_message(auth->conn, reply);
|
|
|
|
dbus_connection_unref(auth->conn);
|
|
|
|
serv_adapter->pending_list = g_slist_remove(serv_adapter->pending_list,
|
|
auth);
|
|
g_free(auth);
|
|
|
|
auth = next_pending(serv_adapter);
|
|
if (auth == NULL)
|
|
goto done;
|
|
|
|
if (serv_adapter->adapter)
|
|
adapter_get_address(serv_adapter->adapter, &src);
|
|
else
|
|
bacpy(&src, BDADDR_ANY);
|
|
|
|
btd_request_authorization(&src, &auth->dst,
|
|
auth->uuid, auth_cb, serv_adapter);
|
|
|
|
done:
|
|
return dbus_message_new_method_return(msg);
|
|
}
|
|
|
|
static GDBusMethodTable service_methods[] = {
|
|
{ "AddRecord", "s", "u", add_service_record },
|
|
{ "UpdateRecord", "us", "", update_service_record },
|
|
{ "RemoveRecord", "u", "", remove_service_record },
|
|
{ "RequestAuthorization","su", "", request_authorization,
|
|
G_DBUS_METHOD_FLAG_ASYNC},
|
|
{ "CancelAuthorization", "", "", cancel_authorization },
|
|
{ }
|
|
};
|
|
|
|
static void path_unregister(void *data)
|
|
{
|
|
struct service_adapter *serv_adapter = data;
|
|
GSList *l, *next = NULL;
|
|
|
|
for (l = serv_adapter->records; l != NULL; l = next) {
|
|
struct record_data *user_record = l->data;
|
|
|
|
next = l->next;
|
|
|
|
g_dbus_remove_watch(connection, user_record->listener_id);
|
|
exit_callback(connection, user_record);
|
|
}
|
|
}
|
|
|
|
static int register_interface(const char *path, struct btd_adapter *adapter)
|
|
{
|
|
struct service_adapter *serv_adapter;
|
|
|
|
DBG("path %s", path);
|
|
|
|
serv_adapter = g_try_new0(struct service_adapter, 1);
|
|
if (serv_adapter == NULL)
|
|
return -ENOMEM;
|
|
|
|
serv_adapter->adapter = adapter;
|
|
serv_adapter->pending_list = NULL;
|
|
|
|
if (g_dbus_register_interface(connection, path, SERVICE_INTERFACE,
|
|
service_methods, NULL, NULL, serv_adapter,
|
|
path_unregister) == FALSE) {
|
|
error("D-Bus failed to register %s interface",
|
|
SERVICE_INTERFACE);
|
|
g_free(serv_adapter);
|
|
return -EIO;
|
|
}
|
|
|
|
debug("Registered interface %s on path %s", SERVICE_INTERFACE, path);
|
|
|
|
if (serv_adapter->adapter == NULL)
|
|
serv_adapter_any = serv_adapter;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void unregister_interface(const char *path)
|
|
{
|
|
DBG("path %s", path);
|
|
|
|
g_dbus_unregister_interface(connection, path, SERVICE_INTERFACE);
|
|
}
|
|
|
|
static int service_probe(struct btd_adapter *adapter)
|
|
{
|
|
register_interface(adapter_get_path(adapter), adapter);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void service_remove(struct btd_adapter *adapter)
|
|
{
|
|
unregister_interface(adapter_get_path(adapter));
|
|
}
|
|
|
|
static struct btd_adapter_driver service_driver = {
|
|
.name = "service",
|
|
.probe = service_probe,
|
|
.remove = service_remove,
|
|
};
|
|
|
|
static const char *any_path;
|
|
|
|
static int service_init(void)
|
|
{
|
|
int err;
|
|
|
|
connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
|
|
if (connection == NULL)
|
|
return -EIO;
|
|
|
|
any_path = btd_adapter_any_request_path();
|
|
if (any_path != NULL) {
|
|
if (register_interface(any_path, NULL) < 0) {
|
|
btd_adapter_any_release_path();
|
|
any_path = NULL;
|
|
}
|
|
}
|
|
|
|
err = btd_register_adapter_driver(&service_driver);
|
|
if (err < 0) {
|
|
dbus_connection_unref(connection);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void service_exit(void)
|
|
{
|
|
btd_unregister_adapter_driver(&service_driver);
|
|
|
|
if (any_path != NULL) {
|
|
unregister_interface(any_path);
|
|
|
|
btd_adapter_any_release_path();
|
|
any_path = NULL;
|
|
}
|
|
|
|
dbus_connection_unref(connection);
|
|
}
|
|
|
|
BLUETOOTH_PLUGIN_DEFINE(service, VERSION,
|
|
BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, service_init, service_exit)
|