2 Copyright 2015, 2016, 2017 IoT.bzh
4 author: José Bollo <jose.bollo@iot.bzh>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
26 #include <sys/types.h>
28 #include <json-c/json.h>
30 #include "utils-json.h"
35 * The json object recorded by application widget
36 * has the following contents:
38 * id: STRING, the application identifier without version info
39 * path: STRING, the path of the root directory for the application
40 * content: STRING, the relative path to the entryu point of the application
41 * type: STRING, the mime type describing the type 'content'
42 * bindings: ARRAY, array of bindings
44 * STRING, path to the binding
46 * public: OBJECT, public content describing the application widget
48 * id: STRING, the application identifier "idaver"
49 * version: STRING, the full version of the application
50 * width: INTEGER, the width indication or 0
51 * height: INTEGER, the height indication or 0
52 * name: STRING, the name of the application
53 * description: STRING, the description of the application
54 * shortname: STRING, the short name of the application
55 * author: STRING, the author of the application
60 * The structure afm_apps records the data about applications
61 * for several accesses.
64 struct json_object *pubarr; /* array of the public data of apps */
65 struct json_object *direct; /* hash of applications by their "idaver" */
66 struct json_object *byapp; /* hash of applications by their id */
70 * Two types of directory are handled:
71 * - root directories: contains subdirectories appid/version
72 * containing the applications
73 * - application directories: it contains an application
76 type_root, /* type for root directory */
77 type_app /* type for application directory */
81 * Structure for recording a path to application(s)
82 * in the list of directories.
85 struct afm_db_dir *next; /* link to the next item of the list */
86 enum dir_type type; /* the type of the path */
87 char path[1]; /* the path of the directory */
91 * The structure afm_db records the applications
92 * for a set of directories recorded as a linked list
95 int refcount; /* count of references to the structure */
96 struct afm_db_dir *dirhead; /* first directory of the set */
97 struct afm_db_dir *dirtail; /* last directory of the set */
98 struct afm_apps applications; /* the data about applications */
102 * The structure enumdata records data used when enumerating
103 * application directories of a root directory.
106 char path[PATH_MAX]; /* "current" computed path */
107 int length; /* length of path */
108 struct afm_apps apps; /* */
112 * Release the data of the afm_apps object 'apps'.
114 static void apps_put(struct afm_apps *apps)
116 json_object_put(apps->pubarr);
117 json_object_put(apps->direct);
118 json_object_put(apps->byapp);
122 * Adds the application widget 'desc' of the directory 'path' to the
123 * afm_apps object 'apps'.
124 * Returns 0 in case of success.
125 * Returns -1 and set errno in case of error
127 static int addwgt(struct afm_apps *apps, const char *path,
128 const struct wgt_desc *desc)
130 const struct wgt_desc_feature *feat;
131 struct json_object *priv, *pub, *bya, *plugs, *str;
133 /* create the application structure */
134 priv = json_object_new_object();
138 pub = j_add_new_object(priv, "public");
142 plugs = j_add_new_array(priv, "bindings");
146 if(!j_add_string(priv, "id", desc->id)
147 || !j_add_string(priv, "path", path)
148 || !j_add_string(priv, "content", desc->content_src)
149 || !j_add_string(priv, "type", desc->content_type)
150 || !j_add_string(pub, "id", desc->idaver)
151 || !j_add_string(pub, "version", desc->version)
152 || !j_add_integer(pub, "width", desc->width)
153 || !j_add_integer(pub, "height", desc->height)
154 || !j_add_string(pub, "name", desc->name)
155 || !j_add_string(pub, "description", desc->description)
156 || !j_add_string(pub, "shortname", desc->name_short)
157 || !j_add_string(pub, "author", desc->author))
160 /* extract bindings from features */
161 feat = desc->features;
163 static const char prefix[] = FWK_PREFIX_BINDING;
164 if (!memcmp(feat->name, prefix, sizeof prefix - 1)) {
165 str = json_object_new_string (
166 feat->name + sizeof prefix - 1);
169 if (json_object_array_add(plugs, str)) {
170 json_object_put(str);
177 /* record the application structure */
178 if (!j_add(apps->direct, desc->idaver, priv))
181 if (json_object_array_add(apps->pubarr, pub))
183 json_object_get(pub);
185 if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) {
186 bya = j_add_new_object(apps->byapp, desc->id);
191 if (!j_add(bya, desc->version, priv))
193 json_object_get(priv);
197 json_object_put(priv);
202 * Adds the application widget in the directory 'path' to the
203 * afm_apps object 'apps'.
204 * Returns 0 in case of success.
205 * Returns -1 and set errno in case of error
207 static int addapp(struct afm_apps *apps, const char *path)
210 struct wgt_info *info;
212 /* connect to the widget */
213 info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
216 return 0; /* silently ignore bad directories */
219 /* adds the widget */
220 rc = addwgt(apps, path, wgt_info_desc(info));
221 wgt_info_unref(info);
226 * Enumerate the directories designated by 'data' and call the
227 * function 'callto' for each of them.
229 static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
237 /* opens the directory */
238 dir = opendir(data->path);
242 /* prepare appending entry names */
243 beg = data->path + data->length;
246 /* enumerate entries */
254 if (e->d_name[0] != '.' || (e->d_name[1]
255 && (e->d_name[1] != '.' || e->d_name[2]))) {
257 len = strlen(e->d_name);
258 if (beg + len >= data->path + sizeof data->path) {
259 errno = ENAMETOOLONG;
262 data->length = (int)(stpcpy(beg, e->d_name)
264 /* call the function */
275 * called for each version directory.
277 static int recordapp(struct enumdata *data)
279 return addapp(&data->apps, data->path);
283 * called for each application directory.
284 * enumerate directories of the existing versions.
286 static int enumvers(struct enumdata *data)
288 int rc = enumentries(data, recordapp);
289 return !rc || errno != ENOTDIR ? 0 : rc;
293 * Adds the directory of 'path' and 'type' to the afm_db object 'afdb'.
294 * Returns 0 in case of success.
295 * Returns -1 and set errno in case of error
296 * Possible errno values: ENOMEM, ENAMETOOLONG
298 static int add_dir(struct afm_db *afdb, const char *path, enum dir_type type)
300 struct afm_db_dir *dir;
307 if (len >= PATH_MAX) {
308 errno = ENAMETOOLONG;
312 /* avoiding duplications */
314 while(dir != NULL && (strcmp(dir->path, path) || dir->type != type))
319 /* allocates the structure */
320 dir = malloc(strlen(path) + sizeof * dir);
329 strcpy(dir->path, path);
330 if (afdb->dirtail == NULL)
333 afdb->dirtail->next = dir;
339 * Creates an afm_db object and returns it with one reference added.
340 * Return NULL with errno = ENOMEM if memory exhausted.
342 struct afm_db *afm_db_create()
344 struct afm_db *afdb = malloc(sizeof * afdb);
349 afdb->dirhead = NULL;
350 afdb->dirtail = NULL;
351 afdb->applications.pubarr = NULL;
352 afdb->applications.direct = NULL;
353 afdb->applications.byapp = NULL;
359 * Adds a reference to an existing afm_db.
361 void afm_db_addref(struct afm_db *afdb)
368 * Removes a reference to an existing afm_db object.
369 * Removes the objet if there no more reference to it.
371 void afm_db_unref(struct afm_db *afdb)
373 struct afm_db_dir *dir;
376 if (!--afdb->refcount) {
377 /* no more reference, clean the memory used by the object */
378 apps_put(&afdb->applications);
379 while (afdb->dirhead != NULL) {
381 afdb->dirhead = dir->next;
389 * Adds the root directory of 'path' to the afm_db object 'afdb'.
390 * Be aware that no check is done on the directory of 'path' that will
391 * only be used within calls to the function 'afm_db_update_applications'.
392 * Returns 0 in case of success.
393 * Returns -1 and set errno in case of error
394 * Possible errno values: ENOMEM, ENAMETOOLONG
396 int afm_db_add_root(struct afm_db *afdb, const char *path)
398 return add_dir(afdb, path, type_root);
402 * Adds the application directory of 'path' to the afm_db object 'afdb'.
403 * Be aware that no check is done on the directory of 'path' that will
404 * only be used within calls to the function 'afm_db_update_applications'.
405 * Returns 0 in case of success.
406 * Returns -1 and set errno in case of error
407 * Possible errno values: ENOMEM, ENAMETOOLONG
409 int afm_db_add_application(struct afm_db *afdb, const char *path)
411 return add_dir(afdb, path, type_app);
415 * Regenerate the list of applications of the afm_bd object 'afdb'.
416 * Returns 0 in case of success.
417 * Returns -1 and set errno in case of error
419 int afm_db_update_applications(struct afm_db *afdb)
422 struct enumdata edata;
423 struct afm_apps oldapps;
424 struct afm_db_dir *dir;
426 /* create the result */
427 edata.apps.pubarr = json_object_new_array();
428 edata.apps.direct = json_object_new_object();
429 edata.apps.byapp = json_object_new_object();
430 if (edata.apps.pubarr == NULL || edata.apps.direct == NULL
431 || edata.apps.byapp == NULL) {
435 /* for each directory of afdb */
436 for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) {
437 if (dir->type == type_root) {
438 edata.length = (int)(stpcpy(edata.path, dir->path)
440 assert(edata.length < (int)sizeof edata.path);
441 /* enumerate the applications */
442 rc = enumentries(&edata, enumvers);
446 rc = addapp(&edata.apps, dir->path);
449 /* commit the result */
450 oldapps = afdb->applications;
451 afdb->applications = edata.apps;
456 apps_put(&edata.apps);
461 * Ensure that applications of the afm_bd object 'afdb' are listed.
462 * Returns 0 in case of success.
463 * Returns -1 and set errno in case of error
465 int afm_db_ensure_applications(struct afm_db *afdb)
467 return afdb->applications.pubarr ? 0 : afm_db_update_applications(afdb);
471 * Get the list of the applications public data of the afm_db object 'afdb'.
472 * The list is returned as a JSON-array that must be released using
474 * Returns NULL in case of error.
476 struct json_object *afm_db_application_list(struct afm_db *afdb)
478 return afm_db_ensure_applications(afdb) ? NULL
479 : json_object_get(afdb->applications.pubarr);
483 * Get the private data of the applications of 'id' in the afm_db object 'afdb'.
484 * It returns a JSON-object that must be released using 'json_object_put'.
485 * Returns NULL in case of error.
487 struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id)
490 struct json_object *result;
492 if (afm_db_ensure_applications(afdb))
495 /* search case sensitively */
496 if (json_object_object_get_ex( afdb->applications.direct, id, &result))
497 return json_object_get(result);
499 /* fallback to a case insensitive search */
500 i = json_object_array_length(afdb->applications.pubarr);
502 result = json_object_array_get_idx(afdb->applications.pubarr, --i);
504 && json_object_object_get_ex(result, "id", &result)
505 && !strcasecmp(id, json_object_get_string(result))) {
506 if (json_object_object_get_ex( afdb->applications.direct,
507 json_object_get_string(result),
509 return json_object_get(result);
518 * Get the public data of the applications of 'id' in the afm_db object 'afdb'.
519 * It returns a JSON-object that must be released using 'json_object_put'.
520 * Returns NULL in case of error.
522 struct json_object *afm_db_get_application_public(struct afm_db *afdb,
525 struct json_object *result;
526 struct json_object *priv = afm_db_get_application(afdb, id);
529 if (json_object_object_get_ex(priv, "public", &result))
530 json_object_get(result);
533 json_object_put(priv);
540 #if defined(TESTAPPFWK)
544 struct afm_db *afdb = afm_db_create();
545 afm_db_add_root(afdb,FWK_APP_DIR);
546 afm_db_update_applications(afdb);
547 printf("array = %s\n", json_object_to_json_string_ext(afdb->applications.pubarr, 3));
548 printf("direct = %s\n", json_object_to_json_string_ext(afdb->applications.direct, 3));
549 printf("byapp = %s\n", json_object_to_json_string_ext(afdb->applications.byapp, 3));