mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-16 08:44:38 +08:00
f688edd8e1
Fix keyword typos and comments.
2607 lines
51 KiB
C
2607 lines
51 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2019-2020 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#define _GNU_SOURCE
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <ftw.h>
|
|
#include <libgen.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#include <ell/ell.h>
|
|
#include <json-c/json.h>
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
#include "mesh/util.h"
|
|
|
|
#include "tools/mesh/keys.h"
|
|
#include "tools/mesh/remote.h"
|
|
#include "tools/mesh/cfgcli.h"
|
|
#include "tools/mesh/model.h"
|
|
#include "tools/mesh/mesh-db.h"
|
|
|
|
#define KEY_IDX_INVALID NET_IDX_INVALID
|
|
#define DEFAULT_LOCATION 0x0000
|
|
|
|
struct mesh_db {
|
|
json_object *jcfg;
|
|
char *cfg_fname;
|
|
uint8_t token[8];
|
|
};
|
|
|
|
static struct mesh_db *cfg;
|
|
static const char *bak_ext = ".bak";
|
|
static const char *tmp_ext = ".tmp";
|
|
|
|
static const char *js_schema = "http://json-schema.org/draft-04/schema#";
|
|
static const char *schema_id = "http://www.bluetooth.com/specifications/"
|
|
"assigned-numbers/mesh-profile/"
|
|
"cdb-schema.json#";
|
|
const char *schema_version = "1.0.0";
|
|
|
|
|
|
static bool add_string(json_object *jobj, const char *desc, const char *str)
|
|
{
|
|
json_object *jstring = json_object_new_string(str);
|
|
|
|
if (!jstring)
|
|
return false;
|
|
|
|
/* Overwrite old value if present */
|
|
json_object_object_del(jobj, desc);
|
|
|
|
json_object_object_add(jobj, desc, jstring);
|
|
return true;
|
|
}
|
|
|
|
static bool set_timestamp(json_object *jobj)
|
|
{
|
|
time_t time_raw;
|
|
struct tm *tp;
|
|
char buf[80];
|
|
|
|
time(&time_raw);
|
|
tp = gmtime(&time_raw);
|
|
|
|
strftime(buf, 80, "%FT%TZ", tp);
|
|
|
|
return add_string(jobj, "timestamp", buf);
|
|
}
|
|
|
|
static bool save_config_file(const char *fname)
|
|
{
|
|
FILE *outfile;
|
|
const char *str;
|
|
bool result = false;
|
|
|
|
outfile = fopen(fname, "w");
|
|
if (!outfile) {
|
|
l_error("Failed to save configuration to %s", cfg->cfg_fname);
|
|
return false;
|
|
}
|
|
|
|
set_timestamp(cfg->jcfg);
|
|
|
|
str = json_object_to_json_string_ext(cfg->jcfg,
|
|
JSON_C_TO_STRING_PRETTY);
|
|
|
|
if (fwrite(str, sizeof(char), strlen(str), outfile) < strlen(str))
|
|
l_warn("Incomplete write of mesh configuration");
|
|
else
|
|
result = true;
|
|
|
|
fclose(outfile);
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool save_config(void)
|
|
{
|
|
char *fname_tmp, *fname_bak, *fname_cfg;
|
|
bool result = false;
|
|
|
|
fname_cfg = cfg->cfg_fname;
|
|
fname_tmp = l_strdup_printf("%s%s", fname_cfg, tmp_ext);
|
|
fname_bak = l_strdup_printf("%s%s", fname_cfg, bak_ext);
|
|
remove(fname_tmp);
|
|
|
|
result = save_config_file(fname_tmp);
|
|
|
|
if (result) {
|
|
remove(fname_bak);
|
|
rename(fname_cfg, fname_bak);
|
|
rename(fname_tmp, fname_cfg);
|
|
}
|
|
|
|
remove(fname_tmp);
|
|
|
|
l_free(fname_tmp);
|
|
l_free(fname_bak);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void release_config(void)
|
|
{
|
|
l_free(cfg->cfg_fname);
|
|
json_object_put(cfg->jcfg);
|
|
l_free(cfg);
|
|
cfg = NULL;
|
|
}
|
|
|
|
static json_object *get_node_by_unicast(json_object *jcfg, uint16_t unicast)
|
|
{
|
|
json_object *jarray;
|
|
int i, sz;
|
|
|
|
if (!json_object_object_get_ex(jcfg, "nodes", &jarray))
|
|
return NULL;
|
|
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return NULL;
|
|
|
|
sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry, *jval;
|
|
uint16_t addr;
|
|
const char *str;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
if (!json_object_object_get_ex(jentry, "unicastAddress",
|
|
&jval))
|
|
return NULL;
|
|
|
|
str = json_object_get_string(jval);
|
|
if (sscanf(str, "%04hx", &addr) != 1)
|
|
continue;
|
|
|
|
if (addr == unicast)
|
|
return jentry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool get_int(json_object *jobj, const char *keyword, int *value)
|
|
{
|
|
json_object *jvalue;
|
|
|
|
if (!json_object_object_get_ex(jobj, keyword, &jvalue))
|
|
return false;
|
|
|
|
*value = json_object_get_int(jvalue);
|
|
if (errno == EINVAL) {
|
|
l_error("Error: %s should contain an integer value\n",
|
|
keyword);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool write_int(json_object *jobj, const char *keyword, int val)
|
|
{
|
|
json_object *jval;
|
|
|
|
jval = json_object_new_int(val);
|
|
if (!jval)
|
|
return false;
|
|
|
|
/* Overwrite old value if present */
|
|
json_object_object_del(jobj, keyword);
|
|
|
|
json_object_object_add(jobj, keyword, jval);
|
|
return true;
|
|
}
|
|
|
|
static bool get_bool(json_object *jobj, const char *keyword, bool *value)
|
|
{
|
|
json_object *jvalue;
|
|
|
|
if (!json_object_object_get_ex(jobj, keyword, &jvalue))
|
|
return false;
|
|
|
|
if (json_object_get_type(jvalue) != json_type_boolean) {
|
|
l_error("Error: %s should contain a boolean value\n",
|
|
keyword);
|
|
return false;
|
|
}
|
|
|
|
*value = json_object_get_boolean(jvalue);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool write_bool(json_object *jobj, const char *keyword, bool val)
|
|
{
|
|
json_object *jval;
|
|
|
|
jval = json_object_new_boolean(val);
|
|
if (!jval)
|
|
return false;
|
|
|
|
/* Overwrite old value if present */
|
|
json_object_object_del(jobj, keyword);
|
|
|
|
json_object_object_add(jobj, keyword, jval);
|
|
return true;
|
|
}
|
|
|
|
static json_object *get_key_object(json_object *jarray, uint16_t idx)
|
|
{
|
|
int i, sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry;
|
|
int jidx;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
if (!get_int(jentry, "index", &jidx))
|
|
return NULL;
|
|
|
|
if (jidx == idx)
|
|
return jentry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool write_uint16_hex(json_object *jobj, const char *desc,
|
|
uint16_t value)
|
|
{
|
|
json_object *jstring;
|
|
char buf[5];
|
|
|
|
snprintf(buf, 5, "%4.4x", value);
|
|
jstring = json_object_new_string(buf);
|
|
if (!jstring)
|
|
return false;
|
|
|
|
/* Overwrite old value if present */
|
|
json_object_object_del(jobj, desc);
|
|
|
|
json_object_object_add(jobj, desc, jstring);
|
|
return true;
|
|
}
|
|
|
|
static bool write_uint32_hex(json_object *jobj, const char *desc, uint32_t val)
|
|
{
|
|
json_object *jstring;
|
|
char buf[9];
|
|
|
|
snprintf(buf, 9, "%8.8x", val);
|
|
jstring = json_object_new_string(buf);
|
|
if (!jstring)
|
|
return false;
|
|
|
|
/* Overwrite old value if present */
|
|
json_object_object_del(jobj, desc);
|
|
|
|
json_object_object_add(jobj, desc, jstring);
|
|
return true;
|
|
}
|
|
|
|
static json_object *get_node_by_uuid(json_object *jcfg, uint8_t uuid[16])
|
|
{
|
|
json_object *jarray = NULL;
|
|
char buf[37];
|
|
int i, sz;
|
|
|
|
if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
|
|
return NULL;
|
|
|
|
json_object_object_get_ex(jcfg, "nodes", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return NULL;
|
|
|
|
sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry, *jval;
|
|
const char *str;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
if (!json_object_object_get_ex(jentry, "UUID", &jval))
|
|
return NULL;
|
|
|
|
str = json_object_get_string(jval);
|
|
if (strlen(str) != 36)
|
|
continue;
|
|
|
|
if (!strcmp(buf, str))
|
|
return jentry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool add_u8_8(json_object *jobj, const char *desc,
|
|
const uint8_t value[8])
|
|
{
|
|
json_object *jstring;
|
|
char buf[17];
|
|
|
|
hex2str((uint8_t *) value, 8, buf, 17);
|
|
jstring = json_object_new_string(buf);
|
|
if (!jstring)
|
|
return false;
|
|
|
|
/* Overwrite old value if present */
|
|
json_object_object_del(jobj, desc);
|
|
|
|
json_object_object_add(jobj, desc, jstring);
|
|
return true;
|
|
}
|
|
|
|
static bool add_u8_16(json_object *jobj, const char *desc,
|
|
const uint8_t value[16])
|
|
{
|
|
json_object *jstring;
|
|
char buf[33];
|
|
|
|
hex2str((uint8_t *) value, 16, buf, 33);
|
|
jstring = json_object_new_string(buf);
|
|
if (!jstring)
|
|
return false;
|
|
|
|
/* Overwrite old value if present */
|
|
json_object_object_del(jobj, desc);
|
|
|
|
json_object_object_add(jobj, desc, jstring);
|
|
return true;
|
|
}
|
|
|
|
static bool get_token(json_object *jobj, uint8_t token[8])
|
|
{
|
|
json_object *jval;
|
|
const char *str;
|
|
|
|
if (!token)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jobj, "token", &jval))
|
|
return false;
|
|
|
|
str = json_object_get_string(jval);
|
|
if (!str2hex(str, strlen(str), token, 8))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static uint16_t node_parse_key(json_object *jarray, int i)
|
|
{
|
|
json_object *jkey;
|
|
int idx;
|
|
|
|
jkey = json_object_array_get_idx(jarray, i);
|
|
if (!jkey)
|
|
return KEY_IDX_INVALID;
|
|
|
|
if (!get_int(jkey, "index", &idx))
|
|
return KEY_IDX_INVALID;
|
|
|
|
return (uint16_t)idx;
|
|
}
|
|
|
|
static bool node_check_key_updated(json_object *jarray, int i, bool *updated)
|
|
{
|
|
json_object *jkey;
|
|
|
|
jkey = json_object_array_get_idx(jarray, i);
|
|
if (!jkey)
|
|
return false;
|
|
|
|
if (!get_bool(jkey, "updated", updated))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int compare_group_addr(const void *a, const void *b, void *user_data)
|
|
{
|
|
const struct mesh_group *grp0 = a;
|
|
const struct mesh_group *grp1 = b;
|
|
|
|
if (grp0->addr < grp1->addr)
|
|
return -1;
|
|
|
|
if (grp0->addr > grp1->addr)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool load_composition(json_object *jnode, uint16_t unicast)
|
|
{
|
|
json_object *jarray;
|
|
int i, ele_cnt;
|
|
|
|
if (!json_object_object_get_ex(jnode, "elements", &jarray))
|
|
return false;
|
|
|
|
if (json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
ele_cnt = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < ele_cnt; ++i) {
|
|
json_object *jentry, *jval, *jmods;
|
|
int32_t index;
|
|
int k, mod_cnt;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
if (!json_object_object_get_ex(jentry, "index", &jval))
|
|
return false;
|
|
|
|
index = json_object_get_int(jval);
|
|
if (index > 0xff)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jentry, "models", &jmods))
|
|
return false;
|
|
|
|
mod_cnt = json_object_array_length(jmods);
|
|
|
|
for (k = 0; k < mod_cnt; ++k) {
|
|
json_object *jmod, *jid;
|
|
uint32_t mod_id, len;
|
|
const char *str;
|
|
|
|
jmod = json_object_array_get_idx(jmods, k);
|
|
if (!json_object_object_get_ex(jmod, "modelId", &jid))
|
|
return false;
|
|
|
|
str = json_object_get_string(jid);
|
|
len = strlen(str);
|
|
|
|
if (len != 4 && len != 8)
|
|
return false;
|
|
|
|
if ((len == 4) && (sscanf(str, "%04x", &mod_id) != 1))
|
|
return false;
|
|
|
|
if ((len == 8) && (sscanf(str, "%08x", &mod_id) != 1))
|
|
return false;
|
|
|
|
remote_set_model(unicast, index, mod_id, len == 8);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void load_remotes(json_object *jcfg)
|
|
{
|
|
json_object *jnodes;
|
|
int i, sz, node_count = 0;
|
|
|
|
json_object_object_get_ex(jcfg, "nodes", &jnodes);
|
|
if (!jnodes || json_object_get_type(jnodes) != json_type_array)
|
|
return;
|
|
|
|
sz = json_object_array_length(jnodes);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jnode, *jval, *jarray;
|
|
uint8_t uuid[16];
|
|
uint16_t unicast, key_idx;
|
|
const char *str;
|
|
int ele_cnt, key_cnt;
|
|
int j;
|
|
|
|
jnode = json_object_array_get_idx(jnodes, i);
|
|
if (!jnode)
|
|
continue;
|
|
|
|
if (!json_object_object_get_ex(jnode, "UUID", &jval))
|
|
continue;
|
|
|
|
str = json_object_get_string(jval);
|
|
if (strlen(str) != 36)
|
|
continue;
|
|
|
|
if (!l_uuid_from_string(str, uuid))
|
|
continue;
|
|
|
|
if (!json_object_object_get_ex(jnode, "unicastAddress", &jval))
|
|
continue;
|
|
|
|
str = json_object_get_string(jval);
|
|
if (sscanf(str, "%04hx", &unicast) != 1)
|
|
continue;
|
|
|
|
json_object_object_get_ex(jnode, "elements", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
continue;
|
|
|
|
ele_cnt = json_object_array_length(jarray);
|
|
|
|
if (ele_cnt > MAX_ELE_COUNT)
|
|
continue;
|
|
|
|
json_object_object_get_ex(jnode, "netKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
continue;
|
|
|
|
key_cnt = json_object_array_length(jarray);
|
|
if (key_cnt < 0)
|
|
continue;
|
|
|
|
key_idx = node_parse_key(jarray, 0);
|
|
if (key_idx == KEY_IDX_INVALID)
|
|
continue;
|
|
|
|
remote_add_node((const uint8_t *)uuid, unicast, ele_cnt,
|
|
key_idx);
|
|
for (j = 1; j < key_cnt; j++) {
|
|
bool updated = false;
|
|
|
|
key_idx = node_parse_key(jarray, j);
|
|
|
|
if (key_idx == KEY_IDX_INVALID)
|
|
continue;
|
|
|
|
remote_add_net_key(unicast, key_idx, false);
|
|
|
|
node_check_key_updated(jarray, j, &updated);
|
|
remote_update_net_key(unicast, key_idx, updated, false);
|
|
}
|
|
|
|
json_object_object_get_ex(jnode, "appKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
continue;
|
|
|
|
key_cnt = json_object_array_length(jarray);
|
|
|
|
for (j = 0; j < key_cnt; j++) {
|
|
bool updated = false;
|
|
|
|
key_idx = node_parse_key(jarray, j);
|
|
|
|
if (key_idx == KEY_IDX_INVALID)
|
|
continue;
|
|
|
|
remote_add_app_key(unicast, key_idx, false);
|
|
|
|
node_check_key_updated(jarray, j, &updated);
|
|
remote_update_app_key(unicast, key_idx, updated, false);
|
|
}
|
|
|
|
if (!load_composition(jnode, unicast))
|
|
continue;
|
|
|
|
/* If "crpl" is present, composition's is available */
|
|
jval = NULL;
|
|
if (json_object_object_get_ex(jnode, "crpl", &jval) && jval)
|
|
remote_set_composition(unicast, true);
|
|
|
|
/* TODO: Add the rest of the configuration */
|
|
|
|
node_count++;
|
|
}
|
|
|
|
if (node_count != sz)
|
|
l_warn("The remote node configuration load is incomplete!");
|
|
|
|
}
|
|
|
|
static bool add_app_key(json_object *jobj, uint16_t net_idx, uint16_t app_idx)
|
|
{
|
|
json_object *jkey, *jarray;
|
|
char buf[12];
|
|
|
|
json_object_object_get_ex(jobj, "appKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
jkey = json_object_new_object();
|
|
|
|
snprintf(buf, 12, "AppKey %4.4x", app_idx);
|
|
|
|
if (!add_string(jkey, "name", buf))
|
|
goto fail;
|
|
|
|
if (!write_int(jkey, "boundNetKey", (int)net_idx))
|
|
goto fail;
|
|
|
|
if (!write_int(jkey, "index", (int)app_idx))
|
|
goto fail;
|
|
|
|
json_object_array_add(jarray, jkey);
|
|
|
|
return true;
|
|
fail:
|
|
json_object_put(jkey);
|
|
return false;
|
|
}
|
|
|
|
static bool add_node_key(json_object *jobj, const char *desc, uint16_t idx)
|
|
{
|
|
json_object *jkey, *jarray;
|
|
|
|
json_object_object_get_ex(jobj, desc, &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
jkey = json_object_new_object();
|
|
|
|
if (!write_int(jkey, "index", (int)idx))
|
|
goto fail;
|
|
|
|
if (!write_bool(jkey, "updated", false))
|
|
goto fail;
|
|
|
|
json_object_array_add(jarray, jkey);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
json_object_put(jkey);
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_node_set_ttl(uint16_t unicast, uint8_t ttl)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (!write_int(jnode, "defaultTTL", ttl))
|
|
return false;
|
|
|
|
return save_config();
|
|
}
|
|
|
|
static bool add_transmit_info(json_object *jobj, int cnt, int interval,
|
|
const char *desc)
|
|
{
|
|
json_object *jtxmt;
|
|
|
|
json_object_object_del(jobj, desc);
|
|
jtxmt = json_object_new_object();
|
|
|
|
if (!write_int(jtxmt, "count", cnt))
|
|
goto fail;
|
|
|
|
if (!write_int(jtxmt, "interval", interval))
|
|
goto fail;
|
|
|
|
json_object_object_add(jobj, desc, jtxmt);
|
|
return true;
|
|
|
|
fail:
|
|
json_object_put(jtxmt);
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_node_set_net_transmit(uint16_t unicast, uint8_t cnt,
|
|
uint16_t interval)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (!add_transmit_info(jnode, cnt, interval, "networkTransmit"))
|
|
return false;
|
|
|
|
return save_config();
|
|
}
|
|
|
|
static bool set_feature(json_object *jnode, const char *desc, uint8_t feature)
|
|
{
|
|
json_object *jobj;
|
|
|
|
if (feature > MESH_MODE_UNSUPPORTED)
|
|
return false;
|
|
|
|
jobj = json_object_object_get(jnode, "features");
|
|
if (!jobj) {
|
|
jobj = json_object_new_object();
|
|
json_object_object_add(jnode, "features", jobj);
|
|
}
|
|
|
|
if (!write_int(jobj, desc, feature))
|
|
return false;
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_node_set_relay(uint16_t unicast, uint8_t relay, uint8_t cnt,
|
|
uint16_t interval)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (relay < MESH_MODE_UNSUPPORTED &&
|
|
!add_transmit_info(jnode, cnt, interval, "relayRetransmit"))
|
|
return false;
|
|
|
|
return set_feature(jnode, "relay", relay);
|
|
}
|
|
|
|
bool mesh_db_node_set_proxy(uint16_t unicast, uint8_t proxy)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return set_feature(jnode, "proxy", proxy);
|
|
}
|
|
|
|
bool mesh_db_node_set_friend(uint16_t unicast, uint8_t friend)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return set_feature(jnode, "friend", friend);
|
|
}
|
|
|
|
bool mesh_db_node_set_beacon(uint16_t unicast, bool enabled)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (!write_bool(jnode, "secureNetworkBeacon", enabled))
|
|
return false;
|
|
|
|
return save_config();
|
|
}
|
|
|
|
static json_object *get_element(uint16_t unicast, uint16_t ele_addr)
|
|
{
|
|
json_object *jnode, *jarray;
|
|
int i, ele_cnt;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jnode, "elements", &jarray))
|
|
return NULL;
|
|
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return NULL;
|
|
|
|
ele_cnt = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < ele_cnt; ++i) {
|
|
json_object *jentry, *jval;
|
|
int32_t index;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
if (!json_object_object_get_ex(jentry, "index", &jval))
|
|
return NULL;
|
|
|
|
index = json_object_get_int(jval);
|
|
if (index > 0xff)
|
|
return NULL;
|
|
|
|
if (ele_addr == unicast + index)
|
|
return jentry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static json_object *get_model(uint16_t unicast, uint16_t ele_addr,
|
|
uint32_t mod_id, bool vendor)
|
|
{
|
|
json_object *jelement, *jarray;
|
|
int i, sz;
|
|
|
|
jelement = get_element(unicast, ele_addr);
|
|
if (!jelement)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jelement, "models", &jarray))
|
|
return NULL;
|
|
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return NULL;
|
|
|
|
if (!vendor)
|
|
mod_id = mod_id & ~VENDOR_ID_MASK;
|
|
|
|
sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry, *jval;
|
|
uint32_t id, len;
|
|
const char *str;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
if (!json_object_object_get_ex(jentry, "modelId",
|
|
&jval))
|
|
return NULL;
|
|
|
|
str = json_object_get_string(jval);
|
|
len = strlen(str);
|
|
if (len != 4 && len != 8)
|
|
return NULL;
|
|
|
|
if ((len == 4 && vendor) || (len == 8 && !vendor))
|
|
continue;
|
|
|
|
if (sscanf(str, "%08x", &id) != 1)
|
|
return NULL;
|
|
|
|
if (id == mod_id)
|
|
return jentry;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void jarray_int_del(json_object *jarray, int val)
|
|
{
|
|
int i, sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
|
|
if (val == json_object_get_int(jentry)) {
|
|
json_object_array_del_idx(jarray, i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool update_model_int_array(uint16_t unicast, uint16_t ele_addr,
|
|
bool vendor, uint32_t mod_id,
|
|
int val, const char *keyword, bool add)
|
|
{
|
|
json_object *jarray, *jmod, *jvalue;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jmod = get_model(unicast, ele_addr, mod_id, vendor);
|
|
if (!jmod)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jmod, keyword, &jarray))
|
|
return false;
|
|
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
jarray_int_del(jarray, val);
|
|
|
|
if (!add)
|
|
return true;
|
|
|
|
jvalue = json_object_new_int(val);
|
|
if (!jvalue)
|
|
return false;
|
|
|
|
json_object_array_add(jarray, jvalue);
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_node_model_bind(uint16_t unicast, uint16_t ele_addr, bool vendor,
|
|
uint32_t mod_id, uint16_t app_idx)
|
|
{
|
|
char buf[5];
|
|
|
|
snprintf(buf, 5, "%4.4x", app_idx);
|
|
|
|
return update_model_int_array(unicast, ele_addr, vendor, mod_id,
|
|
(int) app_idx, "bind", true);
|
|
}
|
|
|
|
bool mesh_db_node_model_unbind(uint16_t unicast, uint16_t ele_addr, bool vendor,
|
|
uint32_t mod_id, uint16_t app_idx)
|
|
{
|
|
char buf[5];
|
|
|
|
snprintf(buf, 5, "%4.4x", app_idx);
|
|
|
|
return update_model_int_array(unicast, ele_addr, vendor, mod_id,
|
|
(int) app_idx, "bind", false);
|
|
}
|
|
|
|
static void jarray_string_del(json_object *jarray, const char *str, size_t len)
|
|
{
|
|
int i, sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry;
|
|
char *str_entry;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
str_entry = (char *)json_object_get_string(jentry);
|
|
|
|
if (str_entry && (strlen(str_entry) == len) &&
|
|
!strncmp(str, str_entry, len)) {
|
|
json_object_array_del_idx(jarray, i, 1);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool add_array_string(json_object *jarray, const char *str)
|
|
{
|
|
json_object *jstring;
|
|
|
|
jstring = json_object_new_string(str);
|
|
if (!jstring)
|
|
return false;
|
|
|
|
json_object_array_add(jarray, jstring);
|
|
return true;
|
|
}
|
|
|
|
static bool update_model_string_array(uint16_t unicast, uint16_t ele_addr,
|
|
bool vendor, uint32_t mod_id,
|
|
const char *str, uint32_t len,
|
|
const char *keyword, bool add)
|
|
{
|
|
json_object *jarray, *jmod;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jmod = get_model(unicast, ele_addr, mod_id, vendor);
|
|
if (!jmod)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jmod, keyword, &jarray))
|
|
return false;
|
|
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
jarray_string_del(jarray, str, len);
|
|
|
|
if (!add)
|
|
return true;
|
|
|
|
if (!add_array_string(jarray, str))
|
|
return false;
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_node_model_add_sub(uint16_t unicast, uint16_t ele, bool vendor,
|
|
uint32_t mod_id, uint16_t addr)
|
|
{
|
|
char buf[5];
|
|
|
|
snprintf(buf, 5, "%4.4x", addr);
|
|
|
|
return update_model_string_array(unicast, ele, vendor, mod_id, buf, 4,
|
|
"subscribe", true);
|
|
}
|
|
|
|
bool mesh_db_node_model_del_sub(uint16_t unicast, uint16_t ele, bool vendor,
|
|
uint32_t mod_id, uint16_t addr)
|
|
{
|
|
char buf[5];
|
|
|
|
snprintf(buf, 5, "%4.4x", addr);
|
|
|
|
return update_model_string_array(unicast, ele, vendor, mod_id, buf, 4,
|
|
"subscribe", false);
|
|
}
|
|
|
|
bool mesh_db_node_model_add_sub_virt(uint16_t unicast, uint16_t ele,
|
|
bool vendor, uint32_t mod_id,
|
|
uint8_t *label)
|
|
{
|
|
char buf[33];
|
|
|
|
hex2str(label, 16, buf, sizeof(buf));
|
|
|
|
return update_model_string_array(unicast, ele, vendor, mod_id, buf, 32,
|
|
"subscribe", true);
|
|
|
|
}
|
|
|
|
bool mesh_db_node_model_del_sub_virt(uint16_t unicast, uint16_t ele,
|
|
bool vendor, uint32_t mod_id,
|
|
uint8_t *label)
|
|
{
|
|
char buf[33];
|
|
|
|
hex2str(label, 16, buf, sizeof(buf));
|
|
|
|
return update_model_string_array(unicast, ele, vendor, mod_id, buf, 32,
|
|
"subscribe", false);
|
|
}
|
|
|
|
static json_object *delete_subs(uint16_t unicast, uint16_t ele, bool vendor,
|
|
uint32_t mod_id)
|
|
{
|
|
json_object *jarray, *jmod;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return NULL;
|
|
|
|
jmod = get_model(unicast, ele, mod_id, vendor);
|
|
if (!jmod)
|
|
return NULL;
|
|
|
|
json_object_object_del(jmod, "subscribe");
|
|
|
|
jarray = json_object_new_array();
|
|
if (!jarray)
|
|
return NULL;
|
|
|
|
json_object_object_add(jmod, "subscribe", jarray);
|
|
|
|
return jarray;
|
|
}
|
|
|
|
bool mesh_db_node_model_del_sub_all(uint16_t unicast, uint16_t ele, bool vendor,
|
|
uint32_t mod_id)
|
|
{
|
|
|
|
if (!delete_subs(unicast, ele, vendor, mod_id))
|
|
return false;
|
|
|
|
return save_config();
|
|
}
|
|
|
|
static bool sub_overwrite(uint16_t unicast, uint16_t ele, bool vendor,
|
|
uint32_t mod_id, char *buf)
|
|
{
|
|
json_object *jarray, *jstring;
|
|
|
|
jarray = delete_subs(unicast, ele, vendor, mod_id);
|
|
if (!jarray)
|
|
return false;
|
|
|
|
jstring = json_object_new_string(buf);
|
|
if (!jstring)
|
|
return false;
|
|
|
|
json_object_array_add(jarray, jstring);
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_node_model_overwrt_sub(uint16_t unicast, uint16_t ele, bool vendor,
|
|
uint32_t mod_id, uint16_t addr)
|
|
{
|
|
char buf[5];
|
|
|
|
snprintf(buf, 5, "%4.4x", addr);
|
|
|
|
return sub_overwrite(unicast, ele, vendor, mod_id, buf);
|
|
}
|
|
|
|
bool mesh_db_node_model_overwrt_sub_virt(uint16_t unicast, uint16_t ele,
|
|
bool vendor, uint32_t mod_id,
|
|
uint8_t *label)
|
|
{
|
|
char buf[33];
|
|
|
|
hex2str(label, 16, buf, sizeof(buf));
|
|
|
|
return sub_overwrite(unicast, ele, vendor, mod_id, buf);
|
|
}
|
|
|
|
bool mesh_db_node_model_set_pub(uint16_t unicast, uint16_t ele_addr,
|
|
bool vendor, uint32_t mod_id,
|
|
struct model_pub *pub, bool virt)
|
|
{
|
|
json_object *jmod, *jpub, *jobj = NULL;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jmod = get_model(unicast, ele_addr, mod_id, vendor);
|
|
if (!jmod)
|
|
return false;
|
|
|
|
jpub = json_object_new_object();
|
|
|
|
if (!virt && !write_uint16_hex(jpub, "address", pub->u.addr))
|
|
goto fail;
|
|
|
|
if (virt) {
|
|
char buf[33];
|
|
|
|
hex2str(pub->u.label, 16, buf, sizeof(buf));
|
|
|
|
if (!add_string(jpub, "address", buf))
|
|
goto fail;
|
|
}
|
|
|
|
if (!write_int(jpub, "index", pub->app_idx))
|
|
goto fail;
|
|
|
|
if (!write_int(jpub, "ttl", pub->ttl))
|
|
goto fail;
|
|
|
|
if (!write_int(jpub, "credentials", pub->cred ? 1 : 0))
|
|
goto fail;
|
|
|
|
if (!add_transmit_info(jpub, pub->rtx_cnt, pub->rtx_interval,
|
|
"retransmit"))
|
|
goto fail;
|
|
|
|
jobj = json_object_new_object();
|
|
|
|
if (!write_int(jobj, "numberOfSteps", pub->prd_steps))
|
|
goto fail;
|
|
|
|
if (!write_int(jobj, "resolution", pub->prd_res))
|
|
goto fail;
|
|
|
|
json_object_object_add(jpub, "period", jobj);
|
|
|
|
json_object_object_del(jmod, "publish");
|
|
json_object_object_add(jmod, "publish", jpub);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
if (jobj)
|
|
json_object_put(jobj);
|
|
|
|
json_object_put(jpub);
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_node_set_hb_pub(uint16_t unicast, uint16_t dst, uint16_t net_idx,
|
|
uint8_t period_log, uint8_t ttl,
|
|
uint16_t features)
|
|
{
|
|
json_object *jnode, *jpub, *jarray = NULL;
|
|
uint32_t period;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
if (period_log > 0x12 || ttl > 0x7F)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
jpub = json_object_new_object();
|
|
|
|
if (!write_uint16_hex(jpub, "address", dst))
|
|
goto fail;
|
|
|
|
period = period_log ? 1 << (period_log - 1) : 0;
|
|
|
|
if (!write_int(jpub, "period", period))
|
|
goto fail;
|
|
|
|
if (!write_int(jpub, "ttl", ttl))
|
|
goto fail;
|
|
|
|
if (!write_int(jpub, "index", net_idx))
|
|
goto fail;
|
|
|
|
jarray = json_object_new_array();
|
|
|
|
if (features & FEATURE_PROXY)
|
|
if (!add_array_string(jarray, "proxy"))
|
|
goto fail;
|
|
|
|
if (features & FEATURE_RELAY)
|
|
if (!add_array_string(jarray, "relay"))
|
|
goto fail;
|
|
|
|
if (features & FEATURE_FRIEND)
|
|
if (!add_array_string(jarray, "friend"))
|
|
goto fail;
|
|
|
|
if (features & FEATURE_LPN)
|
|
if (!add_array_string(jarray, "lowPower"))
|
|
goto fail;
|
|
|
|
json_object_object_add(jpub, "features", jarray);
|
|
json_object_object_del(jnode, "heartbeatPub");
|
|
json_object_object_add(jnode, "heartbeatPub", jpub);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
if (jarray)
|
|
json_object_put(jarray);
|
|
|
|
json_object_put(jpub);
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_node_set_hb_sub(uint16_t unicast, uint16_t src, uint16_t dst)
|
|
{
|
|
json_object *jnode, *jsub;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
jsub = json_object_new_object();
|
|
|
|
if (!write_uint16_hex(jsub, "source", src))
|
|
goto fail;
|
|
|
|
if (!write_uint16_hex(jsub, "destination", dst))
|
|
goto fail;
|
|
|
|
json_object_object_del(jnode, "heartbeatSub");
|
|
json_object_object_add(jnode, "heartbeatSub", jsub);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
json_object_put(jsub);
|
|
return false;
|
|
}
|
|
|
|
static void jarray_key_del(json_object *jarray, int16_t idx)
|
|
{
|
|
int i, sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry;
|
|
int val;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
|
|
if (!get_int(jentry, "index", &val))
|
|
continue;
|
|
|
|
if (val == idx) {
|
|
json_object_array_del_idx(jarray, i, 1);
|
|
return;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static bool delete_key(json_object *jobj, const char *desc, uint16_t idx)
|
|
{
|
|
json_object *jarray;
|
|
|
|
if (!json_object_object_get_ex(jobj, desc, &jarray))
|
|
return true;
|
|
|
|
jarray_key_del(jarray, idx);
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_node_add_net_key(uint16_t unicast, uint16_t idx)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return add_node_key(jnode, "netKeys", idx);
|
|
}
|
|
|
|
bool mesh_db_node_del_net_key(uint16_t unicast, uint16_t net_idx)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return delete_key(jnode, "netKeys", net_idx);
|
|
}
|
|
|
|
static bool key_update(uint16_t unicast, int16_t idx, bool updated,
|
|
const char *desc)
|
|
{
|
|
json_object *jnode, *jarray;
|
|
int i, sz;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jnode, desc, &jarray))
|
|
return false;
|
|
|
|
sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry;
|
|
int val;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
|
|
if (!get_int(jentry, "index", &val))
|
|
continue;
|
|
|
|
if ((val == idx) && write_bool(jentry, "updated", updated))
|
|
return save_config();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_node_update_net_key(uint16_t unicast, uint16_t idx, bool updated)
|
|
{
|
|
return key_update(unicast, idx, updated, "netKeys");
|
|
}
|
|
|
|
bool mesh_db_node_add_app_key(uint16_t unicast, uint16_t idx)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return add_node_key(jnode, "appKeys", idx);
|
|
}
|
|
|
|
bool mesh_db_node_del_app_key(uint16_t unicast, uint16_t idx)
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return delete_key(jnode, "appKeys", idx);
|
|
}
|
|
|
|
bool mesh_db_node_update_app_key(uint16_t unicast, uint16_t idx, bool updated)
|
|
{
|
|
return key_update(unicast, idx, updated, "appKeys");
|
|
}
|
|
|
|
static bool load_keys(json_object *jobj)
|
|
{
|
|
json_object *jarray, *jentry;
|
|
int net_idx, app_idx;
|
|
int i, key_cnt;
|
|
|
|
json_object_object_get_ex(jobj, "netKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
key_cnt = json_object_array_length(jarray);
|
|
if (key_cnt < 0)
|
|
return false;
|
|
|
|
for (i = 0; i < key_cnt; ++i) {
|
|
int phase;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
|
|
if (!get_int(jentry, "index", &net_idx))
|
|
return false;
|
|
|
|
keys_add_net_key((uint16_t) net_idx);
|
|
|
|
if (!get_int(jentry, "phase", &phase))
|
|
return false;
|
|
|
|
keys_set_net_key_phase(net_idx, (uint8_t) phase, false);
|
|
}
|
|
|
|
json_object_object_get_ex(jobj, "appKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
key_cnt = json_object_array_length(jarray);
|
|
if (key_cnt < 0)
|
|
return false;
|
|
|
|
for (i = 0; i < key_cnt; ++i) {
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
|
|
if (!get_int(jentry, "boundNetKey", &net_idx))
|
|
return false;
|
|
|
|
if (!get_int(jentry, "index", &app_idx))
|
|
return false;
|
|
|
|
keys_add_app_key((uint16_t) net_idx, (uint16_t) app_idx);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_db_add_net_key(uint16_t net_idx)
|
|
{
|
|
json_object *jkey, *jarray;
|
|
char buf[12];
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
json_object_object_get_ex(cfg->jcfg, "netKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
if (get_key_object(jarray, net_idx))
|
|
return true;
|
|
|
|
jkey = json_object_new_object();
|
|
|
|
snprintf(buf, 12, "Subnet %4.4x", net_idx);
|
|
|
|
if (!add_string(jkey, "name", buf))
|
|
goto fail;
|
|
|
|
if (!write_int(jkey, "index", net_idx))
|
|
goto fail;
|
|
|
|
if (!write_int(jkey, "phase", KEY_REFRESH_PHASE_NONE))
|
|
goto fail;
|
|
|
|
if (!add_string(jkey, "minSecurity", "secure"))
|
|
goto fail;
|
|
|
|
if (!set_timestamp(jkey))
|
|
goto fail;
|
|
|
|
json_object_array_add(jarray, jkey);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
json_object_put(jkey);
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_del_net_key(uint16_t net_idx)
|
|
{
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
return delete_key(cfg->jcfg, "netKeys", net_idx);
|
|
}
|
|
|
|
bool mesh_db_set_net_key_phase(uint16_t net_idx, uint8_t phase)
|
|
{
|
|
json_object *jval, *jarray, *jkey;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
json_object_object_get_ex(cfg->jcfg, "netKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
jkey = get_key_object(jarray, net_idx);
|
|
if (!jkey)
|
|
return false;
|
|
|
|
jval = json_object_new_int(phase);
|
|
if (!jval)
|
|
return false;
|
|
|
|
json_object_object_add(jkey, "phase", jval);
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_add_app_key(uint16_t net_idx, uint16_t app_idx)
|
|
{
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
if (!add_app_key(cfg->jcfg, net_idx, app_idx))
|
|
return false;
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_del_app_key(uint16_t app_idx)
|
|
{
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
return delete_key(cfg->jcfg, "appKeys", app_idx);
|
|
}
|
|
|
|
bool mesh_db_add_group(struct mesh_group *grp)
|
|
{
|
|
json_object *jgroup, *jgroups, *jval;
|
|
char buf[16];
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(cfg->jcfg, "groups", &jgroups))
|
|
return false;
|
|
|
|
jgroup = json_object_new_object();
|
|
if (!jgroup)
|
|
return false;
|
|
|
|
snprintf(buf, 11, "Group_%4.4x", grp->addr);
|
|
jval = json_object_new_string(buf);
|
|
json_object_object_add(jgroup, "name", jval);
|
|
|
|
if (IS_VIRTUAL(grp->addr)) {
|
|
if (!add_u8_16(jgroup, "address", grp->label))
|
|
goto fail;
|
|
} else {
|
|
if (!write_uint16_hex(jgroup, "address", grp->addr))
|
|
goto fail;
|
|
}
|
|
|
|
/* Initialize parent group to unassigned address for now*/
|
|
if (!write_uint16_hex(jgroup, "parentAddress", UNASSIGNED_ADDRESS))
|
|
goto fail;
|
|
|
|
json_object_array_add(jgroups, jgroup);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
json_object_put(jgroup);
|
|
return false;
|
|
}
|
|
|
|
struct l_queue *mesh_db_load_groups(void)
|
|
{
|
|
json_object *jgroups;
|
|
struct l_queue *groups;
|
|
int i, sz;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return NULL;
|
|
|
|
if (!json_object_object_get_ex(cfg->jcfg, "groups", &jgroups)) {
|
|
jgroups = json_object_new_array();
|
|
if (!jgroups)
|
|
return NULL;
|
|
|
|
json_object_object_add(cfg->jcfg, "groups", jgroups);
|
|
}
|
|
|
|
groups = l_queue_new();
|
|
|
|
sz = json_object_array_length(jgroups);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jgroup, *jval;
|
|
struct mesh_group *grp;
|
|
uint16_t addr, addr_len;
|
|
const char *str;
|
|
|
|
jgroup = json_object_array_get_idx(jgroups, i);
|
|
if (!jgroup)
|
|
continue;
|
|
|
|
if (!json_object_object_get_ex(jgroup, "name", &jval))
|
|
continue;
|
|
|
|
str = json_object_get_string(jval);
|
|
if (strlen(str) != 10)
|
|
continue;
|
|
|
|
if (sscanf(str + 6, "%04hx", &addr) != 1)
|
|
continue;
|
|
|
|
if (!json_object_object_get_ex(jgroup, "address", &jval))
|
|
continue;
|
|
|
|
str = json_object_get_string(jval);
|
|
addr_len = strlen(str);
|
|
if (addr_len != 4 && addr_len != 32)
|
|
continue;
|
|
|
|
if (addr_len == 32 && !IS_VIRTUAL(addr))
|
|
continue;
|
|
|
|
grp = l_new(struct mesh_group, 1);
|
|
|
|
if (addr_len == 4)
|
|
sscanf(str, "%04hx", &grp->addr);
|
|
else {
|
|
str2hex(str, 32, grp->label, 16);
|
|
grp->addr = addr;
|
|
}
|
|
|
|
l_queue_insert(groups, grp, compare_group_addr, NULL);
|
|
}
|
|
|
|
return groups;
|
|
}
|
|
|
|
static json_object *init_elements(uint8_t num_els)
|
|
{
|
|
json_object *jelements;
|
|
uint8_t i;
|
|
|
|
jelements = json_object_new_array();
|
|
|
|
for (i = 0; i < num_els; ++i) {
|
|
json_object *jelement, *jmods;
|
|
|
|
jelement = json_object_new_object();
|
|
|
|
write_int(jelement, "index", i);
|
|
write_uint16_hex(jelement, "location", DEFAULT_LOCATION);
|
|
jmods = json_object_new_array();
|
|
json_object_object_add(jelement, "models", jmods);
|
|
|
|
json_object_array_add(jelements, jelement);
|
|
}
|
|
|
|
return jelements;
|
|
}
|
|
|
|
bool mesh_db_add_node(uint8_t uuid[16], uint8_t num_els, uint16_t unicast,
|
|
uint16_t net_idx)
|
|
{
|
|
json_object *jnode;
|
|
json_object *jelements, *jnodes, *jnetkeys, *jappkeys;
|
|
char buf[37];
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_uuid(cfg->jcfg, uuid);
|
|
if (jnode) {
|
|
l_error("Node already exists");
|
|
return false;
|
|
}
|
|
|
|
jnode = json_object_new_object();
|
|
if (!jnode)
|
|
return false;
|
|
|
|
if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
|
|
goto fail;
|
|
|
|
if (!add_string(jnode, "UUID", buf))
|
|
goto fail;
|
|
|
|
if (!add_string(jnode, "security", "secure"))
|
|
goto fail;
|
|
|
|
if (!write_bool(jnode, "excluded", false))
|
|
goto fail;
|
|
|
|
if (!write_bool(jnode, "configComplete", false))
|
|
goto fail;
|
|
|
|
jelements = init_elements(num_els);
|
|
|
|
json_object_object_add(jnode, "elements", jelements);
|
|
|
|
jnetkeys = json_object_new_array();
|
|
if (!jnetkeys)
|
|
goto fail;
|
|
|
|
json_object_object_add(jnode, "netKeys", jnetkeys);
|
|
|
|
if (!add_node_key(jnode, "netKeys", net_idx))
|
|
goto fail;
|
|
|
|
jappkeys = json_object_new_array();
|
|
if (!jappkeys)
|
|
goto fail;
|
|
|
|
json_object_object_add(jnode, "appKeys", jappkeys);
|
|
|
|
if (!write_uint16_hex(jnode, "unicastAddress", unicast))
|
|
goto fail;
|
|
|
|
if (!json_object_object_get_ex(cfg->jcfg, "nodes", &jnodes))
|
|
goto fail;
|
|
|
|
json_object_array_add(jnodes, jnode);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
json_object_put(jnode);
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_del_node(uint16_t unicast)
|
|
{
|
|
json_object *jarray;
|
|
int i, sz;
|
|
|
|
if (!json_object_object_get_ex(cfg->jcfg, "nodes", &jarray))
|
|
return false;
|
|
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
sz = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < sz; ++i) {
|
|
json_object *jentry, *jval;
|
|
uint16_t addr;
|
|
const char *str;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
if (!json_object_object_get_ex(jentry, "unicastAddress",
|
|
&jval))
|
|
continue;
|
|
|
|
str = json_object_get_string(jval);
|
|
if (sscanf(str, "%04hx", &addr) != 1)
|
|
continue;
|
|
|
|
if (addr == unicast)
|
|
break;
|
|
}
|
|
|
|
if (i == sz)
|
|
return true;
|
|
|
|
json_object_array_del_idx(jarray, i, 1);
|
|
|
|
return save_config();
|
|
}
|
|
|
|
static json_object *init_model(uint16_t mod_id)
|
|
{
|
|
json_object *jmod, *jarray;
|
|
|
|
jmod = json_object_new_object();
|
|
|
|
if (!write_uint16_hex(jmod, "modelId", mod_id)) {
|
|
json_object_put(jmod);
|
|
return NULL;
|
|
}
|
|
|
|
jarray = json_object_new_array();
|
|
json_object_object_add(jmod, "bind", jarray);
|
|
|
|
jarray = json_object_new_array();
|
|
json_object_object_add(jmod, "subscribe", jarray);
|
|
|
|
return jmod;
|
|
}
|
|
|
|
static json_object *init_vendor_model(uint32_t mod_id)
|
|
{
|
|
json_object *jmod, *jarray;
|
|
|
|
jmod = json_object_new_object();
|
|
|
|
if (!write_uint32_hex(jmod, "modelId", mod_id)) {
|
|
json_object_put(jmod);
|
|
return NULL;
|
|
}
|
|
|
|
jarray = json_object_new_array();
|
|
json_object_object_add(jmod, "bind", jarray);
|
|
|
|
jarray = json_object_new_array();
|
|
json_object_object_add(jmod, "subscribe", jarray);
|
|
|
|
return jmod;
|
|
}
|
|
|
|
bool mesh_db_node_set_composition(uint16_t unicast, uint8_t *data, uint16_t len)
|
|
{
|
|
uint16_t features;
|
|
int sz, i = 0;
|
|
json_object *jnode, *jobj, *jelements;
|
|
uint16_t crpl;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(cfg->jcfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
/* skip page -- We only support Page Zero */
|
|
data++;
|
|
len--;
|
|
|
|
/* If "crpl" property is present, composition is already recorded */
|
|
if (json_object_object_get_ex(jnode, "crpl", &jobj))
|
|
return true;
|
|
|
|
if (!write_uint16_hex(jnode, "cid", l_get_le16(&data[0])))
|
|
return false;
|
|
|
|
if (!write_uint16_hex(jnode, "pid", l_get_le16(&data[2])))
|
|
return false;
|
|
|
|
if (!write_uint16_hex(jnode, "vid", l_get_le16(&data[4])))
|
|
return false;
|
|
|
|
crpl = l_get_le16(&data[6]);
|
|
|
|
features = l_get_le16(&data[8]);
|
|
data += 10;
|
|
len -= 10;
|
|
|
|
jobj = json_object_object_get(jnode, "features");
|
|
if (!jobj) {
|
|
jobj = json_object_new_object();
|
|
json_object_object_add(jnode, "features", jobj);
|
|
}
|
|
|
|
if (!(features & FEATURE_RELAY))
|
|
write_int(jobj, "relay", 2);
|
|
|
|
if (!(features & FEATURE_FRIEND))
|
|
write_int(jobj, "friend", 2);
|
|
|
|
if (!(features & FEATURE_PROXY))
|
|
write_int(jobj, "proxy", 2);
|
|
|
|
if (!(features & FEATURE_LPN))
|
|
write_int(jobj, "lowPower", 2);
|
|
|
|
jelements = json_object_object_get(jnode, "elements");
|
|
if (!jelements)
|
|
return false;
|
|
|
|
sz = json_object_array_length(jelements);
|
|
|
|
while (len) {
|
|
json_object *jentry, *jmods;
|
|
uint32_t mod_id;
|
|
uint8_t m, v;
|
|
|
|
/* Mismatch in the element count */
|
|
if (i >= sz)
|
|
return false;
|
|
|
|
jentry = json_object_array_get_idx(jelements, i);
|
|
|
|
write_int(jentry, "index", i);
|
|
|
|
if (!write_uint16_hex(jentry, "location", l_get_le16(data)))
|
|
return false;
|
|
|
|
data += 2;
|
|
len -= 2;
|
|
|
|
m = *data++;
|
|
v = *data++;
|
|
len -= 2;
|
|
|
|
jmods = json_object_object_get(jentry, "models");
|
|
if (!jmods) {
|
|
/* For backwards compatibility */
|
|
jmods = json_object_new_array();
|
|
json_object_object_add(jentry, "models", jmods);
|
|
}
|
|
|
|
while (len >= 2 && m--) {
|
|
mod_id = l_get_le16(data);
|
|
|
|
jobj = init_model(mod_id);
|
|
if (!jobj)
|
|
goto fail;
|
|
|
|
json_object_array_add(jmods, jobj);
|
|
data += 2;
|
|
len -= 2;
|
|
}
|
|
|
|
while (len >= 4 && v--) {
|
|
jobj = json_object_new_object();
|
|
mod_id = l_get_le16(data + 2);
|
|
mod_id = l_get_le16(data) << 16 | mod_id;
|
|
|
|
jobj = init_vendor_model(mod_id);
|
|
if (!jobj)
|
|
goto fail;
|
|
|
|
json_object_array_add(jmods, jobj);
|
|
|
|
data += 4;
|
|
len -= 4;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
/* CRPL is written last. Will be used to check composition's presence */
|
|
if (!write_uint16_hex(jnode, "crpl", crpl))
|
|
goto fail;
|
|
|
|
/* Initiate remote's composition from storage */
|
|
if (!load_composition(jnode, unicast))
|
|
goto fail;
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
/* Reset elements array */
|
|
json_object_object_del(jnode, "elements");
|
|
init_elements(sz);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_get_token(uint8_t token[8])
|
|
{
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
memcpy(token, cfg->token, 8);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_db_get_addr_range(uint16_t *low, uint16_t *high)
|
|
{
|
|
json_object *jprov, *jarray, *jobj, *jlow, *jhigh;
|
|
const char *str;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
jarray = json_object_object_get(cfg->jcfg, "provisioners");
|
|
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
/* Assumption: only one provisioner in the system */
|
|
jprov = json_object_array_get_idx(jarray, 0);
|
|
if (!jprov)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jprov, "allocatedUnicastRange", &jarray))
|
|
return false;
|
|
|
|
/* Assumption: only one contiguous range is specified */
|
|
jobj = json_object_array_get_idx(jarray, 0);
|
|
if (!jobj)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jobj, "lowAddress", &jlow) ||
|
|
!json_object_object_get_ex(jobj, "highAddress", &jhigh))
|
|
return false;
|
|
|
|
str = json_object_get_string(jlow);
|
|
if (sscanf(str, "%04hx", low) != 1)
|
|
return false;
|
|
|
|
str = json_object_get_string(jhigh);
|
|
if (sscanf(str, "%04hx", high) != 1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* This is a simplistic implementation of allocated range, where
|
|
* the range is one contiguous chunk of the address space.
|
|
*/
|
|
static bool add_range(json_object *jobj, const char *keyword, uint16_t low,
|
|
uint16_t high)
|
|
{
|
|
json_object *jarray, *jrange;
|
|
|
|
jrange = json_object_new_object();
|
|
|
|
if (!write_uint16_hex(jrange, "lowAddress", low))
|
|
goto fail;
|
|
|
|
if (!write_uint16_hex(jrange, "highAddress", high))
|
|
goto fail;
|
|
|
|
jarray = json_object_new_array();
|
|
if (!jarray)
|
|
goto fail;
|
|
|
|
json_object_array_add(jarray, jrange);
|
|
json_object_object_add(jobj, keyword, jarray);
|
|
|
|
return true;
|
|
|
|
fail:
|
|
json_object_put(jrange);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_add_provisioner(const char *name, uint8_t uuid[16],
|
|
uint16_t unicast_low, uint16_t unicast_high,
|
|
uint16_t group_low, uint16_t group_high)
|
|
{
|
|
json_object *jprovs, *jprov, *jscenes;
|
|
char buf[37];
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(cfg->jcfg, "provisioners", &jprovs))
|
|
return false;
|
|
|
|
if (!jprovs || json_object_get_type(jprovs) != json_type_array)
|
|
return false;
|
|
|
|
jprov = json_object_new_object();
|
|
|
|
if (!add_string(jprov, "provisionerName", name))
|
|
goto fail;
|
|
|
|
if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
|
|
goto fail;
|
|
|
|
if (!add_string(jprov, "UUID", buf))
|
|
goto fail;
|
|
|
|
if (!add_range(jprov, "allocatedUnicastRange", unicast_low,
|
|
unicast_high))
|
|
goto fail;
|
|
|
|
if (!add_range(jprov, "allocatedGroupRange", group_low, group_high))
|
|
goto fail;
|
|
|
|
/* Scenes are not supported. Just add an empty array */
|
|
jscenes = json_object_new_array();
|
|
if (!jscenes)
|
|
goto fail;
|
|
|
|
json_object_object_add(jprov, "allocatedSceneRange", jscenes);
|
|
|
|
json_object_array_add(jprovs, jprov);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
json_object_put(jprov);
|
|
return false;
|
|
}
|
|
|
|
uint32_t mesh_db_get_iv_index(void)
|
|
{
|
|
int ivi;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return 0;
|
|
|
|
if (!get_int(cfg->jcfg, "ivIndex", &ivi))
|
|
return 0;
|
|
|
|
return (uint32_t) ivi;
|
|
}
|
|
|
|
bool mesh_db_set_iv_index(uint32_t ivi)
|
|
{
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
write_int(cfg->jcfg, "ivIndex", ivi);
|
|
|
|
return save_config();
|
|
}
|
|
|
|
static int get_rejected_by_iv_index(json_object *jarray, uint32_t iv_index)
|
|
{
|
|
int i, cnt;
|
|
|
|
cnt = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
json_object *jentry;
|
|
int index;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
|
|
if (!get_int(jentry, "ivIndex", &index))
|
|
continue;
|
|
|
|
if (iv_index == (uint32_t)index)
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static bool load_rejected_addresses(json_object *jobj)
|
|
{
|
|
json_object *jarray;
|
|
int i, cnt;
|
|
|
|
json_object_object_get_ex(jobj, "networkExclusions", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return true;
|
|
|
|
cnt = json_object_array_length(jarray);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
json_object *jaddrs, *jentry, *jval;
|
|
int iv_index, addr_cnt, j;
|
|
|
|
jentry = json_object_array_get_idx(jarray, i);
|
|
|
|
if (!get_int(jentry, "ivIndex", &iv_index))
|
|
return false;
|
|
|
|
if (!json_object_object_get_ex(jentry, "addresses",
|
|
&jaddrs))
|
|
return false;
|
|
|
|
addr_cnt = json_object_array_length(jaddrs);
|
|
|
|
for (j = 0; j < addr_cnt; j++) {
|
|
const char *str;
|
|
uint16_t unicast;
|
|
|
|
jval = json_object_array_get_idx(jaddrs, j);
|
|
str = json_object_get_string(jval);
|
|
|
|
if (sscanf(str, "%04hx", &unicast) != 1)
|
|
return false;
|
|
|
|
remote_add_rejected_address(unicast, iv_index, false);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool mesh_db_add_rejected_addr(uint16_t unicast, uint32_t iv_index)
|
|
{
|
|
json_object *jarray, *jobj, *jaddrs, *jstring;
|
|
int idx;
|
|
char buf[5];
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
json_object_object_get_ex(cfg->jcfg, "networkExclusions", &jarray);
|
|
if (!jarray) {
|
|
jarray = json_object_new_array();
|
|
json_object_object_add(cfg->jcfg, "networkExclusions", jarray);
|
|
}
|
|
|
|
idx = get_rejected_by_iv_index(jarray, iv_index);
|
|
|
|
if (idx < 0) {
|
|
jobj = json_object_new_object();
|
|
|
|
if (!write_int(jobj, "ivIndex", iv_index))
|
|
goto fail;
|
|
|
|
jaddrs = json_object_new_array();
|
|
json_object_object_add(jobj, "addresses", jaddrs);
|
|
|
|
} else {
|
|
jobj = json_object_array_get_idx(jarray, idx);
|
|
}
|
|
|
|
json_object_object_get_ex(jobj, "addresses", &jaddrs);
|
|
|
|
snprintf(buf, 5, "%4.4x", unicast);
|
|
jstring = json_object_new_string(buf);
|
|
if (!jstring)
|
|
goto fail;
|
|
|
|
json_object_array_add(jaddrs, jstring);
|
|
|
|
if (idx < 0)
|
|
json_object_array_add(jarray, jobj);
|
|
|
|
return save_config();
|
|
|
|
fail:
|
|
json_object_put(jobj);
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_clear_rejected(uint32_t iv_index)
|
|
{
|
|
json_object *jarray;
|
|
int idx;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
json_object_object_get_ex(cfg->jcfg, "networkExclusions", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
idx = get_rejected_by_iv_index(jarray, iv_index);
|
|
if (idx < 0)
|
|
return true;
|
|
|
|
json_object_array_del_idx(jarray, idx, 1);
|
|
|
|
return save_config();
|
|
}
|
|
|
|
bool mesh_db_create(const char *fname, const uint8_t token[8],
|
|
const char *mesh_name)
|
|
{
|
|
json_object *jcfg, *jarray;
|
|
uint8_t uuid[16];
|
|
char buf[37];
|
|
|
|
if (cfg)
|
|
return false;
|
|
|
|
if (!fname)
|
|
return false;
|
|
|
|
jcfg = json_object_new_object();
|
|
if (!jcfg)
|
|
return false;
|
|
|
|
cfg = l_new(struct mesh_db, 1);
|
|
cfg->jcfg = jcfg;
|
|
cfg->cfg_fname = l_strdup(fname);
|
|
memcpy(cfg->token, token, 8);
|
|
|
|
if (!add_u8_8(jcfg, "token", token))
|
|
goto fail;
|
|
|
|
l_uuid_v4(uuid);
|
|
|
|
if (!l_uuid_to_string(uuid, buf, sizeof(buf)))
|
|
goto fail;
|
|
|
|
if (!add_string(jcfg, "meshUUID", buf))
|
|
goto fail;
|
|
|
|
if (mesh_name && !add_string(jcfg, "meshName", mesh_name))
|
|
goto fail;
|
|
|
|
jarray = json_object_new_array();
|
|
if (!jarray)
|
|
goto fail;
|
|
|
|
json_object_object_add(jcfg, "nodes", jarray);
|
|
|
|
jarray = json_object_new_array();
|
|
if (!jarray)
|
|
goto fail;
|
|
|
|
json_object_object_add(jcfg, "provisioners", jarray);
|
|
|
|
jarray = json_object_new_array();
|
|
if (!jarray)
|
|
goto fail;
|
|
|
|
json_object_object_add(jcfg, "netKeys", jarray);
|
|
|
|
jarray = json_object_new_array();
|
|
if (!jarray)
|
|
goto fail;
|
|
|
|
json_object_object_add(jcfg, "appKeys", jarray);
|
|
|
|
jarray = json_object_new_array();
|
|
if (!jarray)
|
|
goto fail;
|
|
|
|
json_object_object_add(jcfg, "networkExclusions", jarray);
|
|
|
|
write_int(jcfg, "ivIndex", 0);
|
|
|
|
if (!save_config())
|
|
goto fail;
|
|
|
|
return true;
|
|
|
|
fail:
|
|
release_config();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_load(const char *fname)
|
|
{
|
|
int fd;
|
|
char *str;
|
|
struct stat st;
|
|
ssize_t sz;
|
|
json_object *jcfg;
|
|
|
|
fd = open(fname, O_RDONLY);
|
|
if (fd < 0)
|
|
return false;
|
|
|
|
if (fstat(fd, &st) == -1) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
str = (char *) l_new(char, st.st_size + 1);
|
|
if (!str) {
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
sz = read(fd, str, st.st_size);
|
|
if (sz != st.st_size) {
|
|
l_error("Failed to read configuration file %s", fname);
|
|
return false;
|
|
}
|
|
|
|
jcfg = json_tokener_parse(str);
|
|
|
|
close(fd);
|
|
l_free(str);
|
|
|
|
if (!jcfg)
|
|
return false;
|
|
|
|
cfg = l_new(struct mesh_db, 1);
|
|
|
|
cfg->jcfg = jcfg;
|
|
cfg->cfg_fname = l_strdup(fname);
|
|
|
|
if (!get_token(jcfg, cfg->token)) {
|
|
l_error("Configuration file missing token");
|
|
goto fail;
|
|
}
|
|
|
|
if (!load_keys(jcfg))
|
|
goto fail;
|
|
|
|
load_remotes(jcfg);
|
|
|
|
load_rejected_addresses(jcfg);
|
|
|
|
return true;
|
|
fail:
|
|
release_config();
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mesh_db_set_device_key(void *expt_cfg, uint16_t unicast, uint8_t key[16])
|
|
{
|
|
json_object *jnode;
|
|
|
|
if (!expt_cfg)
|
|
return false;
|
|
|
|
jnode = get_node_by_unicast(expt_cfg, unicast);
|
|
if (!jnode)
|
|
return false;
|
|
|
|
return add_u8_16(jnode, "deviceKey", key);
|
|
}
|
|
|
|
bool mesh_db_set_net_key(void *expt_cfg, uint16_t idx, uint8_t key[16],
|
|
uint8_t *old_key, uint8_t phase)
|
|
{
|
|
json_object *jarray, *jkey;
|
|
|
|
if (!expt_cfg)
|
|
return false;
|
|
|
|
json_object_object_get_ex(expt_cfg, "netKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
jkey = get_key_object(jarray, idx);
|
|
if (!jkey)
|
|
return false;
|
|
|
|
if (!write_int(jkey, "phase", phase))
|
|
return false;
|
|
|
|
if (!add_u8_16(jkey, "key", key))
|
|
return false;
|
|
|
|
if (old_key && !(!add_u8_16(jkey, "oldKey", old_key)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool mesh_db_set_app_key(void *expt_cfg, uint16_t net_idx, uint16_t app_idx,
|
|
uint8_t key[16], uint8_t *old_key)
|
|
{
|
|
json_object *jarray, *jkey;
|
|
|
|
if (!expt_cfg)
|
|
return false;
|
|
|
|
json_object_object_get_ex(expt_cfg, "appKeys", &jarray);
|
|
if (!jarray || json_object_get_type(jarray) != json_type_array)
|
|
return false;
|
|
|
|
jkey = get_key_object(jarray, app_idx);
|
|
if (!jkey)
|
|
return false;
|
|
|
|
if (!add_u8_16(jkey, "key", key))
|
|
return false;
|
|
|
|
if (old_key && !(!add_u8_16(jkey, "oldKey", old_key)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void *mesh_db_prepare_export(void)
|
|
{
|
|
json_object *export = NULL, *jarray;
|
|
|
|
if (!cfg || !cfg->jcfg)
|
|
return false;
|
|
|
|
if (json_object_deep_copy(cfg->jcfg, &export, NULL) != 0)
|
|
return NULL;
|
|
|
|
/* Delete token */
|
|
json_object_object_del(export, "token");
|
|
|
|
/* Delete IV index */
|
|
json_object_object_del(export, "ivIndex");
|
|
|
|
/* Scenes are not supported. Just add an empty array */
|
|
jarray = json_object_new_array();
|
|
json_object_object_add(export, "scenes", jarray);
|
|
|
|
if (!write_bool(export, "partial", false))
|
|
l_warn("Failed to write\"partial\" property");
|
|
|
|
return export;
|
|
}
|
|
|
|
bool mesh_db_finish_export(bool is_error, void *expt_cfg, const char *fname)
|
|
{
|
|
FILE *outfile = NULL;
|
|
const char *str, *hdr;
|
|
json_object *jhdr = NULL;
|
|
bool result = false;
|
|
char *pos;
|
|
|
|
uint32_t sz;
|
|
|
|
if (!expt_cfg)
|
|
return false;
|
|
|
|
if (is_error) {
|
|
json_object_put(expt_cfg);
|
|
return true;
|
|
}
|
|
|
|
if (!fname)
|
|
goto done;
|
|
|
|
outfile = fopen(fname, "w");
|
|
if (!outfile) {
|
|
l_error("Failed to save configuration to %s", fname);
|
|
goto done;
|
|
}
|
|
|
|
jhdr = json_object_new_object();
|
|
if (!add_string(jhdr, "$schema", js_schema))
|
|
goto done;
|
|
|
|
if (!add_string(jhdr, "id", schema_id))
|
|
goto done;
|
|
|
|
if (!add_string(jhdr, "version", schema_version))
|
|
goto done;
|
|
|
|
hdr = json_object_to_json_string_ext(jhdr, JSON_C_TO_STRING_PRETTY |
|
|
JSON_C_TO_STRING_NOSLASHESCAPE);
|
|
|
|
str = json_object_to_json_string_ext(expt_cfg, JSON_C_TO_STRING_PRETTY |
|
|
JSON_C_TO_STRING_NOSLASHESCAPE);
|
|
|
|
if (!hdr || !str)
|
|
goto done;
|
|
|
|
/*
|
|
* Write two strings to the output while stripping closing "}" from the
|
|
* header string and opening "{" from the config object.
|
|
*/
|
|
|
|
pos = strrchr(hdr, '}');
|
|
if (!pos)
|
|
goto done;
|
|
|
|
*pos = '\0';
|
|
|
|
pos = strrchr(hdr, '"');
|
|
if (!pos)
|
|
goto done;
|
|
|
|
pos[1] = ',';
|
|
|
|
if (fwrite(hdr, sizeof(char), strlen(hdr), outfile) < strlen(hdr))
|
|
goto done;
|
|
|
|
pos = strchr(str, '{');
|
|
if (!pos || pos[1] == '\0')
|
|
goto done;
|
|
|
|
pos++;
|
|
|
|
sz = strlen(pos);
|
|
|
|
if (fwrite(pos, sizeof(char), sz, outfile) < sz)
|
|
goto done;
|
|
|
|
result = true;
|
|
|
|
done:
|
|
if (outfile)
|
|
fclose(outfile);
|
|
|
|
json_object_put(expt_cfg);
|
|
|
|
if (jhdr)
|
|
json_object_put(jhdr);
|
|
|
|
return result;
|
|
}
|