Select Git revision
db-device.c 22.94 KiB
/* db - Database for xAAL Agent
* Part of the xaaws software
* (c) 2019 Christophe Lohr <christophe.lohr@imt-atlantique.fr>
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <string.h>
#include <errno.h>
#include <uuid/uuid.h>
#include <regex.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <xaal_jc.h>
#include "db-device.h"
#include "db-schema.h"
/*
* Section Devices
*/
/* load a DB of devices from a cbor file */
void load_devices(devices_t *devices, char *file) {
struct cbor_load_result cresult;
cbor_item_t *cdevices;
unsigned char *file_map;
struct stat sb;
size_t i, sz;
int fd;
if (!file) return;
fd = open(file, O_RDONLY);
if (fd == -1) {
fprintf(stderr, "Could not open(r) database file %s. %s\n", file, strerror(errno));
return;
}
if (fstat(fd, &sb) == -1) { /* get file size */
fprintf(stderr, "Could not do fstat on database file %s. %s\n", file, strerror(errno));
return;
}
file_map = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (file_map == MAP_FAILED) {
fprintf(stderr, "Could not mmap database file %s. %s\n", file, strerror(errno));
return;
}
cdevices = cbor_load(file_map, sb.st_size, &cresult);
munmap(file_map, sb.st_size);
close(fd);
if ( !cdevices ) {
fprintf(stderr, "Could not parse database file %s. CBOR error; code %d, position %lu\n",
file, cresult.error.code, cresult.error.position);
return;
}
if ( cbor_isa_array(cdevices) ) {
sz = cbor_array_size(cdevices);
for (i = 0; i < sz; i++)
update_device(devices, cbor_move(cbor_array_get(cdevices, i)));
}
cbor_decref(&cdevices);
}
/* dump DB of devices to a cbor file */
void dump_devices(devices_t *devices, char *file) {
cbor_item_t *cdevices, *cdevice;
device_t *device;
unsigned char *buffer;
size_t size, len;
int fd;
cdevices = cbor_new_indefinite_array();
TAILQ_FOREACH(device, devices, entries) {
cdevice = get_cbor_device(device); /* address, dev_type, description */
cbor_map_add(cdevice, (struct cbor_pair){ cbor_move(cbor_build_string("values")), cbor_move(get_cbor_values(device)) }); /* attributes */
cbor_map_add(cdevice, (struct cbor_pair){ cbor_move(cbor_build_string("map")), cbor_move(get_cbor_kvs(device)) }); /* metadata */
cbor_array_push(cdevices, cbor_move(cdevice));
}
fd = open(file, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
if (fd == -1) {
fprintf(stderr, "Could not open(w+) database file %s. %s\n", file, strerror(errno));
return;
}
len = cbor_serialize_alloc(cdevices, &buffer, &size);
if ( write(fd, buffer, len) != len )
fprintf(stderr, "Writing database file: %s\n", strerror(errno));
free(buffer);
close(fd);
cbor_decref(&cdevices);
}
/* update a device from loading cbor data */
void update_device(devices_t *devices, cbor_item_t *cdevice) {
cbor_item_t *caddress, *ctimeout, *cvals, *cval, *cvalue, *cdate, *cmap;
char *dev_type, *attribute, *attrtype;
uuid_t addr;
device_t *device;
size_t sz, i, len;
time_t date, now = time(NULL);
caddress = xAAL_cbor_map_get(cdevice, "address");
if ( !xAAL_cbor_is_uuid(caddress, &addr) )
return;
dev_type = xAAL_cbor_string_dup(xAAL_cbor_map_get(cdevice, "dev_type"), &len);
if ( !dev_type )
return;
if ( !validate_identifier(dev_type) ) {
free(dev_type);
return;
}
device = add_device(devices, &addr);
if (device->devinfo.dev_type) {
if (strcmp(device->devinfo.dev_type, dev_type) != 0) {
free(dev_type);
return;
}
free(dev_type);
} else
device->devinfo.dev_type = dev_type;
update_description(device, cdevice);
ctimeout = xAAL_cbor_map_get(cdevice, "timeout");
if ( cbor_is_int(ctimeout) )
device->timeout = cbor_get_int(ctimeout);
cvals = xAAL_cbor_map_get(cdevice, "values");
if ( cvals && cbor_isa_array(cvals) ) {
sz = cbor_array_size(cvals);
for (i = 0; i < sz; i++) {
cval = cbor_move(cbor_array_get(cvals, i));
attribute = xAAL_cbor_string_dup(xAAL_cbor_map_get(cval, "name"), &len);
if ( !attribute )
continue;
if ( !validate_identifier(attribute) ) {
free(attribute);
continue;
}
attrtype = xAAL_cbor_string_dup(xAAL_cbor_map_get(cval, "type"), &len);
if ( !validate_identifier(attrtype) ) {
free(attrtype);
attrtype = NULL;
}
cvalue = xAAL_cbor_map_get(cval, "value");
if ( !cvalue ) {
free(attribute);
continue;
}
cdate = xAAL_cbor_map_get(cval, "date");
if ( cdate && cbor_is_int(cdate) )
date = cbor_get_int(cdate);
else
date = now;
update_value(&(device->vals), attribute, attrtype, cvalue, date);
if (attrtype) free(attrtype);
}
}
cmap = xAAL_cbor_map_get(cdevice, "map");
if ( cmap && cbor_isa_map(cmap) )
update_map(&(device->map), cmap);
}
/* select a device by its address */
device_t *select_device(devices_t *devices, const uuid_t *addr) {
device_t *np;
TAILQ_FOREACH(np, devices, entries)
if (uuid_compare(*addr, np->devinfo.addr) == 0)
return np;
return NULL;
}
/* select or insert a device (its addr) */
device_t *add_device(devices_t *devices, const uuid_t *addr) {
device_t *np;
TAILQ_FOREACH(np, devices, entries)
if (uuid_compare(*addr, np->devinfo.addr) == 0)
return np;
np = (device_t *)malloc(sizeof(device_t));
uuid_copy(np->devinfo.addr, *addr);
np->devinfo.dev_type = NULL;
np->devinfo.vendor_id = NULL;
np->devinfo.product_id = NULL;
np->devinfo.hw_id = NULL;
np->devinfo.version = NULL;
np->devinfo.group_id = NULL;
np->devinfo.url = NULL;
np->devinfo.schema = NULL;
np->devinfo.info = NULL;
np->devinfo.unsupported_attributes = NULL;
np->devinfo.unsupported_methods = NULL;
np->devinfo.unsupported_notifications = NULL;
np->timeout = 0;
TAILQ_INIT(&(np->vals));
TAILQ_INIT(&(np->map));
TAILQ_INSERT_TAIL(devices, np, entries);
return np;
}
/* update the description of a device */
void update_description(device_t *device, cbor_item_t *cdescription) {
cbor_item_t *chw_id, *cuAttributes, *cuMethods, *cuNotifications;
char *vendor_id, *product_id, *version, *url, *schema, *info;
uuid_t *group_id;
size_t len;
vendor_id = xAAL_cbor_string_dup(xAAL_cbor_map_get(cdescription, "vendor_id"), &len);
if ( vendor_id )
device->devinfo.vendor_id = vendor_id;
product_id = xAAL_cbor_string_dup(xAAL_cbor_map_get(cdescription, "product_id"), &len);
if ( product_id )
device->devinfo.product_id = product_id;
chw_id = xAAL_cbor_map_get(cdescription, "hw_id");
if ( chw_id )
device->devinfo.hw_id = cbor_incref(chw_id);
version = xAAL_cbor_string_dup(xAAL_cbor_map_get(cdescription, "version"), &len);
if ( version )
device->devinfo.version = version;
group_id = (uuid_t *)malloc(sizeof(uuid_t));
xAAL_cbor_map_get(cdescription, "group_id");
if ( xAAL_cbor_is_uuid(xAAL_cbor_map_get(cdescription, "group_id"), group_id) )
device->devinfo.group_id = group_id;
else
free(group_id);
url = xAAL_cbor_string_dup(xAAL_cbor_untag(xAAL_cbor_map_get(cdescription, "url")), &len);
if ( url )
device->devinfo.url = url;
schema = xAAL_cbor_string_dup(xAAL_cbor_untag(xAAL_cbor_map_get(cdescription, "schema")), &len);
if ( schema )
device->devinfo.schema = schema;
info = xAAL_cbor_string_dup(xAAL_cbor_map_get(cdescription, "info"), &len);
if ( info )
device->devinfo.info = info;
cuAttributes = xAAL_cbor_map_get(cdescription, "unsupported_attributes");
if ( cuAttributes && cbor_isa_array(cuAttributes) )
device->devinfo.unsupported_attributes = xAAL_strings_unpack(cuAttributes);
cuMethods = xAAL_cbor_map_get(cdescription, "unsupported_methods");
if ( cuMethods && cbor_isa_array(cuMethods) )
device->devinfo.unsupported_methods = xAAL_strings_unpack(cuMethods);
cuNotifications = xAAL_cbor_map_get(cdescription, "unsupported_notifications");
if ( cuNotifications && cbor_isa_array(cuNotifications) )
device->devinfo.unsupported_notifications = xAAL_strings_unpack(cuNotifications);
}
/* get (cbor) devices (having a given key in its kv map, or all) */
cbor_item_t *get_cbor_devices(devices_t *devices, const char *key) {
cbor_item_t *carray = cbor_new_indefinite_array();
cbor_item_t *crow;
device_t *np = NULL;
TAILQ_FOREACH(np, devices, entries)
if ( (key == NULL) || (select_kv(&(np->map), key) != NULL) ) {
crow = cbor_new_definite_map(3);
cbor_map_add(crow, (struct cbor_pair){ cbor_move(cbor_build_string("address")), cbor_move(cbor_build_bytestring(np->devinfo.addr, 16)) });
cbor_map_add(crow, (struct cbor_pair){ cbor_move(cbor_build_string("dev_type")), cbor_move(cbor_build_string(np->devinfo.dev_type)) });
cbor_map_add(crow, (struct cbor_pair){ cbor_move(cbor_build_string("timeout")), cbor_move(xAAL_cbor_build_int(np->timeout)) });
cbor_array_push(carray, crow);
}
return carray;
}
/* get (json) devices (having a given key in its map, or all) */
json_object *get_json_devices(devices_t *devices, const char *key) {
json_object *jarray = json_object_new_array();
json_object *jrow;
device_t *np = NULL;
char uuid[37];
TAILQ_FOREACH(np, devices, entries)
if ( (key == NULL) || (select_kv(&(np->map), key) != NULL) ) {
jrow = json_object_new_object();
uuid_unparse(np->devinfo.addr, uuid);
json_object_object_add(jrow, "address", json_object_new_string(uuid));
if (np->devinfo.dev_type)
json_object_object_add(jrow, "dev_type", json_object_new_string(np->devinfo.dev_type));
if (np->timeout)
json_object_object_add(jrow, "timeout", json_object_new_int64(np->timeout));
json_object_array_add(jarray, jrow);
}
return jarray;
}
/* serialize a device to cbor */
cbor_item_t *get_cbor_device(device_t *device) {
cbor_item_t *cobj = cbor_new_indefinite_map();
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("address")), cbor_move(cbor_build_bytestring(device->devinfo.addr, 16)) });
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("dev_type")), cbor_move(cbor_build_string(device->devinfo.dev_type)) });
if (device->devinfo.vendor_id)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("vendor_id")), cbor_move(cbor_build_string(device->devinfo.vendor_id)) });
if (device->devinfo.product_id)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("product_id")), cbor_move(cbor_build_string(device->devinfo.product_id)) });
if (device->devinfo.hw_id)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("hw_id")), device->devinfo.hw_id });
if (device->devinfo.version)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("version")), cbor_move(cbor_build_string(device->devinfo.version)) });
if (device->devinfo.group_id)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("group_id")), cbor_move(cbor_build_bytestring(*(device->devinfo.group_id), 16)) });
if (device->devinfo.url)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("url")), cbor_move(cbor_build_string(device->devinfo.url)) });
if (device->devinfo.schema)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("schema")), cbor_move(cbor_build_string(device->devinfo.schema)) });
if (device->devinfo.info)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("info")), cbor_move(cbor_build_string(device->devinfo.info)) });
if (device->devinfo.unsupported_attributes)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("unsupported_attributes")), cbor_move(xAAL_strings_pack(device->devinfo.unsupported_attributes)) });
if (device->devinfo.unsupported_methods)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("unsupported_methods")), cbor_move(xAAL_strings_pack(device->devinfo.unsupported_methods)) });
if (device->devinfo.unsupported_notifications)
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("unsupported_notifications")), cbor_move(xAAL_strings_pack(device->devinfo.unsupported_notifications)) });
cbor_map_add(cobj, (struct cbor_pair){ cbor_move(cbor_build_string("timeout")), cbor_move(xAAL_cbor_build_int(device->timeout)) });
return cobj;
}
/* serialize a null terminatied list of strings to json */
json_object *strings2json(char **list) {
json_object *jarray = json_object_new_array();
char **idx;
for (idx = list; *idx != NULL; idx++)
json_object_array_add(jarray, json_object_new_string(*idx));
return jarray;
}
/* serialize a device to json */
json_object *get_json_device(device_t *device) {
json_object *jobj = json_object_new_object();
char uuid[37];
uuid_unparse(device->devinfo.addr, uuid);
json_object_object_add(jobj, "address", json_object_new_string(uuid));
json_object_object_add(jobj, "dev_type", json_object_new_string(device->devinfo.dev_type));
if (device->devinfo.vendor_id)
json_object_object_add(jobj, "vendor_id",
json_object_new_string(device->devinfo.vendor_id));
if (device->devinfo.product_id)
json_object_object_add(jobj, "product_id",
json_object_new_string(device->devinfo.product_id));
if (device->devinfo.hw_id)
json_object_object_add(jobj, "hw_id",
xAAL_cbor2json(device->devinfo.hw_id));
if (device->devinfo.version)
json_object_object_add(jobj, "version",
json_object_new_string(device->devinfo.version));
if (device->devinfo.group_id) {
uuid_unparse(*(device->devinfo.group_id), uuid);
json_object_object_add(jobj, "group_id", json_object_new_string(uuid));
}
if (device->devinfo.url)
json_object_object_add(jobj, "url",
json_object_new_string(device->devinfo.url));
if (device->devinfo.schema)
json_object_object_add(jobj, "schema",
json_object_new_string(device->devinfo.schema));
if (device->devinfo.info)
json_object_object_add(jobj, "info",
json_object_new_string(device->devinfo.info));
if (device->devinfo.unsupported_attributes)
json_object_object_add(jobj, "unsupported_attributes",
strings2json(device->devinfo.unsupported_attributes));
if (device->devinfo.unsupported_methods)
json_object_object_add(jobj, "unsupported_methods",
strings2json(device->devinfo.unsupported_methods));
if (device->devinfo.unsupported_notifications)
json_object_object_add(jobj, "unsupported_notifications",
strings2json(device->devinfo.unsupported_notifications));
json_object_object_add(jobj, "timeout", json_object_new_int64(device->timeout));
return jobj;
}
/* select a device and serialize it to cbor */
cbor_item_t *get_cbor_device_by_addr(devices_t *devices, const uuid_t *addr) {
device_t *device = select_device(devices, addr);
if (device)
return get_cbor_device(device);
return cbor_new_indefinite_map();
}
/* select a device and serialize it to cbor */
json_object *get_json_device_by_addr(devices_t *devices, const uuid_t *addr) {
device_t *device = select_device(devices, addr);
if (device)
return get_json_device(device);
return json_object_new_object();;
}
/* free a null-terminated vector of strings */
void free_vector(char **items) {
char **item;
for (item = items; *item != NULL; item++)
free(*item);
free(items);
}
/* delete a device */
void delete_device(devices_t *devices, device_t *device) {
TAILQ_REMOVE(devices, device, entries);
if (device->devinfo.dev_type)
free(device->devinfo.dev_type);
if (device->devinfo.vendor_id)
free(device->devinfo.vendor_id);
if (device->devinfo.product_id)
free(device->devinfo.product_id);
if (device->devinfo.hw_id)
cbor_decref(&device->devinfo.hw_id);
if (device->devinfo.version)
free(device->devinfo.version);
if (device->devinfo.group_id)
free(device->devinfo.group_id);
if (device->devinfo.url)
free(device->devinfo.url);
if (device->devinfo.schema)
free(device->devinfo.schema);
if (device->devinfo.info)
free(device->devinfo.info);
if (device->devinfo.unsupported_attributes)
free_vector(device->devinfo.unsupported_attributes);
if (device->devinfo.unsupported_methods)
free_vector(device->devinfo.unsupported_methods);
if (device->devinfo.unsupported_notifications)
free_vector(device->devinfo.unsupported_notifications);
delete_map(&(device->map));
delete_values(&(device->vals));
free(device);
}
/*
* Section Values of Attributes of a Device
*/
/* select a value by its name */
val_t *select_value(vals_t *vals, const char *name) {
val_t *val;
TAILQ_FOREACH(val, vals, entries)
if (val->name && (strcmp(val->name, name) == 0) )
return val;
return NULL;
}
/* insert or update the value of an attribute of a device */
void update_value(vals_t *vals, const char *name, const char *type, cbor_item_t *value, time_t date) {
val_t *val;
if (!name)
return;
val = select_value(vals, name);
if (!val) {
val = (val_t *)malloc(sizeof(val_t));
val->name = strdup(name);
val->type = type ? strdup(type) : NULL;
val->date = 0;
TAILQ_INSERT_TAIL(vals, val, entries);
} else {
if (type) {
if (val->type && strcmp(val->type, type))
free(val->type);
val->type = strdup(type);
}
}
if (date > val->date) {
val->value = cbor_incref(value);
val->date = date;
}
}
/* serialize values of attributes of a device to cbor */
cbor_item_t *get_cbor_values(device_t *device) {
cbor_item_t *carray = cbor_new_indefinite_array();
cbor_item_t *crow;
val_t *np = NULL;
TAILQ_FOREACH(np, &(device->vals), entries) {
crow = cbor_new_definite_map(3);
cbor_map_add(crow, (struct cbor_pair){ cbor_move(cbor_build_string("name")), cbor_move(cbor_build_string(np->name)) });
if (np->type)
cbor_map_add(crow, (struct cbor_pair){ cbor_move(cbor_build_string("type")), cbor_move(cbor_build_string(np->type)) });
cbor_map_add(crow, (struct cbor_pair){ cbor_move(cbor_build_string("value")), np->value });
cbor_map_add(crow, (struct cbor_pair){ cbor_move(cbor_build_string("date")), cbor_move(xAAL_cbor_build_int(np->date)) });
cbor_array_push(carray, cbor_move(crow));
}
return carray;
}
/* serialize values of attributes of a device to json */
json_object *get_json_values(device_t *device) {
json_object *jarray = json_object_new_array();
json_object *jrow;
val_t *np = NULL;
TAILQ_FOREACH(np, &(device->vals), entries) {
jrow = json_object_new_object();
json_object_object_add(jrow, "name", json_object_new_string(np->name));
if (np->type)
json_object_object_add(jrow, "type", json_object_new_string(np->type));
json_object_object_add(jrow, "value", xAAL_cbor2json(np->value));
json_object_object_add(jrow, "date", json_object_new_int64(np->date));
json_object_array_add(jarray, jrow);
}
return jarray;
}
/* select a device and serialize its attributes values to cbor */
cbor_item_t *get_cbor_values_by_addr(devices_t *devices, const uuid_t *addr) {
device_t *device = select_device(devices, addr);
if (device)
return get_cbor_values(device);
return NULL;
}
/* select a device and serialize its attributes values to json */
json_object *get_json_values_by_addr(devices_t *devices, const uuid_t *addr) {
device_t *device = select_device(devices, addr);
if (device)
return get_json_values(device);
return NULL;
}
/* delete the list of values */
void delete_values(vals_t *vals) {
while (vals->tqh_first != NULL) {
cbor_decref(&vals->tqh_first->value);
if (vals->tqh_first->name) free(vals->tqh_first->name);
if (vals->tqh_first->type) free(vals->tqh_first->type);
TAILQ_REMOVE(vals, vals->tqh_first, entries);
free(vals->tqh_first);
}
}
/*
* Section Map (keys-values metadata) of a Device
*/
/* select a key-value in the map if present */
kv_t *select_kv(map_t *map, const char *key) {
kv_t *np;
TAILQ_FOREACH(np, map, entries)
if (strcmp(key, np->key) == 0)
return np;
return NULL;
}
/* insert a key-value in the map */
void insert_kv(map_t *map, const char *key, const char *value) {
kv_t *np;
TAILQ_FOREACH(np, map, entries)
if (strcmp(key, np->key) == 0) {
if (!strcmp(value, np->value) == 0) {
free(np->value);
np->value = strdup(value);
}
return;
}
np = (kv_t *)malloc(sizeof(kv_t));
np->key = strdup(key);
np->value = strdup(value);
TAILQ_INSERT_TAIL(map, np, entries);
}
/* serialize KVs-map of a device in cbor */
cbor_item_t *get_cbor_kvs(device_t *device) {
cbor_item_t *cmap = cbor_new_indefinite_map();
kv_t *np = NULL;
TAILQ_FOREACH(np, &(device->map), entries)
cbor_map_add(cmap, (struct cbor_pair){ cbor_move(cbor_build_string(np->key)), cbor_move(cbor_build_string(np->value)) });
return cmap;
}
/* serialize KVs-map of a device in json */
json_object *get_json_map(device_t *device) {
json_object *jmap = json_object_new_object();
kv_t *np = NULL;
TAILQ_FOREACH(np, &(device->map), entries)
json_object_object_add(jmap, np->key, json_object_new_string(np->value));
return jmap;
}
/* get (cbor) KVs-map of a device by addr */
cbor_item_t *get_cbor_kvs_by_addr(devices_t *devices, const uuid_t *addr) {
device_t *device = select_device(devices, addr);
if (device)
return get_cbor_kvs(device);
return NULL;
}
/* get (json) KVs-map of a device by addr */
json_object *get_json_kvs_by_addr(devices_t *devices, const uuid_t *addr) {
device_t *device = select_device(devices, addr);
if (device)
return get_json_map(device);
return NULL;
}
/* delete a key-value in the map */
void delete_kv(map_t *map, const char *key) {
kv_t *np;
TAILQ_FOREACH(np, map, entries)
if (strcmp(key, np->key) == 0) {
TAILQ_REMOVE(map, np, entries);
free(np->key);
free(np->value);
free(np);
return;
}
}
/* delete the map of kv */
void delete_map(map_t *map) {
kv_t *np;
TAILQ_FOREACH(np, map, entries) {
TAILQ_REMOVE(map, np, entries);
free(np->key);
free(np->value);
free(np);
}
}
/* update the map (merge) */
void update_map(map_t *map, cbor_item_t *cmap) {
struct cbor_pair *pairs;
size_t len, i, sz;
char *key, *value;
if ( !cmap || !cbor_isa_map(cmap) )
return;
pairs = cbor_map_handle(cmap);
sz = cbor_map_size(cmap);
for (i = 0; i < sz; i++) {
key = xAAL_cbor_string_dup(pairs[i].key, &len);
if ( key ) {
if ( cbor_is_null(pairs[i].value) )
delete_kv(map, key);
else {
value = xAAL_cbor_string_dup(pairs[i].value, &len);
insert_kv(map, key, value);
free(value);
}
free(key);
}
}
}