4dc2d5fde6125cd550623d6cf1223a20ed361079
[src/app-framework-main.git] / src / afm-db.c
1 /*
2  Copyright 2015 IoT.bzh
3
4  author: José Bollo <jose.bollo@iot.bzh>
5
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
9
10      http://www.apache.org/licenses/LICENSE-2.0
11
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.
17 */
18
19 #include <stdlib.h>
20 #include <assert.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <sys/types.h>
27
28 #include <json.h>
29
30 #include "utils-json.h"
31 #include "wgt-info.h"
32 #include "afm-db.h"
33
34 /*
35  * The json object recorded by application widget
36  * has the following contents:
37  *  {
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  *    plugins: ARRAY, array of plugins
43  *      [
44  *        STRING, path to the plugin
45  *      ]
46  *    public: OBJECT, public content describing the application widget
47  *      {  
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
56  *      }
57  */
58
59 /*
60  * The structure afm_apps records the data about applications
61  * for several accesses.
62  */
63 struct afm_apps {
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 */
67 };
68
69 /*
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
74  */
75 enum dir_type {
76         type_root, /* type for root directory */
77         type_app   /* type for application directory */
78 };
79
80 /*
81  * Structure for recording a path to application(s)
82  * in the list of directories.
83  */
84 struct afm_db_dir {
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 */
88 };
89
90 /*
91  * The structure afm_db records the applications
92  * for a set of directories recorded as a linked list
93  */
94 struct afm_db {
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 */
99 };
100
101 /*
102  * The structure enumdata records data used when enumerating
103  * application directories of a root directory.
104  */
105 struct enumdata {
106         char path[PATH_MAX];   /* "current" computed path */
107         int length;            /* length of path */
108         struct afm_apps apps;  /* */
109 };
110
111 /*
112  * Release the data of the afm_apps object 'apps'.
113  */
114 static void apps_put(struct afm_apps *apps)
115 {
116         json_object_put(apps->pubarr);
117         json_object_put(apps->direct);
118         json_object_put(apps->byapp);
119 }
120
121 /*
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
126  */
127 static int addwgt(struct afm_apps *apps, const char *path,
128                                                 const struct wgt_desc *desc)
129 {
130         const struct wgt_desc_feature *feat;
131         struct json_object *priv, *pub, *bya, *plugs, *str;
132
133         /* create the application structure */
134         priv = json_object_new_object();
135         if (!priv)
136                 return -1;
137
138         pub = j_add_new_object(priv, "public");
139         if (!pub)
140                 goto error;
141
142         plugs = j_add_new_array(priv, "plugins");
143         if (!plugs)
144                 goto error;
145
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))
158                 goto error;
159
160         /* extract plugins from features */
161         feat = desc->features;
162         while (feat) {
163                 static const char prefix[] = FWK_PREFIX_PLUGIN;
164                 if (!memcmp(feat->name, prefix, sizeof prefix - 1)) {
165                         str = json_object_new_string (
166                                         feat->name + sizeof prefix - 1);
167                         if (str == NULL)
168                                 goto error;
169                         if (json_object_array_add(plugs, str)) {
170                                 json_object_put(str);
171                                 goto error;
172                         }
173                 }
174                 feat = feat->next;
175         }
176
177         /* record the application structure */
178         if (!j_add(apps->direct, desc->idaver, priv))
179                 goto error;
180
181         if (json_object_array_add(apps->pubarr, pub))
182                 goto error;
183         json_object_get(pub);
184
185         if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) {
186                 bya = j_add_new_object(apps->byapp, desc->id);
187                 if (!bya)
188                         goto error;
189         }
190
191         if (!j_add(bya, desc->version, priv))
192                 goto error;
193         json_object_get(priv);
194         return 0;
195
196 error:
197         json_object_put(priv);
198         return -1;
199 }
200
201 /*
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
206  */
207 static int addapp(struct afm_apps *apps, const char *path)
208 {
209         int rc;
210         struct wgt_info *info;
211
212         /* connect to the widget */
213         info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
214         if (info == NULL) {
215                 if (errno == ENOENT)
216                         return 0; /* silently ignore bad directories */
217                 return -1;
218         }
219         /* adds the widget */
220         rc = addwgt(apps, path, wgt_info_desc(info));
221         wgt_info_unref(info);
222         return rc;
223 }
224
225 /*
226  * Enumerate the directories designated by 'data' and call the
227  * function 'callto' for each of them.
228  */
229 static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
230 {
231         DIR *dir;
232         int rc;
233         char *beg;
234         struct dirent entry, *e;
235         size_t len;
236
237         /* opens the directory */
238         dir = opendir(data->path);
239         if (!dir)
240                 return -1;
241
242         /* prepare appending entry names */
243         beg = data->path + data->length;
244         *beg++ = '/';
245
246         /* enumerate entries */
247         rc = readdir_r(dir, &entry, &e);
248         while (!rc && e) {
249                 if (entry.d_name[0] != '.' || (entry.d_name[1]
250                         && (entry.d_name[1] != '.' || entry.d_name[2]))) {
251                         /* prepare callto */
252                         len = strlen(entry.d_name);
253                         if (beg + len >= data->path + sizeof data->path) {
254                                 errno = ENAMETOOLONG;
255                                 return -1;
256                         }
257                         data->length = stpcpy(beg, entry.d_name) - data->path;
258                         /* call the function */
259                         rc = callto(data);
260                         if (rc)
261                                 break;
262                 }       
263                 rc = readdir_r(dir, &entry, &e);
264         }
265         closedir(dir);
266         return rc;
267 }
268
269 /*
270  * called for each version directory.
271  */
272 static int recordapp(struct enumdata *data)
273 {
274         return addapp(&data->apps, data->path);
275 }
276
277 /*
278  * called for each application directory.
279  * enumerate directories of the existing versions.
280  */
281 static int enumvers(struct enumdata *data)
282 {
283         int rc = enumentries(data, recordapp);
284         return !rc || errno != ENOTDIR ? 0 : rc;
285 }
286
287 /*
288  * Adds the directory of 'path' and 'type' to the afm_db object 'afdb'.
289  * Returns 0 in case of success.
290  * Returns -1 and set errno in case of error
291  * Possible errno values: ENOMEM, ENAMETOOLONG
292  */
293 static int add_dir(struct afm_db *afdb, const char *path, enum dir_type type)
294 {
295         struct afm_db_dir *dir;
296         size_t len;
297
298         assert(afdb);
299
300         /* check size */
301         len = strlen(path);
302         if (len >= PATH_MAX) {
303                 errno = ENAMETOOLONG;
304                 return -1;
305         }
306
307         /* avoiding duplications */
308         dir = afdb->dirhead;
309         while(dir != NULL && (strcmp(dir->path, path) || dir->type != type))
310                 dir = dir ->next;
311         if (dir != NULL)
312                 return 0;
313
314         /* allocates the structure */
315         dir = malloc(strlen(path) + sizeof * dir);
316         if (dir == NULL) {
317                 errno = ENOMEM;
318                 return -1;
319         }
320
321         /* add it at tail */
322         dir->next = NULL;
323         dir->type = type;
324         strcpy(dir->path, path);
325         if (afdb->dirtail == NULL)
326                 afdb->dirhead = dir;
327         else
328                 afdb->dirtail->next = dir;
329         afdb->dirtail = dir;
330         return 0;
331 }
332
333 /*
334  * Creates an afm_db object and returns it with one reference added.
335  * Return NULL with errno = ENOMEM if memory exhausted.
336  */
337 struct afm_db *afm_db_create()
338 {
339         struct afm_db *afdb = malloc(sizeof * afdb);
340         if (afdb == NULL)
341                 errno = ENOMEM;
342         else {
343                 afdb->refcount = 1;
344                 afdb->dirhead = NULL;
345                 afdb->dirtail = NULL;
346                 afdb->applications.pubarr = NULL;
347                 afdb->applications.direct = NULL;
348                 afdb->applications.byapp = NULL;
349         }
350         return afdb;
351 }
352
353 /*
354  * Adds a reference to an existing afm_db.
355  */
356 void afm_db_addref(struct afm_db *afdb)
357 {
358         assert(afdb);
359         afdb->refcount++;
360 }
361
362 /*
363  * Removes a reference to an existing afm_db object.
364  * Removes the objet if there no more reference to it.
365  */
366 void afm_db_unref(struct afm_db *afdb)
367 {
368         struct afm_db_dir *dir;
369
370         assert(afdb);
371         if (!--afdb->refcount) {
372                 /* no more reference, clean the memory used by the object */
373                 apps_put(&afdb->applications);
374                 while (afdb->dirhead != NULL) {
375                         dir = afdb->dirhead;
376                         afdb->dirhead = dir->next;
377                         free(dir);
378                 }
379                 free(afdb);
380         }
381 }
382
383 /*
384  * Adds the root directory of 'path' to the afm_db object 'afdb'.
385  * Be aware that no check is done on the directory of 'path' that will
386  * only be used within calls to the function 'afm_db_update_applications'.
387  * Returns 0 in case of success.
388  * Returns -1 and set errno in case of error
389  * Possible errno values: ENOMEM, ENAMETOOLONG
390  */
391 int afm_db_add_root(struct afm_db *afdb, const char *path)
392 {
393         return add_dir(afdb, path, type_root);
394 }
395
396 /*
397  * Adds the application directory of 'path' to the afm_db object 'afdb'.
398  * Be aware that no check is done on the directory of 'path' that will
399  * only be used within calls to the function 'afm_db_update_applications'.
400  * Returns 0 in case of success.
401  * Returns -1 and set errno in case of error
402  * Possible errno values: ENOMEM, ENAMETOOLONG
403  */
404 int afm_db_add_application(struct afm_db *afdb, const char *path)
405 {
406         return add_dir(afdb, path, type_app);
407 }
408
409 /*
410  * Regenerate the list of applications of the afm_bd object 'afdb'.
411  * Returns 0 in case of success.
412  * Returns -1 and set errno in case of error
413  */
414 int afm_db_update_applications(struct afm_db *afdb)
415 {
416         int rc;
417         struct enumdata edata;
418         struct afm_apps oldapps;
419         struct afm_db_dir *dir;
420
421         /* create the result */
422         edata.apps.pubarr = json_object_new_array();
423         edata.apps.direct = json_object_new_object();
424         edata.apps.byapp = json_object_new_object();
425         if (edata.apps.pubarr == NULL || edata.apps.direct == NULL
426                                                 || edata.apps.byapp == NULL) {
427                 errno = ENOMEM;
428                 goto error;
429         }
430         /* for each directory of afdb */
431         for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) {
432                 if (dir->type == type_root) {
433                         edata.length = stpcpy(edata.path, dir->path)
434                                                                 - edata.path;
435                         assert(edata.length < sizeof edata.path);
436                         /* enumerate the applications */
437                         rc = enumentries(&edata, enumvers);
438                         if (rc)
439                                 goto error;
440                 } else {
441                         rc = addapp(&edata.apps, dir->path);
442                 }
443         }
444         /* commit the result */
445         oldapps = afdb->applications;
446         afdb->applications = edata.apps;
447         apps_put(&oldapps);
448         return 0;
449
450 error:
451         apps_put(&edata.apps);
452         return -1;
453 }
454
455 /*
456  * Ensure that applications of the afm_bd object 'afdb' are listed.
457  * Returns 0 in case of success.
458  * Returns -1 and set errno in case of error
459  */
460 int afm_db_ensure_applications(struct afm_db *afdb)
461 {
462         return afdb->applications.pubarr ? 0 : afm_db_update_applications(afdb);
463 }
464
465 /*
466  * Get the list of the applications public data of the afm_db object 'afdb'.
467  * The list is returned as a JSON-array that must be released using
468  * 'json_object_put'.
469  * Returns NULL in case of error.
470  */
471 struct json_object *afm_db_application_list(struct afm_db *afdb)
472 {
473         return afm_db_ensure_applications(afdb) ? NULL
474                         : json_object_get(afdb->applications.pubarr);
475 }
476
477 /*
478  * Get the private data of the applications of 'id' in the afm_db object 'afdb'.
479  * It returns a JSON-object that must be released using 'json_object_put'.
480  * Returns NULL in case of error.
481  */
482 struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id)
483 {
484         struct json_object *result;
485         if (!afm_db_ensure_applications(afdb) && json_object_object_get_ex(
486                                 afdb->applications.direct, id, &result))
487                 return json_object_get(result);
488         return NULL;
489 }
490
491 /*
492  * Get the public data of the applications of 'id' in the afm_db object 'afdb'.
493  * It returns a JSON-object that must be released using 'json_object_put'.
494  * Returns NULL in case of error.
495  */
496 struct json_object *afm_db_get_application_public(struct afm_db *afdb,
497                                                         const char *id)
498 {
499         struct json_object *result;
500         struct json_object *priv = afm_db_get_application(afdb, id);
501         if (priv == NULL)
502                 return NULL;
503         if (json_object_object_get_ex(priv, "public", &result))
504                 json_object_get(result);
505         else
506                 result = NULL;
507         json_object_put(priv);
508         return result;
509 }
510
511
512
513
514 #if defined(TESTAPPFWK)
515 #include <stdio.h>
516 int main()
517 {
518 struct afm_db *afdb = afm_db_create();
519 afm_db_add_root(afdb,FWK_APP_DIR);
520 afm_db_update_applications(afdb);
521 printf("array = %s\n", json_object_to_json_string_ext(afdb->applications.pubarr, 3));
522 printf("direct = %s\n", json_object_to_json_string_ext(afdb->applications.direct, 3));
523 printf("byapp = %s\n", json_object_to_json_string_ext(afdb->applications.byapp, 3));
524 return 0;
525 }
526 #endif
527