Add ability to manage all widget/applications
[src/app-framework-main.git] / src / afm-udb.c
index e199ae1..d804704 100644 (file)
@@ -1,5 +1,5 @@
 /*
- Copyright 2015, 2016, 2017 IoT.bzh
+ Copyright (C) 2015-2019 IoT.bzh
 
  author: José Bollo <jose.bollo@iot.bzh>
 
@@ -17,6 +17,7 @@
 */
 
 #include <stdlib.h>
+#include <stdio.h>
 #include <assert.h>
 #include <string.h>
 #include <errno.h>
@@ -33,7 +34,6 @@
 
 #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";
@@ -42,6 +42,8 @@ static const char key_unit_scope[] = "-unit-scope";
 static const char scope_user[] = "user";
 static const char scope_system[] = "system";
 static const char key_id[] = "id";
+static const char key_visibility[] = "visibility";
+static const char value_visible[] = "visible";
 
 #define x_afm_prefix_length  (sizeof x_afm_prefix - 1)
 #define service_extension_length  (sizeof service_extension - 1)
@@ -51,10 +53,11 @@ static const char key_id[] = "id";
  * for several accesses.
  */
 struct afm_apps {
-       struct json_object *prvarr; /* array of the private data of apps */
-       struct json_object *pubarr; /* array of the public data of apps */
-       struct json_object *pubobj; /* hash of application's publics */
-       struct json_object *prvobj; /* hash of application's privates */
+       struct {
+               struct json_object *visibles; /* array of the private data of visible apps */
+               struct json_object *all; /* array of the private data of all apps */
+               struct json_object *byname; /* hash of application's privates */
+       } privates, publics;
 };
 
 /*
@@ -78,15 +81,44 @@ struct afm_updt {
        struct afm_apps applications;
 };
 
+/*
+ * The default language
+ */
+static char *default_lang;
+
+/*
+ * initilize object 'apps'.
+ * returns 1 if okay or 0 on case of memory depletion
+ */
+static int apps_init(struct afm_apps *apps)
+{
+       apps->publics.all = json_object_new_array();
+       apps->publics.visibles = json_object_new_array();
+       apps->publics.byname = json_object_new_object();
+
+       apps->privates.all = json_object_new_array();
+       apps->privates.visibles = json_object_new_array();
+       apps->privates.byname = json_object_new_object();
+
+       return apps->publics.all
+          && apps->publics.visibles
+          && apps->publics.byname
+          && apps->privates.all
+          && apps->privates.visibles
+          && apps->privates.byname;
+}
+
 /*
  * Release the data of the afm_apps object 'apps'.
  */
 static void apps_put(struct afm_apps *apps)
 {
-       json_object_put(apps->prvarr);
-       json_object_put(apps->pubarr);
-       json_object_put(apps->pubobj);
-       json_object_put(apps->prvobj);
+       json_object_put(apps->publics.all);
+       json_object_put(apps->publics.visibles);
+       json_object_put(apps->publics.byname);
+       json_object_put(apps->privates.all);
+       json_object_put(apps->privates.visibles);
+       json_object_put(apps->privates.byname);
 }
 
 /*
@@ -112,7 +144,7 @@ static int append_field(
                        array = json_object_new_array();
                        if (!array)
                                goto error;
-                       json_object_array_add(array, item);
+                       json_object_array_add(array, json_object_get(item));
                        json_object_object_add(object, name, array);
                }
                json_object_array_add(array, data);
@@ -156,8 +188,10 @@ static int add_field(
 
        /* add the value */
        if (name[0] == '-') {
+               /* private value */
                append_field(priv, &name[1], v);
        } else {
+               /* public value */
                append_field(priv, name, json_object_get(v));
                append_field(pub, name, v);
        }
