Adds 2017 to copyrights
[src/app-framework-main.git] / src / afm-db.c
index 8f52d1c..638713e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- Copyright 2015 IoT.bzh
+ Copyright 2015, 2016, 2017 IoT.bzh
 
  author: José Bollo <jose.bollo@iot.bzh>
 
 #include <unistd.h>
 #include <sys/types.h>
 
-#include <json.h>
+#include <json-c/json.h>
 
+#include "utils-json.h"
 #include "wgt-info.h"
 #include "afm-db.h"
 
-struct afapps {
-       struct json_object *pubarr;
-       struct json_object *direct;
-       struct json_object *byapp;
+/*
+ * The json object recorded by application widget
+ * has the following contents:
+ *  {
+ *    id: STRING, the application identifier without version info
+ *    path: STRING, the path of the root directory for the application
+ *    content: STRING, the relative path to the entryu point of the application
+ *    type: STRING, the mime type describing the type 'content'
+ *    bindings: ARRAY, array of bindings
+ *      [
+ *        STRING, path to the binding
+ *      ]
+ *    public: OBJECT, public content describing the application widget
+ *      {  
+ *        id: STRING, the application identifier "idaver"
+ *        version: STRING, the full version of the application
+ *        width: INTEGER, the width indication or 0
+ *        height: INTEGER, the height indication or 0
+ *        name: STRING, the name of the application
+ *        description: STRING, the description of the application
+ *        shortname: STRING, the short name of the application
+ *        author: STRING, the author of the application
+ *      }
+ */
+
+/*
+ * The structure afm_apps records the data about applications
+ * for several accesses.
+ */
+struct afm_apps {
+       struct json_object *pubarr; /* array of the public data of apps */
+       struct json_object *direct; /* hash of applications by their "idaver" */
+       struct json_object *byapp;  /* hash of applications by their id */
 };
 
+/*
+ * Two types of directory are handled:
+ *  - root directories: contains subdirectories appid/version
+ *         containing the applications
+ *  - application directories: it contains an application
+ */
 enum dir_type {
-       type_root,
-       type_app
+       type_root, /* type for root directory */
+       type_app   /* type for application directory */
 };
 
+/*
+ * Structure for recording a path to application(s)
+ * in the list of directories.
+ */
 struct afm_db_dir {
-       struct afm_db_dir *next;
-       char *path;
-       enum dir_type type;
+       struct afm_db_dir *next; /* link to the next item of the list */
+       enum dir_type type;      /* the type of the path */
+       char path[1];            /* the path of the directory */
 };
 
+/*
+ * The structure afm_db records the applications
+ * for a set of directories recorded as a linked list
+ */
 struct afm_db {
-       int refcount;
-       struct afm_db_dir *dirhead;
-       struct afm_db_dir *dirtail;
-       struct afapps applications;
+       int refcount;               /* count of references to the structure */
+       struct afm_db_dir *dirhead; /* first directory of the set */
+       struct afm_db_dir *dirtail; /* last directory of the set */
+       struct afm_apps applications; /* the data about applications */
 };
 
-struct afm_db *afm_db_create()
-{
-       struct afm_db *afdb = malloc(sizeof * afdb);
-       if (afdb == NULL)
-               errno = ENOMEM;
-       else {
-               afdb->refcount = 1;
-               afdb->dirhead = NULL;
-               afdb->dirtail = NULL;
-               afdb->applications.pubarr = NULL;
-               afdb->applications.direct = NULL;
-               afdb->applications.byapp = NULL;
-       }
-       return afdb;
-}
-
-void afm_db_addref(struct afm_db *afdb)
-{
-       assert(afdb);
-       afdb->refcount++;
-}
-
-void afm_db_unref(struct afm_db *afdb)
-{
-       struct afm_db_dir *dir;
-       assert(afdb);
-       if (!--afdb->refcount) {
-               json_object_put(afdb->applications.pubarr);
-               json_object_put(afdb->applications.direct);
-               json_object_put(afdb->applications.byapp);
-               while (afdb->dirhead != NULL) {
-                       dir = afdb->dirhead;
-                       afdb->dirhead = dir->next;
-                       free(dir->path);
-                       free(dir);
-               }
-               free(afdb);
-       }
-}
-
-int add_dir(struct afm_db *afdb, const char *path, enum dir_type type)
-{
-       struct afm_db_dir *dir;
-       char *r;
-
-       assert(afdb);
-
-       /* don't depend on the cwd and unique name */
-       r = realpath(path, NULL);
-       if (!r)
-               return -1;
-
-       /* avoiding duplications */
-       dir = afdb->dirhead;
-       while(dir != NULL && (strcmp(dir->path, path) || dir->type != type))
-               dir = dir ->next;
-       if (dir != NULL) {
-               free(r);
-               return 0;
-       }
-
-       /* allocates the structure */
-       dir = malloc(sizeof * dir);
-       if (dir == NULL) {
-               free(r);
-               errno = ENOMEM;
-               return -1;
-       }
-
-       /* add */
-       dir->next = NULL;
-       dir->path = r;
-       dir->type = type;
-       if (afdb->dirtail == NULL)
-               afdb->dirhead = dir;
-       else
-               afdb->dirtail->next = dir;
-       afdb->dirtail = dir;
-       return 0;
-}
-
-int afm_db_add_root(struct afm_db *afdb, const char *path)
-{
-       return add_dir(afdb, path, type_root);
-}
-
-int afm_db_add_application(struct afm_db *afdb, const char *path)
-{
-       return add_dir(afdb, path, type_app);
-}
-
-static int json_add(struct json_object *obj, const char *key, struct json_object *val)
-{
-       json_object_object_add(obj, key, val);
-       return 0;
-}
-
-static int json_add_str(struct json_object *obj, const char *key, const char *val)
-{
-       struct json_object *str = json_object_new_string (val ? val : "");
-       return str ? json_add(obj, key, str) : -1;
-}
+/*
+ * The structure enumdata records data used when enumerating
+ * application directories of a root directory.
+ */
+struct enumdata {
+       char path[PATH_MAX];   /* "current" computed path */
+       int length;            /* length of path */
+       struct afm_apps apps;  /* */
+};
 
-static int json_add_int(struct json_object *obj, const char *key, int val)
+/*
+ * Release the data of the afm_apps object 'apps'.
+ */
+static void apps_put(struct afm_apps *apps)
 {
-       struct json_object *v = json_object_new_int (val);
-       return v ? json_add(obj, key, v) : -1;
+       json_object_put(apps->pubarr);
+       json_object_put(apps->direct);
+       json_object_put(apps->byapp);
 }
 
-static int addapp(struct afapps *apps, const char *path)
+/*
+ * Adds the application widget 'desc' of the directory 'path' to the
+ * afm_apps object 'apps'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ */
+static int addwgt(struct afm_apps *apps, const char *path,
+                                               const struct wgt_desc *desc)
 {
-       struct wgt_info *info;
-       const struct wgt_desc *desc;
        const struct wgt_desc_feature *feat;
-       struct json_object *priv = NULL, *pub, *bya, *plugs, *str;
-
-       /* connect to the widget */
-       info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
-       if (info == NULL) {
-               if (errno == ENOENT)
-                       return 0; /* silently ignore bad directories */
-               goto error;
-       }
-       desc = wgt_info_desc(info);
+       struct json_object *priv, *pub, *bya, *plugs, *str;
 
        /* create the application structure */
        priv = json_object_new_object();
        if (!priv)
-               goto error2;
-
-       pub = json_object_new_object();
-       if (!priv)
-               goto error2;
-
-       if (json_add(priv, "public", pub)) {
-               json_object_put(pub);
-               goto error2;
-       }
+               return -1;
 
-       plugs = json_object_new_array();
-       if (!priv)
-               goto error2;
+       pub = j_add_new_object(priv, "public");
+       if (!pub)
+               goto error;
 
-       if (json_add(priv, "plugins", plugs)) {
-               json_object_put(plugs);
-               goto error2;
-       }
+       plugs = j_add_new_array(priv, "bindings");
+       if (!plugs)
+               goto error;
 
-       if(json_add_str(priv, "id", desc->id)
-       || json_add_str(priv, "path", path)
-       || json_add_str(priv, "content", desc->content_src)
-       || json_add_str(priv, "type", desc->content_type)
-       || json_add_str(pub, "id", desc->idaver)
-       || json_add_str(pub, "version", desc->version)
-       || json_add_int(pub, "width", desc->width)
-       || json_add_int(pub, "height", desc->height)
-       || json_add_str(pub, "name", desc->name)
-       || json_add_str(pub, "description", desc->description)
-       || json_add_str(pub, "shortname", desc->name_short)
-       || json_add_str(pub, "author", desc->author))
-               goto error2;
+       if(!j_add_string(priv, "id", desc->id)
+       || !j_add_string(priv, "path", path)
+       || !j_add_string(priv, "content", desc->content_src)
+       || !j_add_string(priv, "type", desc->content_type)
+       || !j_add_string(pub, "id", desc->idaver)
+       || !j_add_string(pub, "version", desc->version)
+       || !j_add_integer(pub, "width", desc->width)
+       || !j_add_integer(pub, "height", desc->height)
+       || !j_add_string(pub, "name", desc->name)
+       || !j_add_string(pub, "description", desc->description)
+       || !j_add_string(pub, "shortname", desc->name_short)
+       || !j_add_string(pub, "author", desc->author))
+               goto error;
 
+       /* extract bindings from features */
        feat = desc->features;
        while (feat) {
-               static const char prefix[] = FWK_PREFIX_PLUGIN;
+               static const char prefix[] = FWK_PREFIX_BINDING;
                if (!memcmp(feat->name, prefix, sizeof prefix - 1)) {
-                       str = json_object_new_string (feat->name + sizeof prefix - 1);
+                       str = json_object_new_string (
+                                       feat->name + sizeof prefix - 1);
                        if (str == NULL)
-                               goto error2;
+                               goto error;
                        if (json_object_array_add(plugs, str)) {
                                json_object_put(str);
-                               goto error2;
+                               goto error;
                        }
                }
                feat = feat->next;
        }
 
        /* record the application structure */
+       if (!j_add(apps->direct, desc->idaver, priv))
+               goto error;
+
+       if (json_object_array_add(apps->pubarr, pub))
+               goto error;
+       json_object_get(pub);
+
        if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) {
-               bya = json_object_new_object();
+               bya = j_add_new_object(apps->byapp, desc->id);
                if (!bya)
-                       goto error2;
-               if (json_add(apps->byapp, desc->id, bya)) {
-                       json_object_put(bya);
-                       goto error2;
-               }
+                       goto error;
        }
 
-       if (json_add(apps->direct, desc->idaver, priv))
-               goto error2;
+       if (!j_add(bya, desc->version, priv))
+               goto error;
        json_object_get(priv);
-
-       if (json_add(bya, desc->version, priv)) {
-               json_object_put(priv);
-               goto error2;
-       }
-
-       if (json_object_array_add(apps->pubarr, pub))
-               goto error2;
-
-       wgt_info_unref(info);
        return 0;
 
-error2:
-       json_object_put(priv);
-       wgt_info_unref(info);
 error:
+       json_object_put(priv);
        return -1;
 }
 
