From 799757ccf1d03c33c75bc597cd5ef77741dcb6a7 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 3 Jun 2011 09:17:04 +0000 Subject: Imported upstream 4.91 --- src/storage.c | 1345 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1345 insertions(+) create mode 100644 src/storage.c (limited to 'src/storage.c') diff --git a/src/storage.c b/src/storage.c new file mode 100644 index 0000000..28aea30 --- /dev/null +++ b/src/storage.c @@ -0,0 +1,1345 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2006-2010 Nokia Corporation + * Copyright (C) 2004-2010 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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "textfile.h" +#include "adapter.h" +#include "device.h" +#include "glib-helper.h" +#include "storage.h" + +struct match { + GSList *keys; + char *pattern; +}; + +static inline int create_filename(char *buf, size_t size, + const bdaddr_t *bdaddr, const char *name) +{ + char addr[18]; + + ba2str(bdaddr, addr); + + return create_name(buf, size, STORAGEDIR, addr, name); +} + +int read_device_alias(const char *src, const char *dst, char *alias, size_t size) +{ + char filename[PATH_MAX + 1], *tmp; + int err; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases"); + + tmp = textfile_get(filename, dst); + if (!tmp) + return -ENXIO; + + err = snprintf(alias, size, "%s", tmp); + + free(tmp); + + return err < 0 ? -EIO : 0; +} + +int write_device_alias(const char *src, const char *dst, const char *alias) +{ + char filename[PATH_MAX + 1]; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return textfile_put(filename, dst, alias); +} + +int write_discoverable_timeout(bdaddr_t *bdaddr, int timeout) +{ + char filename[PATH_MAX + 1], str[32]; + + snprintf(str, sizeof(str), "%d", timeout); + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return textfile_put(filename, "discovto", str); +} + +int read_discoverable_timeout(const char *src, int *timeout) +{ + char filename[PATH_MAX + 1], *str; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "config"); + + str = textfile_get(filename, "discovto"); + if (!str) + return -ENOENT; + + if (sscanf(str, "%d", timeout) != 1) { + free(str); + return -ENOENT; + } + + free(str); + + return 0; +} + +int write_pairable_timeout(bdaddr_t *bdaddr, int timeout) +{ + char filename[PATH_MAX + 1], str[32]; + + snprintf(str, sizeof(str), "%d", timeout); + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return textfile_put(filename, "pairto", str); +} + +int read_pairable_timeout(const char *src, int *timeout) +{ + char filename[PATH_MAX + 1], *str; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "config"); + + str = textfile_get(filename, "pairto"); + if (!str) + return -ENOENT; + + if (sscanf(str, "%d", timeout) != 1) { + free(str); + return -ENOENT; + } + + free(str); + + return 0; +} + +int write_device_mode(bdaddr_t *bdaddr, const char *mode) +{ + char filename[PATH_MAX + 1]; + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + if (strcmp(mode, "off") != 0) + textfile_put(filename, "onmode", mode); + + return textfile_put(filename, "mode", mode); +} + +int read_device_mode(const char *src, char *mode, int length) +{ + char filename[PATH_MAX + 1], *str; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "config"); + + str = textfile_get(filename, "mode"); + if (!str) + return -ENOENT; + + strncpy(mode, str, length); + mode[length - 1] = '\0'; + + free(str); + + return 0; +} + +int read_on_mode(const char *src, char *mode, int length) +{ + char filename[PATH_MAX + 1], *str; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "config"); + + str = textfile_get(filename, "onmode"); + if (!str) + return -ENOENT; + + strncpy(mode, str, length); + mode[length - 1] = '\0'; + + free(str); + + return 0; +} + +int write_local_name(bdaddr_t *bdaddr, const char *name) +{ + char filename[PATH_MAX + 1], str[249]; + int i; + + memset(str, 0, sizeof(str)); + for (i = 0; i < 248 && name[i]; i++) + if ((unsigned char) name[i] < 32 || name[i] == 127) + str[i] = '.'; + else + str[i] = name[i]; + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return textfile_put(filename, "name", str); +} + +int read_local_name(bdaddr_t *bdaddr, char *name) +{ + char filename[PATH_MAX + 1], *str; + int len; + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + str = textfile_get(filename, "name"); + if (!str) + return -ENOENT; + + len = strlen(str); + if (len > 248) + str[248] = '\0'; + strcpy(name, str); + + free(str); + + return 0; +} + +int write_local_class(bdaddr_t *bdaddr, uint8_t *class) +{ + char filename[PATH_MAX + 1], str[9]; + + sprintf(str, "0x%2.2x%2.2x%2.2x", class[2], class[1], class[0]); + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return textfile_put(filename, "class", str); +} + +int read_local_class(bdaddr_t *bdaddr, uint8_t *class) +{ + char filename[PATH_MAX + 1], tmp[3], *str; + int i; + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + str = textfile_get(filename, "class"); + if (!str) + return -ENOENT; + + memset(tmp, 0, sizeof(tmp)); + for (i = 0; i < 3; i++) { + memcpy(tmp, str + (i * 2) + 2, 2); + class[2 - i] = (uint8_t) strtol(tmp, NULL, 16); + } + + free(str); + + return 0; +} + +int write_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t class) +{ + char filename[PATH_MAX + 1], addr[18], str[9]; + + create_filename(filename, PATH_MAX, local, "classes"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(peer, addr); + sprintf(str, "0x%6.6x", class); + + return textfile_put(filename, addr, str); +} + +int read_remote_class(bdaddr_t *local, bdaddr_t *peer, uint32_t *class) +{ + char filename[PATH_MAX + 1], addr[18], *str; + + create_filename(filename, PATH_MAX, local, "classes"); + + ba2str(peer, addr); + + str = textfile_get(filename, addr); + if (!str) + return -ENOENT; + + if (sscanf(str, "%x", class) != 1) { + free(str); + return -ENOENT; + } + + free(str); + + return 0; +} + +int write_device_name(bdaddr_t *local, bdaddr_t *peer, char *name) +{ + char filename[PATH_MAX + 1], addr[18], str[249]; + int i; + + memset(str, 0, sizeof(str)); + for (i = 0; i < 248 && name[i]; i++) + if ((unsigned char) name[i] < 32 || name[i] == 127) + str[i] = '.'; + else + str[i] = name[i]; + + create_filename(filename, PATH_MAX, local, "names"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(peer, addr); + return textfile_put(filename, addr, str); +} + +int read_device_name(const char *src, const char *dst, char *name) +{ + char filename[PATH_MAX + 1], *str; + int len; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "names"); + + str = textfile_get(filename, dst); + if (!str) + return -ENOENT; + + len = strlen(str); + if (len > 248) + str[248] = '\0'; + strcpy(name, str); + + free(str); + + return 0; +} + +int write_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data) +{ + char filename[PATH_MAX + 1], addr[18], str[481]; + int i; + + memset(str, 0, sizeof(str)); + for (i = 0; i < 240; i++) + sprintf(str + (i * 2), "%2.2X", data[i]); + + create_filename(filename, PATH_MAX, local, "eir"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(peer, addr); + return textfile_put(filename, addr, str); +} + +int read_remote_eir(bdaddr_t *local, bdaddr_t *peer, uint8_t *data) +{ + char filename[PATH_MAX + 1], addr[18], *str; + int i; + + create_filename(filename, PATH_MAX, local, "eir"); + + ba2str(peer, addr); + + str = textfile_get(filename, addr); + if (!str) + return -ENOENT; + + if (!data) { + free(str); + return 0; + } + + if (strlen(str) < 480) { + free(str); + return -EIO; + } + + for (i = 0; i < 240; i++) + sscanf(str + (i * 2), "%02hhX", &data[i]); + + free(str); + + return 0; +} + +int write_version_info(bdaddr_t *local, bdaddr_t *peer, uint16_t manufacturer, + uint8_t lmp_ver, uint16_t lmp_subver) +{ + char filename[PATH_MAX + 1], addr[18], str[16]; + + memset(str, 0, sizeof(str)); + sprintf(str, "%d %d %d", manufacturer, lmp_ver, lmp_subver); + + create_filename(filename, PATH_MAX, local, "manufacturers"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(peer, addr); + return textfile_put(filename, addr, str); +} + +int write_features_info(bdaddr_t *local, bdaddr_t *peer, + unsigned char *page1, unsigned char *page2) +{ + char filename[PATH_MAX + 1], addr[18]; + char str[] = "0000000000000000 0000000000000000"; + char *old_value; + int i; + + ba2str(peer, addr); + + create_filename(filename, PATH_MAX, local, "features"); + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + old_value = textfile_get(filename, addr); + + if (page1) + for (i = 0; i < 8; i++) + sprintf(str + (i * 2), "%2.2X", page1[i]); + else if (old_value && strlen(old_value) >= 16) + strncpy(str, old_value, 16); + + if (page2) + for (i = 0; i < 8; i++) + sprintf(str + 17 + (i * 2), "%2.2X", page2[i]); + else if (old_value && strlen(old_value) >= 33) + strncpy(str + 17, old_value + 17, 16); + + free(old_value); + + return textfile_put(filename, addr, str); +} + +static int decode_bytes(const char *str, unsigned char *bytes, size_t len) +{ + unsigned int i; + + for (i = 0; i < len; i++) { + if (sscanf(str + (i * 2), "%02hhX", &bytes[i]) != 1) + return -EINVAL; + } + + return 0; +} + +int read_remote_features(bdaddr_t *local, bdaddr_t *peer, + unsigned char *page1, unsigned char *page2) +{ + char filename[PATH_MAX + 1], addr[18], *str; + size_t len; + int err; + + if (page1 == NULL && page2 == NULL) + return -EINVAL; + + create_filename(filename, PATH_MAX, local, "features"); + + ba2str(peer, addr); + + str = textfile_get(filename, addr); + if (!str) + return -ENOENT; + + len = strlen(str); + + err = -ENOENT; + + if (page1 && len >= 16) + err = decode_bytes(str, page1, 8); + + if (page2 && len >= 33) + err = decode_bytes(str + 17, page2, 8); + + free(str); + + return err; +} + +int write_lastseen_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm) +{ + char filename[PATH_MAX + 1], addr[18], str[24]; + + memset(str, 0, sizeof(str)); + strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S %Z", tm); + + create_filename(filename, PATH_MAX, local, "lastseen"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(peer, addr); + return textfile_put(filename, addr, str); +} + +int write_lastused_info(bdaddr_t *local, bdaddr_t *peer, struct tm *tm) +{ + char filename[PATH_MAX + 1], addr[18], str[24]; + + memset(str, 0, sizeof(str)); + strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S %Z", tm); + + create_filename(filename, PATH_MAX, local, "lastused"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(peer, addr); + return textfile_put(filename, addr, str); +} + +int write_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t type, int length) +{ + char filename[PATH_MAX + 1], addr[18], str[38]; + int i; + + memset(str, 0, sizeof(str)); + for (i = 0; i < 16; i++) + sprintf(str + (i * 2), "%2.2X", key[i]); + sprintf(str + 32, " %d %d", type, length); + + create_filename(filename, PATH_MAX, local, "linkkeys"); + + create_file(filename, S_IRUSR | S_IWUSR); + + ba2str(peer, addr); + + if (length < 0) { + char *tmp = textfile_get(filename, addr); + if (tmp) { + if (strlen(tmp) > 34) + memcpy(str + 34, tmp + 34, 3); + free(tmp); + } + } + + return textfile_put(filename, addr, str); +} + +int read_link_key(bdaddr_t *local, bdaddr_t *peer, unsigned char *key, uint8_t *type) +{ + char filename[PATH_MAX + 1], addr[18], tmp[3], *str; + int i; + + create_filename(filename, PATH_MAX, local, "linkkeys"); + + ba2str(peer, addr); + str = textfile_get(filename, addr); + if (!str) + return -ENOENT; + + if (!key) { + free(str); + return 0; + } + + memset(tmp, 0, sizeof(tmp)); + for (i = 0; i < 16; i++) { + memcpy(tmp, str + (i * 2), 2); + key[i] = (uint8_t) strtol(tmp, NULL, 16); + } + + if (type) { + memcpy(tmp, str + 33, 2); + *type = (uint8_t) strtol(tmp, NULL, 10); + } + + free(str); + + return 0; +} + +int read_pin_code(bdaddr_t *local, bdaddr_t *peer, char *pin) +{ + char filename[PATH_MAX + 1], addr[18], *str; + int len; + + create_filename(filename, PATH_MAX, local, "pincodes"); + + ba2str(peer, addr); + str = textfile_get(filename, addr); + if (!str) + return -ENOENT; + + strncpy(pin, str, 16); + len = strlen(pin); + + free(str); + + return len; +} + +static GSList *service_string_to_list(char *services) +{ + GSList *l = NULL; + char *start = services; + int i, finished = 0; + + for (i = 0; !finished; i++) { + if (services[i] == '\0') + finished = 1; + + if (services[i] == ' ' || services[i] == '\0') { + services[i] = '\0'; + l = g_slist_append(l, start); + start = services + i + 1; + } + } + + return l; +} + +static char *service_list_to_string(GSList *services) +{ + char str[1024]; + int len = 0; + + if (!services) + return g_strdup(""); + + memset(str, 0, sizeof(str)); + + while (services) { + int ret; + char *ident = services->data; + + ret = snprintf(str + len, sizeof(str) - len - 1, "%s%s", + ident, services->next ? " " : ""); + + if (ret > 0) + len += ret; + + services = services->next; + } + + return g_strdup(str); +} + +int write_trust(const char *src, const char *addr, const char *service, + gboolean trust) +{ + char filename[PATH_MAX + 1], *str; + GSList *services = NULL, *match; + gboolean trusted; + int ret; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "trusts"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + str = textfile_caseget(filename, addr); + if (str) + services = service_string_to_list(str); + + match = g_slist_find_custom(services, service, (GCompareFunc) strcmp); + trusted = match ? TRUE : FALSE; + + /* If the old setting is the same as the requested one, we're done */ + if (trusted == trust) { + g_slist_free(services); + free(str); + return 0; + } + + if (trust) + services = g_slist_append(services, (void *) service); + else + services = g_slist_remove(services, match->data); + + /* Remove the entry if the last trusted service was removed */ + if (!trust && !services) + ret = textfile_casedel(filename, addr); + else { + char *new_str = service_list_to_string(services); + ret = textfile_caseput(filename, addr, new_str); + free(new_str); + } + + g_slist_free(services); + + free(str); + + return ret; +} + +gboolean read_trust(const bdaddr_t *local, const char *addr, const char *service) +{ + char filename[PATH_MAX + 1], *str; + GSList *services; + gboolean ret; + + create_filename(filename, PATH_MAX, local, "trusts"); + + str = textfile_caseget(filename, addr); + if (!str) + return FALSE; + + services = service_string_to_list(str); + + if (g_slist_find_custom(services, service, (GCompareFunc) strcmp)) + ret = TRUE; + else + ret = FALSE; + + g_slist_free(services); + free(str); + + return ret; +} + +struct trust_list { + GSList *trusts; + const char *service; +}; + +static void append_trust(char *key, char *value, void *data) +{ + struct trust_list *list = data; + + if (strstr(value, list->service)) + list->trusts = g_slist_append(list->trusts, g_strdup(key)); +} + +GSList *list_trusts(bdaddr_t *local, const char *service) +{ + char filename[PATH_MAX + 1]; + struct trust_list list; + + create_filename(filename, PATH_MAX, local, "trusts"); + + list.trusts = NULL; + list.service = service; + + if (textfile_foreach(filename, append_trust, &list) < 0) + return NULL; + + return list.trusts; +} + +int write_device_profiles(bdaddr_t *src, bdaddr_t *dst, const char *profiles) +{ + char filename[PATH_MAX + 1], addr[18]; + + if (!profiles) + return -EINVAL; + + create_filename(filename, PATH_MAX, src, "profiles"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dst, addr); + return textfile_put(filename, addr, profiles); +} + +int delete_entry(bdaddr_t *src, const char *storage, const char *key) +{ + char filename[PATH_MAX + 1]; + + create_filename(filename, PATH_MAX, src, storage); + + return textfile_del(filename, key); +} + +int store_record(const gchar *src, const gchar *dst, sdp_record_t *rec) +{ + char filename[PATH_MAX + 1], key[28]; + sdp_buf_t buf; + int err, size, i; + char *str; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + snprintf(key, sizeof(key), "%17s#%08X", dst, rec->handle); + + if (sdp_gen_record_pdu(rec, &buf) < 0) + return -1; + + size = buf.data_size; + + str = g_malloc0(size*2+1); + + for (i = 0; i < size; i++) + sprintf(str + (i * 2), "%02X", buf.data[i]); + + err = textfile_put(filename, key, str); + + free(buf.data); + free(str); + + return err; +} + +sdp_record_t *record_from_string(const gchar *str) +{ + sdp_record_t *rec; + int size, i, len; + uint8_t *pdata; + char tmp[3]; + + size = strlen(str)/2; + pdata = g_malloc0(size); + + tmp[2] = 0; + for (i = 0; i < size; i++) { + memcpy(tmp, str + (i * 2), 2); + pdata[i] = (uint8_t) strtol(tmp, NULL, 16); + } + + rec = sdp_extract_pdu(pdata, size, &len); + free(pdata); + + return rec; +} + + +sdp_record_t *fetch_record(const gchar *src, const gchar *dst, + const uint32_t handle) +{ + char filename[PATH_MAX + 1], key[28], *str; + sdp_record_t *rec; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp"); + + snprintf(key, sizeof(key), "%17s#%08X", dst, handle); + + str = textfile_get(filename, key); + if (!str) + return NULL; + + rec = record_from_string(str); + free(str); + + return rec; +} + +int delete_record(const gchar *src, const gchar *dst, const uint32_t handle) +{ + char filename[PATH_MAX + 1], key[28]; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp"); + + snprintf(key, sizeof(key), "%17s#%08X", dst, handle); + + return textfile_del(filename, key); +} + +struct record_list { + sdp_list_t *recs; + const gchar *addr; +}; + +static void create_stored_records_from_keys(char *key, char *value, + void *user_data) +{ + struct record_list *rec_list = user_data; + const gchar *addr = rec_list->addr; + sdp_record_t *rec; + + if (strncmp(key, addr, 17)) + return; + + rec = record_from_string(value); + + rec_list->recs = sdp_list_append(rec_list->recs, rec); +} + +void delete_all_records(const bdaddr_t *src, const bdaddr_t *dst) +{ + sdp_list_t *records, *seq; + char srcaddr[18], dstaddr[18]; + + ba2str(src, srcaddr); + ba2str(dst, dstaddr); + + records = read_records(src, dst); + + for (seq = records; seq; seq = seq->next) { + sdp_record_t *rec = seq->data; + delete_record(srcaddr, dstaddr, rec->handle); + } + + if (records) + sdp_list_free(records, (sdp_free_func_t) sdp_record_free); +} + +sdp_list_t *read_records(const bdaddr_t *src, const bdaddr_t *dst) +{ + char filename[PATH_MAX + 1]; + struct record_list rec_list; + char srcaddr[18], dstaddr[18]; + + ba2str(src, srcaddr); + ba2str(dst, dstaddr); + + rec_list.addr = dstaddr; + rec_list.recs = NULL; + + create_name(filename, PATH_MAX, STORAGEDIR, srcaddr, "sdp"); + textfile_foreach(filename, create_stored_records_from_keys, &rec_list); + + return rec_list.recs; +} + +sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid) +{ + sdp_list_t *seq; + + for (seq = recs; seq; seq = seq->next) { + sdp_record_t *rec = (sdp_record_t *) seq->data; + sdp_list_t *svcclass = NULL; + char *uuid_str; + + if (sdp_get_service_classes(rec, &svcclass) < 0) + continue; + + /* Extract the uuid */ + uuid_str = bt_uuid2string(svcclass->data); + if (!uuid_str) + continue; + + if (!strcasecmp(uuid_str, uuid)) { + sdp_list_free(svcclass, free); + free(uuid_str); + return rec; + } + + sdp_list_free(svcclass, free); + free(uuid_str); + } + return NULL; +} + +int store_device_id(const gchar *src, const gchar *dst, + const uint16_t source, const uint16_t vendor, + const uint16_t product, const uint16_t version) +{ + char filename[PATH_MAX + 1], str[20]; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "did"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + snprintf(str, sizeof(str), "%04X %04X %04X %04X", source, + vendor, product, version); + + return textfile_put(filename, dst, str); +} + +static int read_device_id_from_did(const gchar *src, const gchar *dst, + uint16_t *source, uint16_t *vendor, + uint16_t *product, uint16_t *version) +{ + char filename[PATH_MAX + 1]; + char *str, *vendor_str, *product_str, *version_str; + + create_name(filename, PATH_MAX, STORAGEDIR, src, "did"); + + str = textfile_get(filename, dst); + if (!str) + return -ENOENT; + + vendor_str = strchr(str, ' '); + if (!vendor_str) { + free(str); + return -ENOENT; + } + *(vendor_str++) = 0; + + product_str = strchr(vendor_str, ' '); + if (!product_str) { + free(str); + return -ENOENT; + } + *(product_str++) = 0; + + version_str = strchr(product_str, ' '); + if (!version_str) { + free(str); + return -ENOENT; + } + *(version_str++) = 0; + + if (source) + *source = (uint16_t) strtol(str, NULL, 16); + if (vendor) + *vendor = (uint16_t) strtol(vendor_str, NULL, 16); + if (product) + *product = (uint16_t) strtol(product_str, NULL, 16); + if (version) + *version = (uint16_t) strtol(version_str, NULL, 16); + + free(str); + + return 0; +} + +int read_device_id(const gchar *srcaddr, const gchar *dstaddr, + uint16_t *source, uint16_t *vendor, + uint16_t *product, uint16_t *version) +{ + uint16_t lsource, lvendor, lproduct, lversion; + sdp_list_t *recs; + sdp_record_t *rec; + bdaddr_t src, dst; + int err; + + err = read_device_id_from_did(srcaddr, dstaddr, &lsource, + vendor, product, version); + if (!err) { + if (lsource == 0xffff) + err = -ENOENT; + + return err; + } + + str2ba(srcaddr, &src); + str2ba(dstaddr, &dst); + + recs = read_records(&src, &dst); + rec = find_record_in_list(recs, PNP_UUID); + + if (rec) { + sdp_data_t *pdlist; + + pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID_SOURCE); + lsource = pdlist ? pdlist->val.uint16 : 0x0000; + + pdlist = sdp_data_get(rec, SDP_ATTR_VENDOR_ID); + lvendor = pdlist ? pdlist->val.uint16 : 0x0000; + + pdlist = sdp_data_get(rec, SDP_ATTR_PRODUCT_ID); + lproduct = pdlist ? pdlist->val.uint16 : 0x0000; + + pdlist = sdp_data_get(rec, SDP_ATTR_VERSION); + lversion = pdlist ? pdlist->val.uint16 : 0x0000; + + err = 0; + } + + sdp_list_free(recs, (sdp_free_func_t)sdp_record_free); + + if (err) { + /* FIXME: We should try EIR data if we have it, too */ + + /* If we don't have the data, we don't want to go through the + * above search every time. */ + lsource = 0xffff; + lvendor = 0x0000; + lproduct = 0x0000; + lversion = 0x0000; + } + + store_device_id(srcaddr, dstaddr, lsource, lvendor, lproduct, lversion); + + if (err) + return err; + + if (source) + *source = lsource; + if (vendor) + *vendor = lvendor; + if (product) + *product = lproduct; + if (version) + *version = lversion; + + return 0; +} + +int write_device_pairable(bdaddr_t *bdaddr, gboolean mode) +{ + char filename[PATH_MAX + 1]; + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return textfile_put(filename, "pairable", mode ? "yes" : "no"); +} + +int read_device_pairable(bdaddr_t *bdaddr, gboolean *mode) +{ + char filename[PATH_MAX + 1], *str; + + create_filename(filename, PATH_MAX, bdaddr, "config"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + str = textfile_get(filename, "pairable"); + if (!str) + return -ENOENT; + + *mode = strcmp(str, "yes") == 0 ? TRUE : FALSE; + + free(str); + + return 0; +} + +gboolean read_blocked(const bdaddr_t *local, const bdaddr_t *remote) +{ + char filename[PATH_MAX + 1], *str, addr[18]; + + create_filename(filename, PATH_MAX, local, "blocked"); + + ba2str(remote, addr); + + str = textfile_caseget(filename, addr); + if (!str) + return FALSE; + + free(str); + + return TRUE; +} + +int write_blocked(const bdaddr_t *local, const bdaddr_t *remote, + gboolean blocked) +{ + char filename[PATH_MAX + 1], addr[18]; + + create_filename(filename, PATH_MAX, local, "blocked"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(remote, addr); + + if (blocked == FALSE) + return textfile_casedel(filename, addr); + + return textfile_caseput(filename, addr, ""); +} + +int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba, + const char *services) +{ + char filename[PATH_MAX + 1], addr[18]; + + create_filename(filename, PATH_MAX, sba, "primary"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + + return textfile_put(filename, addr, services); +} + +static void filter_keys(char *key, char *value, void *data) +{ + struct match *match = data; + const char *address = match->pattern; + + /* Each key contains: MAC#handle*/ + if (strncasecmp(key, address, 17) == 0) + match->keys = g_slist_append(match->keys, g_strdup(key)); +} + +int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba) +{ + GSList *l; + struct match match; + char filename[PATH_MAX + 1], address[18]; + int err; + + create_filename(filename, PATH_MAX, sba, "primary"); + + memset(address, 0, sizeof(address)); + ba2str(dba, address); + + err = textfile_del(filename, address); + if (err < 0) + return err; + + /* Deleting all characteristics of a given address */ + memset(&match, 0, sizeof(match)); + match.pattern = address; + + create_filename(filename, PATH_MAX, sba, "characteristic"); + err = textfile_foreach(filename, filter_keys, &match); + if (err < 0) + return err; + + for (l = match.keys; l; l = l->next) { + const char *key = l->data; + textfile_del(filename, key); + } + + g_slist_foreach(match.keys, (GFunc) g_free, NULL); + g_slist_free(match.keys); + + /* Deleting all attributes values of a given address */ + memset(&match, 0, sizeof(match)); + match.pattern = address; + + create_filename(filename, PATH_MAX, sba, "attributes"); + err = textfile_foreach(filename, filter_keys, &match); + if (err < 0) + return err; + + for (l = match.keys; l; l = l->next) { + const char *key = l->data; + textfile_del(filename, key); + } + + g_slist_foreach(match.keys, (GFunc) g_free, NULL); + g_slist_free(match.keys); + + return 0; +} + +char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba) +{ + char filename[PATH_MAX + 1], addr[18]; + + create_filename(filename, PATH_MAX, sba, "primary"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + + return textfile_caseget(filename, addr); +} + +int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, + uint16_t handle, const char *chars) +{ + char filename[PATH_MAX + 1], addr[18], key[23]; + + create_filename(filename, PATH_MAX, sba, "characteristic"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + + snprintf(key, sizeof(key), "%17s#%04X", addr, handle); + + return textfile_put(filename, key, chars); +} + +char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, + uint16_t handle) +{ + char filename[PATH_MAX + 1], addr[18], key[23]; + + create_filename(filename, PATH_MAX, sba, "characteristic"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + + snprintf(key, sizeof(key), "%17s#%04X", addr, handle); + + return textfile_caseget(filename, key); +} + +int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba, + uint16_t handle, const char *chars) +{ + char filename[PATH_MAX + 1], addr[18], key[23]; + + create_filename(filename, PATH_MAX, sba, "attributes"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + + snprintf(key, sizeof(key), "%17s#%04X", addr, handle); + + return textfile_put(filename, key, chars); +} + +int read_device_attributes(const bdaddr_t *sba, textfile_cb func, void *data) +{ + char filename[PATH_MAX + 1]; + + create_filename(filename, PATH_MAX, sba, "attributes"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + return textfile_foreach(filename, func, data); +} + +int write_device_type(const bdaddr_t *sba, const bdaddr_t *dba, + device_type_t type) +{ + char filename[PATH_MAX + 1], addr[18], chars[3]; + + create_filename(filename, PATH_MAX, sba, "types"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + + snprintf(chars, sizeof(chars), "%2.2X", type); + + return textfile_put(filename, addr, chars); +} + +device_type_t read_device_type(const bdaddr_t *sba, const bdaddr_t *dba) +{ + char filename[PATH_MAX + 1], addr[18], *chars; + device_type_t type; + + create_filename(filename, PATH_MAX, sba, "types"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + + chars = textfile_caseget(filename, addr); + if (chars == NULL) + return DEVICE_TYPE_UNKNOWN; + + type = strtol(chars, NULL, 16); + + free(chars); + + return type; +} -- cgit v1.2.3