@@ -177,17 +211,25 @@ static int add_fields_of_content(
 {
        char *name, *value, *read, *write;
 
-       read = strstr(content, x_afm_prefix);
-       while (read) {
+       /* start at the beginning */
+       read = content;
+       for (;;) {
+               /* search the next key */
+               read = strstr(read, x_afm_prefix);
+               if (!read)
+                       return 0;
+
+               /* search to equal */
                name = read + x_afm_prefix_length;
                value = strchr(name, '=');
                if (value == NULL)
-                       read = strstr(name, x_afm_prefix);
+                       read = name; /* not found */
                else {
+                       /* get the value (translate it) */
                        *value++ = 0;
                        read = write = value;
                        while(*read && *read != '\n') {
-                               if (read[0] != '\\')
+                               if (*read != '\\')
                                        *write++ = *read++;
                                else {
                                        switch(*++read) {
@@ -198,13 +240,14 @@ static int add_fields_of_content(
                                        read += !!*read;
                                }
                        }
-                       read = strstr(read, x_afm_prefix);
+                       read += !!*read;
                        *write = 0;
+
+                       /* add the found field now */
                        if (add_field(priv, pub, name, value) < 0)
                                return -1;
                }
        }
-       return 0;
 }
 
 /*
@@ -222,8 +265,9 @@ static int addunit(
                size_t length
 )
 {
-       struct json_object *priv, *pub, *id;
+       struct json_object *priv, *pub, *id, *visi;
        const char *strid;
+       size_t len;
 
        /* create the application structure */
        priv = json_object_new_object();
@@ -234,6 +278,11 @@ static int addunit(
        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)
@@ -250,11 +299,19 @@ static int addunit(
 
        /* record the application structure */
        json_object_get(pub);
-       json_object_array_add(apps->pubarr, pub);
-       json_object_object_add(apps->pubobj, strid, pub);
+       json_object_array_add(apps->publics.all, pub);
+       json_object_object_add(apps->publics.byname, strid, pub);
        json_object_get(priv);
-       json_object_array_add(apps->prvarr, priv);
-       json_object_object_add(apps->prvobj, strid, priv);
+       json_object_array_add(apps->privates.all, priv);
+       json_object_object_add(apps->privates.byname, strid, priv);
+
+       /* handle visibility */
+       if (json_object_object_get_ex(priv, key_visibility, &visi)
+               && !strcasecmp(json_object_get_string(visi), value_visible)) {
+               json_object_array_add(apps->publics.visibles, json_object_get(pub));
+               json_object_array_add(apps->privates.visibles, json_object_get(priv));
+       }
+
        return 0;
 
 error:
@@ -263,6 +320,81 @@ error:
        return -1;
 }
 
+/*
+ * Crop and trim unit 'content' of 'length'. Return the new length.
+ */
+static size_t crop_and_trim_unit_content(char *content, size_t length)
+{
+       int st;
+       char c, *read, *write;
+
+       /* 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;
+       return (size_t)(write - content);
+}
+
+/*
+ * read a unit file
+ */
+static int read_unit_file(const char *path, char **content, size_t *length)
+{
+       int rc;
+       size_t nl;
+
+       /* read the file */
+       rc = getfile(path, content, length);
+       if (rc >= 0) {
+               /* crop and trim it */
+               *length = nl = crop_and_trim_unit_content(*content, *length);
+               *content = realloc(*content, nl + 1);
+       }
+       return rc;
+}
+
 /*
  * called for each unit
  */
@@ -284,14 +416,16 @@ static int update_cb(void *closure, const char *name, const char *path, int isus
                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;
 }
 
 /*
@@ -309,10 +443,7 @@ struct afm_udb *afm_udb_create(int sys, int usr, const char *prefix)
                errno = ENOMEM;
        else {
                afudb->refcount = 1;
-               afudb->applications.prvarr = NULL;
-               afudb->applications.pubarr = NULL;
-               afudb->applications.pubobj = NULL;
-               afudb->applications.prvobj = NULL;
+               memset(&afudb->applications, 0, sizeof afudb->applications);
                afudb->system = sys;
                afudb->user = usr;
                afudb->prefixlen = length;
@@ -359,43 +490,43 @@ int afm_udb_update(struct afm_udb *afudb)
 {
        struct afm_updt updt;
        struct afm_apps tmp;
+       int result;
 
        /* lock the db */
        afm_udb_addref(afudb);
        updt.afudb = afudb;
 
-       /* create the result */
-       updt.applications.prvarr = json_object_new_array();
-       updt.applications.pubarr = json_object_new_array();
-       updt.applications.pubobj = json_object_new_object();
-       updt.applications.prvobj = json_object_new_object();
-       if (updt.applications.pubarr == NULL
-        || updt.applications.prvarr == NULL
-        || updt.applications.pubobj == NULL
-        || updt.applications.prvobj == NULL) {
-               errno = ENOMEM;
-               goto error;
+       /* create the apps */
+       if (!apps_init(&updt.applications))
+               result = -1;
+       else {
+               /* scan the units */
+               if (afudb->user && systemd_unit_list(1, update_cb, &updt) < 0)
+                       result = -1;
+               else if (afudb->system && systemd_unit_list(0, update_cb, &updt) < 0)
+                       result = -1;
+               else {
+                       /* commit the result */
+                       tmp = afudb->applications;
+                       afudb->applications = updt.applications;
+                       updt.applications = tmp;
+                       result = 0;
+               }
+               apps_put(&updt.applications);
        }
+       /* unlock the db and return status */
+       afm_udb_unref(afudb);
+       return result;
+}
 
-       /* scan the units */
-       if (afudb->user)
-               if (systemd_unit_list(1, update_cb, &updt) < 0)
-                       goto error;
-       if (afudb->system)
-               if (systemd_unit_list(0, update_cb, &updt) < 0)
-                       goto error;
-
-       /* commit the result */
-       tmp = afudb->applications;
-       afudb->applications = updt.applications;
-       apps_put(&tmp);
-       afm_udb_addref(afudb);
-       return 0;
-
-error:
-       apps_put(&updt.applications);
-       afm_udb_addref(afudb);
-       return -1;
+/*
+ * set the default language to 'lang'
+ */
+void afm_udb_set_default_lang(const char *lang)
+{
+       char *oldval = default_lang;
+       default_lang = lang ? strdup(lang) : NULL;
+       free(oldval);
 }
 
 /*
@@ -404,9 +535,9 @@ error:
  * '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 all, int uid)
 {
-       return json_object_get(afudb->applications.prvarr);
+       return json_object_get(all ? afudb->applications.privates.all : afudb->applications.privates.visibles);
 }
 
 /*
@@ -415,9 +546,9 @@ struct json_object *afm_udb_applications_private(struct afm_udb *afudb)
  * '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 all, int uid, const char *lang)
 {
-       return json_object_get(afudb->applications.pubarr);
+       return json_object_get(all ? afudb->applications.publics.all : afudb->applications.publics.visibles);
 }
 
 /*
@@ -425,7 +556,7 @@ struct json_object *afm_udb_applications_public(struct afm_udb *afudb)
  * 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;
@@ -447,9 +578,9 @@ static struct json_object *get_no_case(struct json_object *object, const char *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.privates.byname, id, uid, NULL);
 }
 
 /*
@@ -458,9 +589,9 @@ struct json_object *afm_udb_get_application_private(struct afm_udb *afudb, const
  * 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.publics.byname, id, uid, lang);
 }
 
 
@@ -470,9 +601,9 @@ struct json_object *afm_udb_get_application_public(struct afm_udb *afudb,
 int main()
 {
 struct afm_udb *afudb = afm_udb_create(1, 1, NULL);
-printf("array = %s\n", json_object_to_json_string_ext(afudb->applications.pubarr, 3));
-printf("pubobj = %s\n", json_object_to_json_string_ext(afudb->applications.pubobj, 3));
-printf("prvobj = %s\n", json_object_to_json_string_ext(afudb->applications.prvobj, 3));
+printf("publics.all = %s\n", json_object_to_json_string_ext(afudb->applications.publics.all, 3));
+printf("publics.byname = %s\n", json_object_to_json_string_ext(afudb->applications.publics.byname, 3));
+printf("privates.byname = %s\n", json_object_to_json_string_ext(afudb->applications.privates.byname, 3));
 return 0;
 }
 #endif