/*
- Copyright 2015, 2016, 2017 IoT.bzh
+ Copyright (C) 2015-2018 IoT.bzh
author: José Bollo <jose.bollo@iot.bzh>
*/
#include <stdlib.h>
+#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include "afm-udb.h"
-
static const char x_afm_prefix[] = "X-AFM-";
static const char service_extension[] = ".service";
static const char key_unit_path[] = "-unit-path";
struct afm_apps applications;
};
+/*
+ * The default language
+ */
+static char *default_lang;
+
/*
* Release the data of the afm_apps object 'apps'.
*/
json_object_put(apps->prvobj);
}
+/*
+ * Append the field 'data' to the field 'name' of the 'object'.
+ * When a second append is done to one field, it is automatically
+ * transformed to an array.
+ * Return 0 in case of success or -1 in case of error.
+ */
+static int append_field(
+ struct json_object *object,
+ const char *name,
+ struct json_object *data
+)
+{
+ struct json_object *item, *array;
+
+ if (!json_object_object_get_ex(object, name, &item))
+ json_object_object_add(object, name, data);
+ else {
+ if (json_object_is_type(item, json_type_array))
+ array = item;
+ else {
+ array = json_object_new_array();
+ if (!array)
+ goto error;
+ json_object_array_add(array, json_object_get(item));
+ json_object_object_add(object, name, array);
+ }
+ json_object_array_add(array, data);
+ }
+ return 0;
+ error:
+ json_object_put(data);
+ errno = ENOMEM;
+ return -1;
+}
+
/*
* Adds the field of 'name' and 'value' in 'priv' and also if possible in 'pub'
* Returns 0 on success or -1 on error.
/* add the value */
if (name[0] == '-') {
- json_object_object_add(priv, &name[1], v);
+ append_field(priv, &name[1], v);
} else {
- json_object_object_add(priv, name, json_object_get(v));
- json_object_object_add(pub, name, v);
+ append_field(priv, name, json_object_get(v));
+ append_field(pub, name, v);
}
return 0;
}
{
struct json_object *priv, *pub, *id;
const char *strid;
+ size_t len;
/* create the application structure */
priv = json_object_new_object();
if (!pub)
goto error;
+ /* make the unit name */
+ len = strlen(unitname);
+ assert(len >= (sizeof service_extension - 1));
+ assert(!memcmp(&unitname[len - (sizeof service_extension - 1)], service_extension, sizeof service_extension));
+
/* adds the values */
if (add_fields_of_content(priv, pub, content, length)
|| add_field(priv, pub, key_unit_path, unitpath)
return -1;
}
+/*
+ * read a unit file
+ */
+static int read_unit_file(const char *path, char **content, size_t *length)
+{
+ int rc, st;
+ char c, *read, *write;
+
+ /* read the file */
+ rc = getfile(path, content, length);
+ if (rc >= 0) {
+ /* removes any comment and join continued lines */
+ st = 0;
+ read = write = *content;
+ for (;;) {
+ do { c = *read++; } while (c == '\r');
+ if (!c)
+ break;
+ switch (st) {
+ case 0:
+ /* state 0: begin of a line */
+ if (c == ';' || c == '#') {
+ st = 3; /* removes lines starting with ; or # */
+ break;
+ }
+ if (c == '\n')
+ break; /* removes empty lines */
+enter_state_1:
+ st = 1;
+ /*@fallthrough@*/
+ case 1:
+ /* state 1: emitting a normal line */
+ if (c == '\\')
+ st = 2;
+ else {
+ *write++ = c;
+ if (c == '\n')
+ st = 0;
+ }
+ break;
+ case 2:
+ /* state 2: character after '\' */
+ if (c == '\n')
+ c = ' ';
+ else
+ *write++ = '\\';
+ goto enter_state_1;
+ case 3:
+ /* state 3: inside a comment, wait its end */
+ if (c == '\n')
+ st = 0;
+ break;
+ }
+ }
+ if (st == 1)
+ *write++ = '\n';
+ *write = 0;
+ *length = (size_t)(write - *content);
+ *content = realloc(*content, *length + 1);
+ }
+ return rc;
+}
+
/*
* called for each unit
*/
return 0;
/* reads the file */
- rc = getfile(path, &content, &length);
+ rc = read_unit_file(path, &content, &length);
if (rc < 0)
- return rc;
+ return 0;
/* process the file */
rc = addunit(&updt->applications, isuser, path, name, content, length);
+ /* TODO: if (rc < 0)
+ ERROR("Ignored boggus unit %s (error: %m)", path); */
free(content);
- return rc;
+ return 0;
}
/*
tmp = afudb->applications;
afudb->applications = updt.applications;
apps_put(&tmp);
- afm_udb_addref(afudb);
+ afm_udb_unref(afudb);
return 0;
error:
apps_put(&updt.applications);
- afm_udb_addref(afudb);
+ afm_udb_unref(afudb);
return -1;
}
+void afm_udb_set_default_lang(const char *lang)
+{
+ char *oldval = default_lang;
+ default_lang = lang ? strdup(lang) : NULL;
+ free(oldval);
+}
+
/*
* Get the list of the applications private data of the afm_udb object 'afudb'.
* The list is returned as a JSON-array that must be released using
* 'json_object_put'.
* Returns NULL in case of error.
*/
-struct json_object *afm_udb_applications_private(struct afm_udb *afudb)
+struct json_object *afm_udb_applications_private(struct afm_udb *afudb, int uid)
{
return json_object_get(afudb->applications.prvarr);
}
* 'json_object_put'.
* Returns NULL in case of error.
*/
-struct json_object *afm_udb_applications_public(struct afm_udb *afudb)
+struct json_object *afm_udb_applications_public(struct afm_udb *afudb, int uid, const char *lang)
{
return json_object_get(afudb->applications.pubarr);
}
* It returns a JSON-object that must be released using 'json_object_put'.
* Returns NULL in case of error.
*/
-static struct json_object *get_no_case(struct json_object *object, const char *id)
+static struct json_object *get_no_case(struct json_object *object, const char *id, int uid, const char *lang)
{
struct json_object *result;
struct json_object_iter i;
* It returns a JSON-object that must be released using 'json_object_put'.
* Returns NULL in case of error.
*/
-struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id)
+struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const char *id, int uid)
{
- return get_no_case(afudb->applications.prvobj, id);
+ return get_no_case(afudb->applications.prvobj, id, uid, NULL);
}
/*
* Returns NULL in case of error.
*/
struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
- const char *id)
+ const char *id, int uid, const char *lang)
{
- return get_no_case(afudb->applications.pubobj, id);
+ return get_no_case(afudb->applications.pubobj, id, uid, lang);
}