-struct enumdata {
-       char path[PATH_MAX];
-       int length;
-       struct afapps apps;
-};
+/*
+ * Adds the application widget in the directory 'path' to the
+ * afm_apps object 'apps'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ */
+static int addapp(struct afm_apps *apps, const char *path)
+{
+       int rc;
+       struct wgt_info *info;
 
+       /* connect to the widget */
+       info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
+       if (info == NULL) {
+               if (errno == ENOENT)
+                       return 0; /* silently ignore bad directories */
+               return -1;
+       }
+       /* adds the widget */
+       rc = addwgt(apps, path, wgt_info_desc(info));
+       wgt_info_unref(info);
+       return rc;
+}
+
+/*
+ * Enumerate the directories designated by 'data' and call the
+ * function 'callto' for each of them.
+ */
 static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
 {
        DIR *dir;
        int rc;
-       char *beg, *end;
+       char *beg;
        struct dirent entry, *e;
+       size_t len;
 
        /* opens the directory */
        dir = opendir(data->path);
@@ -289,10 +246,16 @@ static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
        /* enumerate entries */
        rc = readdir_r(dir, &entry, &e);
        while (!rc && e) {
-               if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) {
+               if (entry.d_name[0] != '.' || (entry.d_name[1]
+                       && (entry.d_name[1] != '.' || entry.d_name[2]))) {
                        /* prepare callto */
-                       end = stpcpy(beg, entry.d_name);
-                       data->length = end - data->path;
+                       len = strlen(entry.d_name);
+                       if (beg + len >= data->path + sizeof data->path) {
+                               errno = ENAMETOOLONG;
+                               return -1;
+                       }
+                       data->length = (int)(stpcpy(beg, entry.d_name)
+                                                               - data->path);
                        /* call the function */
                        rc = callto(data);
                        if (rc)
@@ -304,39 +267,173 @@ static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
        return rc;
 }
 
+/*
+ * called for each version directory.
+ */
 static int recordapp(struct enumdata *data)
 {
        return addapp(&data->apps, data->path);
 }
 
-/* enumerate the versions */
+/*
+ * called for each application directory.
+ * enumerate directories of the existing versions.
+ */
 static int enumvers(struct enumdata *data)
 {
        int rc = enumentries(data, recordapp);
        return !rc || errno != ENOTDIR ? 0 : rc;
 }
 
-/* regenerate the list of applications */
+/*
+ * Adds the directory of 'path' and 'type' to the afm_db object 'afdb'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ * Possible errno values: ENOMEM, ENAMETOOLONG
+ */
+static int add_dir(struct afm_db *afdb, const char *path, enum dir_type type)
+{
+       struct afm_db_dir *dir;
+       size_t len;
+
+       assert(afdb);
+
+       /* check size */
+       len = strlen(path);
+       if (len >= PATH_MAX) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       /* avoiding duplications */
+       dir = afdb->dirhead;
+       while(dir != NULL && (strcmp(dir->path, path) || dir->type != type))
+               dir = dir ->next;
+       if (dir != NULL)
+               return 0;
+
+       /* allocates the structure */
+       dir = malloc(strlen(path) + sizeof * dir);
+       if (dir == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
+
+       /* add it at tail */
+       dir->next = NULL;
+       dir->type = type;
+       strcpy(dir->path, path);
+       if (afdb->dirtail == NULL)
+               afdb->dirhead = dir;
+       else
+               afdb->dirtail->next = dir;
+       afdb->dirtail = dir;
+       return 0;
+}
+
+/*
+ * Creates an afm_db object and returns it with one reference added.
+ * Return NULL with errno = ENOMEM if memory exhausted.
+ */
+struct afm_db *afm_db_create()
+{
+       struct afm_db *afdb = malloc(sizeof * afdb);
+       if (afdb == NULL)
+               errno = ENOMEM;
+       else {
+               afdb->refcount = 1;
+               afdb->dirhead = NULL;
+               afdb->dirtail = NULL;
+               afdb->applications.pubarr = NULL;
+               afdb->applications.direct = NULL;
+               afdb->applications.byapp = NULL;
+       }
+       return afdb;
+}
+
+/*
+ * Adds a reference to an existing afm_db.
+ */
+void afm_db_addref(struct afm_db *afdb)
+{
+       assert(afdb);
+       afdb->refcount++;
+}
+
+/*
+ * Removes a reference to an existing afm_db object.
+ * Removes the objet if there no more reference to it.
+ */
+void afm_db_unref(struct afm_db *afdb)
+{
+       struct afm_db_dir *dir;
+
+       assert(afdb);
+       if (!--afdb->refcount) {
+               /* no more reference, clean the memory used by the object */
+               apps_put(&afdb->applications);
+               while (afdb->dirhead != NULL) {
+                       dir = afdb->dirhead;
+                       afdb->dirhead = dir->next;
+                       free(dir);
+               }
+               free(afdb);
+       }
+}
+
+/*
+ * Adds the root directory of 'path' to the afm_db object 'afdb'.
+ * Be aware that no check is done on the directory of 'path' that will
+ * only be used within calls to the function 'afm_db_update_applications'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ * Possible errno values: ENOMEM, ENAMETOOLONG
+ */
+int afm_db_add_root(struct afm_db *afdb, const char *path)
+{
+       return add_dir(afdb, path, type_root);
+}
+
+/*
+ * Adds the application directory of 'path' to the afm_db object 'afdb'.
+ * Be aware that no check is done on the directory of 'path' that will
+ * only be used within calls to the function 'afm_db_update_applications'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ * Possible errno values: ENOMEM, ENAMETOOLONG
+ */
+int afm_db_add_application(struct afm_db *afdb, const char *path)
+{
+       return add_dir(afdb, path, type_app);
+}
+
+/*
+ * Regenerate the list of applications of the afm_bd object 'afdb'.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ */
 int afm_db_update_applications(struct afm_db *afdb)
 {
        int rc;
        struct enumdata edata;
-       struct afapps oldapps;
+       struct afm_apps oldapps;
        struct afm_db_dir *dir;
 
        /* create the result */
        edata.apps.pubarr = json_object_new_array();
        edata.apps.direct = json_object_new_object();
        edata.apps.byapp = json_object_new_object();
-       if (edata.apps.pubarr == NULL || edata.apps.direct == NULL || edata.apps.byapp == NULL) {
+       if (edata.apps.pubarr == NULL || edata.apps.direct == NULL
+                                               || edata.apps.byapp == NULL) {
                errno = ENOMEM;
                goto error;
        }
-       /* for each root */
+       /* for each directory of afdb */
        for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) {
                if (dir->type == type_root) {
-                       edata.length = stpcpy(edata.path, dir->path) - edata.path;
-                       assert(edata.length < sizeof edata.path);
+                       edata.length = (int)(stpcpy(edata.path, dir->path)
+                                                               - edata.path);
+                       assert(edata.length < (int)sizeof edata.path);
                        /* enumerate the applications */
                        rc = enumentries(&edata, enumvers);
                        if (rc)
@@ -348,40 +445,89 @@ int afm_db_update_applications(struct afm_db *afdb)
        /* commit the result */
        oldapps = afdb->applications;
        afdb->applications = edata.apps;
-       json_object_put(oldapps.pubarr);
-       json_object_put(oldapps.direct);
-       json_object_put(oldapps.byapp);
+       apps_put(&oldapps);
        return 0;
 
 error:
-       json_object_put(edata.apps.pubarr);
-       json_object_put(edata.apps.direct);
-       json_object_put(edata.apps.byapp);
+       apps_put(&edata.apps);
        return -1;
 }
 
+/*
+ * Ensure that applications of the afm_bd object 'afdb' are listed.
+ * Returns 0 in case of success.
+ * Returns -1 and set errno in case of error
+ */
 int afm_db_ensure_applications(struct afm_db *afdb)
 {
        return afdb->applications.pubarr ? 0 : afm_db_update_applications(afdb);
 }
 
+/*
+ * Get the list of the applications public data of the afm_db object 'afdb'.
+ * 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_db_application_list(struct afm_db *afdb)
 {
-       return afm_db_ensure_applications(afdb) ? NULL : json_object_get(afdb->applications.pubarr);
+       return afm_db_ensure_applications(afdb) ? NULL
+                       : json_object_get(afdb->applications.pubarr);
 }
 
+/*
+ * Get the private data of the applications of 'id' in the afm_db object 'afdb'.
+ * It returns a JSON-object that must be released using 'json_object_put'.
+ * Returns NULL in case of error.
+ */
 struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id)
 {
+       int i;
        struct json_object *result;
-       if (!afm_db_ensure_applications(afdb) && json_object_object_get_ex(afdb->applications.direct, id, &result))
+
+       if (afm_db_ensure_applications(afdb))
+               return NULL;
+
+       /* search case sensitively */
+       if (json_object_object_get_ex( afdb->applications.direct, id, &result))
                return json_object_get(result);
+
+       /* fallback to a case insensitive search */
+       i = json_object_array_length(afdb->applications.pubarr);
+       while (i) {
+               result = json_object_array_get_idx(afdb->applications.pubarr, --i);
+               if (result
+                 && json_object_object_get_ex(result, "id", &result)
+                 && !strcasecmp(id, json_object_get_string(result))) {
+                       if (json_object_object_get_ex( afdb->applications.direct, 
+                                                      json_object_get_string(result),
+                                                      &result))
+                               return json_object_get(result);
+                       else
+                               return NULL;
+               }
+       }
        return NULL;
 }
 
-struct json_object *afm_db_get_application_public(struct afm_db *afdb, const char *id)
+/*
+ * Get the public data of the applications of 'id' in the afm_db object 'afdb'.
+ * It returns a JSON-object that must be released using 'json_object_put'.
+ * Returns NULL in case of error.
+ */
+struct json_object *afm_db_get_application_public(struct afm_db *afdb,
+                                                       const char *id)
 {
-       struct json_object *result = afm_db_get_application(afdb, id);
-       return result && json_object_object_get_ex(result, "public", &result) ? json_object_get(result) : NULL;
+       struct json_object *result;
+       struct json_object *priv = afm_db_get_application(afdb, id);
+       if (priv == NULL)
+               return NULL;
+       if (json_object_object_get_ex(priv, "public", &result))
+               json_object_get(result);
+       else
+               result = NULL;
+       json_object_put(priv);
+       return result;
 }