/* * * 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 "textfile.h" #include "glib-helper.h" #include "storage.h" /* When all services should trust a remote device */ #define GLOBAL_TRUST "[all]" 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, uint8_t dst_type, char *alias, size_t size) { char filename[PATH_MAX + 1], *tmp; char key[20]; int err; create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases"); snprintf(key, sizeof(key), "%17s#%hhu", dst, dst_type); tmp = textfile_get(filename, key); if (tmp != NULL) goto done; /* Try old format (address only) */ key[17] = '\0'; tmp = textfile_get(filename, key); if (tmp == NULL) return -ENXIO; done: err = snprintf(alias, size, "%s", tmp); free(tmp); return err < 0 ? -EIO : 0; } int write_device_alias(const char *src, const char *dst, uint8_t dst_type, const char *alias) { char filename[PATH_MAX + 1]; char key[20]; create_name(filename, PATH_MAX, STORAGEDIR, src, "aliases"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); snprintf(key, sizeof(key), "%17s#%hhu", dst, dst_type); return textfile_put(filename, key, alias); } int write_discoverable_timeout(const 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(const 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(const 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(const 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(const 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 > HCI_MAX_NAME_LENGTH) str[HCI_MAX_NAME_LENGTH] = '\0'; strcpy(name, str); free(str); return 0; } int write_local_class(const 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(const 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 read_remote_appearance(const bdaddr_t *local, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t *appearance) { char filename[PATH_MAX + 1], key[20], *str; create_filename(filename, PATH_MAX, local, "appearances"); ba2str(peer, key); sprintf(&key[17], "#%hhu", bdaddr_type); str = textfile_get(filename, key); if (!str) return -ENOENT; if (sscanf(str, "%hx", appearance) != 1) { free(str); return -ENOENT; } free(str); return 0; } int write_remote_appearance(const bdaddr_t *local, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t appearance) { char filename[PATH_MAX + 1], key[20], str[7]; create_filename(filename, PATH_MAX, local, "appearances"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ba2str(peer, key); sprintf(&key[17], "#%hhu", bdaddr_type); sprintf(str, "0x%4.4x", appearance); return textfile_put(filename, key, str); } int write_remote_class(const bdaddr_t *local, const 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(const bdaddr_t *local, const 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(const bdaddr_t *local, const bdaddr_t *peer, uint8_t peer_type, const char *name) { char filename[PATH_MAX + 1], key[20], str[HCI_MAX_NAME_LENGTH + 1]; int i; memset(str, 0, sizeof(str)); for (i = 0; i < HCI_MAX_NAME_LENGTH && 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, key); sprintf(&key[17], "#%hhu", peer_type); return textfile_put(filename, key, str); } int read_device_name(const char *src, const char *dst, uint8_t dst_type, char *name) { char filename[PATH_MAX + 1], *str, key[20]; int len; create_name(filename, PATH_MAX, STORAGEDIR, src, "names"); snprintf(key, sizeof(key), "%17s#%hhu", dst, dst_type); str = textfile_get(filename, key); if (str != NULL) goto done; /* Try old format (address only) */ key[17] = '\0'; str = textfile_get(filename, key); if (str == NULL) return -ENOENT; done: len = strlen(str); if (len > HCI_MAX_NAME_LENGTH) str[HCI_MAX_NAME_LENGTH] = '\0'; strcpy(name, str); free(str); return 0; } int write_version_info(const bdaddr_t *local, const 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(const bdaddr_t *local, const 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(const bdaddr_t *local, const 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(const bdaddr_t *local, const bdaddr_t *peer, uint8_t peer_type, struct tm *tm) { char filename[PATH_MAX + 1], key[20], 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, key); sprintf(&key[17], "#%hhu", peer_type); return textfile_put(filename, key, str); } int write_lastused_info(const bdaddr_t *local, const bdaddr_t *peer, uint8_t peer_type, struct tm *tm) { char filename[PATH_MAX + 1], key[20], 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, key); sprintf(&key[17], "#%hhu", peer_type); return textfile_put(filename, key, str); } int write_link_key(const bdaddr_t *local, const bdaddr_t *peer, uint8_t peer_type, unsigned char *key, uint8_t type, int length) { char filename[PATH_MAX + 1], addr[20], 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); sprintf(&addr[17], "#%hhu", peer_type); 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(const bdaddr_t *local, const bdaddr_t *peer, uint8_t peer_type, unsigned char *key, uint8_t *type) { char filename[PATH_MAX + 1], addr[20], tmp[3], *str; int i; create_filename(filename, PATH_MAX, local, "linkkeys"); ba2str(peer, addr); sprintf(&addr[17], "#%hhu", peer_type); str = textfile_get(filename, addr); if (str != NULL) goto done; /* Try old format (address only) */ addr[17] = '\0'; str = textfile_get(filename, addr); if (!str) return -ENOENT; done: 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; } ssize_t read_pin_code(const bdaddr_t *local, const bdaddr_t *peer, char *pin) { char filename[PATH_MAX + 1], addr[18], *str; ssize_t 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; } int write_trust(const char *src, const char *addr, uint8_t addr_type, gboolean trust) { char filename[PATH_MAX + 1], key[20]; create_name(filename, PATH_MAX, STORAGEDIR, src, "trusts"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); snprintf(key, sizeof(key), "%17s#%hhu", addr, addr_type); if (trust == FALSE) return textfile_casedel(filename, key); return textfile_caseput(filename, key, GLOBAL_TRUST); } gboolean read_trust(const bdaddr_t *local, const char *addr, uint8_t addr_type) { char filename[PATH_MAX + 1], key[20], *str; gboolean ret; create_filename(filename, PATH_MAX, local, "trusts"); snprintf(key, sizeof(key), "%17s#%hhu", addr, addr_type); str = textfile_caseget(filename, key); if (str == NULL) /* Try old format (address only) */ str = textfile_caseget(filename, addr); if (str == NULL) return FALSE; if (strcmp(str, GLOBAL_TRUST) == 0) ret = TRUE; else ret = FALSE; free(str); return ret; } int write_device_profiles(const bdaddr_t *src, const bdaddr_t *dst, uint8_t dst_type, const char *profiles) { char filename[PATH_MAX + 1], key[20]; if (!profiles) return -EINVAL; create_filename(filename, PATH_MAX, src, "profiles"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ba2str(dst, key); sprintf(&key[17], "#%hhu", dst_type); return textfile_put(filename, key, profiles); } int delete_entry(const bdaddr_t *src, const char *storage, const bdaddr_t *dst, uint8_t dst_type) { char filename[PATH_MAX + 1], key[20]; int err, ret; ba2str(dst, key); sprintf(&key[17], "#%hhu", dst_type); create_filename(filename, PATH_MAX, src, storage); err = 0; ret = textfile_del(filename, key); if (ret) err = ret; /* Trying without address type */ key[17] = '\0'; ret = textfile_del(filename, key); if (ret) err = ret; return err; } int store_record(const gchar *src, const gchar *dst, uint8_t dst_type, sdp_record_t *rec) { char filename[PATH_MAX + 1], key[30]; 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#%hhu#%08X", dst, dst_type, 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); g_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); g_free(pdata); return rec; } sdp_record_t *fetch_record(const gchar *src, const gchar *dst, uint8_t dst_type, const uint32_t handle) { char filename[PATH_MAX + 1], old_key[28], key[30], *str; sdp_record_t *rec; create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp"); snprintf(key, sizeof(key), "%17s#%hhu#%08X", dst, dst_type, handle); snprintf(old_key, sizeof(old_key), "%17s#%08X", dst, handle); str = textfile_get(filename, key); if (str != NULL) goto done; /* Try old format (address#handle) */ str = textfile_get(filename, old_key); if (str == NULL) return NULL; done: rec = record_from_string(str); free(str); return rec; } int delete_record(const gchar *src, const gchar *dst, uint8_t dst_type, const uint32_t handle) { char filename[PATH_MAX + 1], old_key[28], key[30]; int err, ret; create_name(filename, PATH_MAX, STORAGEDIR, src, "sdp"); snprintf(key, sizeof(key), "%17s#%hhu#%08X", dst, dst_type, handle); snprintf(old_key, sizeof(old_key), "%17s#%08X", dst, handle); err = 0; ret = textfile_del(filename, key); if (ret) err = ret; /* Try old format (address#handle) */ ret = textfile_del(filename, old_key); if (ret) err = ret; return err; } 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, uint8_t dst_type) { 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, dst_type, 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, uint8_t dst_type, const uint16_t source, const uint16_t vendor, const uint16_t product, const uint16_t version) { char filename[PATH_MAX + 1], key[20], str[20]; create_name(filename, PATH_MAX, STORAGEDIR, src, "did"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); snprintf(key, sizeof(key), "%17s#%hhu", dst, dst_type); snprintf(str, sizeof(str), "%04X %04X %04X %04X", source, vendor, product, version); return textfile_put(filename, key, str); } static int read_device_id_from_did(const gchar *src, const gchar *dst, uint8_t dst_type, uint16_t *source, uint16_t *vendor, uint16_t *product, uint16_t *version) { char filename[PATH_MAX + 1]; char key[20], *str, *vendor_str, *product_str, *version_str; create_name(filename, PATH_MAX, STORAGEDIR, src, "did"); snprintf(key, sizeof(key), "%17s#%hhu", dst, dst_type); str = textfile_get(filename, key); if (str != NULL) goto done; /* Try old format (address only) */ str = textfile_get(filename, dst); if (!str) return -ENOENT; done: 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, uint8_t dst_type, 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, dst_type, &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, dst_type, 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(const 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(const bdaddr_t *bdaddr, gboolean *mode) { char filename[PATH_MAX + 1], *str; create_filename(filename, PATH_MAX, bdaddr, "config"); 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, uint8_t remote_type) { char filename[PATH_MAX + 1], *str, key[20]; create_filename(filename, PATH_MAX, local, "blocked"); ba2str(remote, key); sprintf(&key[17], "#%hhu", remote_type); str = textfile_caseget(filename, key); if (str != NULL) goto done; /* Try old format (address only) */ key[17] = '\0'; str = textfile_caseget(filename, key); if (str == NULL) return FALSE; done: free(str); return TRUE; } int write_blocked(const bdaddr_t *local, const bdaddr_t *remote, uint8_t remote_type, gboolean blocked) { char filename[PATH_MAX + 1], key[20]; create_filename(filename, PATH_MAX, local, "blocked"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ba2str(remote, key); sprintf(&key[17], "#%hhu", remote_type); if (blocked == FALSE) return textfile_casedel(filename, key); return textfile_caseput(filename, key, ""); } int write_device_services(const bdaddr_t *sba, const bdaddr_t *dba, uint8_t bdaddr_type, const char *services) { char filename[PATH_MAX + 1], key[20]; create_filename(filename, PATH_MAX, sba, "primaries"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ba2str(dba, key); sprintf(&key[17], "#%hhu", bdaddr_type); return textfile_put(filename, key, services); } static void filter_keys(char *key, char *value, void *data) { struct match *match = data; if (strncasecmp(key, match->pattern, strlen(match->pattern)) == 0) match->keys = g_slist_append(match->keys, g_strdup(key)); } static void delete_by_pattern(const char *filename, char *pattern) { struct match match; GSList *l; int err; memset(&match, 0, sizeof(match)); match.pattern = pattern; err = textfile_foreach(filename, filter_keys, &match); if (err < 0) goto done; for (l = match.keys; l; l = l->next) { const char *key = l->data; textfile_del(filename, key); } done: g_slist_free_full(match.keys, g_free); } int delete_device_service(const bdaddr_t *sba, const bdaddr_t *dba, uint8_t bdaddr_type) { char filename[PATH_MAX + 1], key[20]; memset(key, 0, sizeof(key)); ba2str(dba, key); sprintf(&key[17], "#%hhu", bdaddr_type); /* Deleting all characteristics of a given key */ create_filename(filename, PATH_MAX, sba, "characteristics"); delete_by_pattern(filename, key); /* Deleting all attributes values of a given key */ create_filename(filename, PATH_MAX, sba, "attributes"); delete_by_pattern(filename, key); /* Deleting all CCC values of a given key */ create_filename(filename, PATH_MAX, sba, "ccc"); delete_by_pattern(filename, key); create_filename(filename, PATH_MAX, sba, "primaries"); return textfile_del(filename, key); } char *read_device_services(const bdaddr_t *sba, const bdaddr_t *dba, uint8_t bdaddr_type) { char filename[PATH_MAX + 1], key[20]; create_filename(filename, PATH_MAX, sba, "primaries"); ba2str(dba, key); sprintf(&key[17], "#%hhu", bdaddr_type); return textfile_caseget(filename, key); } int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, uint8_t bdaddr_type, uint16_t handle, const char *chars) { char filename[PATH_MAX + 1], addr[18], key[25]; create_filename(filename, PATH_MAX, sba, "characteristics"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ba2str(dba, addr); snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle); return textfile_put(filename, key, chars); } char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, uint8_t bdaddr_type, uint16_t handle) { char filename[PATH_MAX + 1], addr[18], key[25]; create_filename(filename, PATH_MAX, sba, "characteristics"); ba2str(dba, addr); snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle); return textfile_caseget(filename, key); } int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba, uint8_t bdaddr_type, uint16_t handle, const char *chars) { char filename[PATH_MAX + 1], addr[18], key[25]; 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#%hhu#%04X", addr, bdaddr_type, 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"); return textfile_foreach(filename, func, data); } int read_device_ccc(const bdaddr_t *local, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t handle, uint16_t *value) { char filename[PATH_MAX + 1], addr[18], key[25]; char *str; unsigned int config; int err = 0; create_filename(filename, PATH_MAX, local, "ccc"); ba2str(peer, addr); snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle); str = textfile_caseget(filename, key); if (str == NULL) return -ENOENT; if (sscanf(str, "%04X", &config) != 1) err = -ENOENT; else *value = config; free(str); return err; } int write_device_ccc(const bdaddr_t *local, const bdaddr_t *peer, uint8_t bdaddr_type, uint16_t handle, uint16_t value) { char filename[PATH_MAX + 1], addr[18], key[25], config[5]; create_filename(filename, PATH_MAX, local, "ccc"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ba2str(peer, addr); snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle); snprintf(config, sizeof(config), "%04X", value); return textfile_put(filename, key, config); } void delete_device_ccc(const bdaddr_t *local, const bdaddr_t *peer) { char filename[PATH_MAX + 1], addr[18]; ba2str(peer, addr); /* Deleting all CCC values of a given address */ create_filename(filename, PATH_MAX, local, "ccc"); delete_by_pattern(filename, addr); } int write_longtermkeys(const bdaddr_t *local, const bdaddr_t *peer, uint8_t bdaddr_type, const char *key) { char filename[PATH_MAX + 1], addr[20]; if (!key) return -EINVAL; create_filename(filename, PATH_MAX, local, "longtermkeys"); create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); ba2str(peer, addr); sprintf(&addr[17], "#%hhu", bdaddr_type); return textfile_put(filename, addr, key); } gboolean has_longtermkeys(const bdaddr_t *local, const bdaddr_t *peer, uint8_t bdaddr_type) { char filename[PATH_MAX + 1], key[20], *str; create_filename(filename, PATH_MAX, local, "longtermkeys"); ba2str(peer, key); sprintf(&key[17], "#%hhu", bdaddr_type); str = textfile_caseget(filename, key); if (str) { free(str); return TRUE; } return FALSE; }