Adding utils-json
[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 struct afapps {
35         struct json_object *pubarr;
36         struct json_object *direct;
37         struct json_object *byapp;
38 };
39
40 enum dir_type {
41         type_root,
42         type_app
43 };
44
45 struct afm_db_dir {
46         struct afm_db_dir *next;
47         char *path;
48         enum dir_type type;
49 };
50
51 struct afm_db {
52         int refcount;
53         struct afm_db_dir *dirhead;
54         struct afm_db_dir *dirtail;
55         struct afapps applications;
56 };
57
58 struct afm_db *afm_db_create()
59 {
60         struct afm_db *afdb = malloc(sizeof * afdb);
61         if (afdb == NULL)
62                 errno = ENOMEM;
63         else {
64                 afdb->refcount = 1;
65                 afdb->dirhead = NULL;
66                 afdb->dirtail = NULL;
67                 afdb->applications.pubarr = NULL;
68                 afdb->applications.direct = NULL;
69                 afdb->applications.byapp = NULL;
70         }
71         return afdb;
72 }
73
74 void afm_db_addref(struct afm_db *afdb)
75 {
76         assert(afdb);
77         afdb->refcount++;
78 }
79
80 void afm_db_unref(struct afm_db *afdb)
81 {
82         struct afm_db_dir *dir;
83         assert(afdb);
84         if (!--afdb->refcount) {
85                 json_object_put(afdb->applications.pubarr);
86                 json_object_put(afdb->applications.direct);
87                 json_object_put(afdb->applications.byapp);
88                 while (afdb->dirhead != NULL) {
89                         dir = afdb->dirhead;
90                         afdb->dirhead = dir->next;
91                         free(dir->path);
92                         free(dir);
93                 }
94                 free(afdb);
95         }
96 }
97
98 int add_dir(struct afm_db *afdb, const char *path, enum dir_type type)
99 {
100         struct afm_db_dir *dir;
101         char *r;
102
103         assert(afdb);
104
105         /* don't depend on the cwd and unique name */
106         r = realpath(path, NULL);
107         if (!r)
108                 return -1;
109
110         /* avoiding duplications */
111         dir = afdb->dirhead;
112         while(dir != NULL && (strcmp(dir->path, path) || dir->type != type))
113                 dir = dir ->next;
114         if (dir != NULL) {
115                 free(r);
116                 return 0;
117         }
118
119         /* allocates the structure */
120         dir = malloc(sizeof * dir);
121         if (dir == NULL) {
122                 free(r);
123                 errno = ENOMEM;
124                 return -1;
125         }
126
127         /* add */
128         dir->next = NULL;
129         dir->path = r;
130         dir->type = type;
131         if (afdb->dirtail == NULL)
132                 afdb->dirhead = dir;
133         else
134                 afdb->dirtail->next = dir;
135         afdb->dirtail = dir;
136         return 0;
137 }
138
139 int afm_db_add_root(struct afm_db *afdb, const char *path)
140 {
141         return add_dir(afdb, path, type_root);
142 }
143
144 int afm_db_add_application(struct afm_db *afdb, const char *path)
145 {
146         return add_dir(afdb, path, type_app);
147 }
148
149 static int addapp(struct afapps *apps, const char *path)
150 {
151         struct wgt_info *info;
152         const struct wgt_desc *desc;
153         const struct wgt_desc_feature *feat;
154         struct json_object *priv = NULL, *pub, *bya, *plugs, *str;
155
156         /* connect to the widget */
157         info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
158         if (info == NULL) {
159                 if (errno == ENOENT)
160                         return 0; /* silently ignore bad directories */
161                 goto error;
162         }
163         desc = wgt_info_desc(info);
164
165         /* create the application structure */
166         priv = json_object_new_object();
167         if (!priv)
168                 goto error2;
169
170         pub = json_object_new_object();
171         if (!priv)
172                 goto error2;
173
174         if (!j_add(priv, "public", pub)) {
175                 json_object_put(pub);
176                 goto error2;
177         }
178
179         plugs = json_object_new_array();
180         if (!priv)
181                 goto error2;
182
183         if (!j_add(priv, "plugins", plugs)) {
184                 json_object_put(plugs);
185                 goto error2;
186         }
187
188         if(!j_add_string(priv, "id", desc->id)
189         || !j_add_string(priv, "path", path)
190         || !j_add_string(priv, "content", desc->content_src)
191         || !j_add_string(priv, "type", desc->content_type)
192         || !j_add_string(pub, "id", desc->idaver)
193         || !j_add_string(pub, "version", desc->version)
194         || !j_add_integer(pub, "width", desc->width)
195         || !j_add_integer(pub, "height", desc->height)
196         || !j_add_string(pub, "name", desc->name)
197         || !j_add_string(pub, "description", desc->description)
198         || !j_add_string(pub, "shortname", desc->name_short)
199         || !j_add_string(pub, "author", desc->author))
200                 goto error2;
201
202         feat = desc->features;
203         while (feat) {
204                 static const char prefix[] = FWK_PREFIX_PLUGIN;
205                 if (!memcmp(feat->name, prefix, sizeof prefix - 1)) {
206                         str = json_object_new_string (feat->name + sizeof prefix - 1);
207                         if (str == NULL)
208                                 goto error2;
209                         if (json_object_array_add(plugs, str)) {
210                                 json_object_put(str);
211                                 goto error2;
212                         }
213                 }
214                 feat = feat->next;
215         }
216
217         /* record the application structure */
218         if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) {
219                 bya = json_object_new_object();
220                 if (!bya)
221                         goto error2;
222                 if (!j_add(apps->byapp, desc->id, bya)) {
223                         json_object_put(bya);
224                         goto error2;
225                 }
226         }
227
228         if (!j_add(apps->direct, desc->idaver, priv))
229                 goto error2;
230         json_object_get(priv);
231
232         if (!j_add(bya, desc->version, priv)) {
233                 json_object_put(priv);
234                 goto error2;
235         }
236
237         if (json_object_array_add(apps->pubarr, pub))
238                 goto error2;
239
240         wgt_info_unref(info);
241         return 0;
242
243 error2:
244         json_object_put(priv);
245         wgt_info_unref(info);
246 error:
247         return -1;
248 }
249
250 struct enumdata {
251         char path[PATH_MAX];
252         int length;
253         struct afapps apps;
254 };
255
256 static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
257 {
258         DIR *dir;
259         int rc;
260         char *beg, *end;
261         struct dirent entry, *e;
262
263         /* opens the directory */
264         dir = opendir(data->path);
265         if (!dir)
266                 return -1;
267
268         /* prepare appending entry names */
269         beg = data->path + data->length;
270         *beg++ = '/';
271
272         /* enumerate entries */
273         rc = readdir_r(dir, &entry, &e);
274         while (!rc && e) {
275                 if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) {
276                         /* prepare callto */
277                         end = stpcpy(beg, entry.d_name);
278                         data->length = end - data->path;
279                         /* call the function */
280                         rc = callto(data);
281                         if (rc)
282                                 break;
283                 }       
284                 rc = readdir_r(dir, &entry, &e);
285         }
286         closedir(dir);
287         return rc;
288 }
289
290 static int recordapp(struct enumdata *data)
291 {
292         return addapp(&data->apps, data->path);
293 }
294
295 /* enumerate the versions */
296 static int enumvers(struct enumdata *data)
297 {
298         int rc = enumentries(data, recordapp);
299         return !rc || errno != ENOTDIR ? 0 : rc;
300 }
301
302 /* regenerate the list of applications */
303 int afm_db_update_applications(struct afm_db *afdb)
304 {
305         int rc;
306         struct enumdata edata;
307         struct afapps oldapps;
308         struct afm_db_dir *dir;
309
310         /* create the result */
311         edata.apps.pubarr = json_object_new_array();
312         edata.apps.direct = json_object_new_object();
313         edata.apps.byapp = json_object_new_object();
314         if (edata.apps.pubarr == NULL || edata.apps.direct == NULL || edata.apps.byapp == NULL) {
315                 errno = ENOMEM;
316                 goto error;
317         }
318         /* for each root */
319         for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) {
320                 if (dir->type == type_root) {
321                         edata.length = stpcpy(edata.path, dir->path) - edata.path;
322                         assert(edata.length < sizeof edata.path);
323                         /* enumerate the applications */
324                         rc = enumentries(&edata, enumvers);
325                         if (rc)
326                                 goto error;
327                 } else {
328                         rc = addapp(&edata.apps, dir->path);
329                 }
330         }
331         /* commit the result */
332         oldapps = afdb->applications;
333         afdb->applications = edata.apps;
334         json_object_put(oldapps.pubarr);
335         json_object_put(oldapps.direct);
336         json_object_put(oldapps.byapp);
337         return 0;
338
339 error:
340         json_object_put(edata.apps.pubarr);
341         json_object_put(edata.apps.direct);
342         json_object_put(edata.apps.byapp);
343         return -1;
344 }
345
346 int afm_db_ensure_applications(struct afm_db *afdb)
347 {
348         return afdb->applications.pubarr ? 0 : afm_db_update_applications(afdb);
349 }
350
351 struct json_object *afm_db_application_list(struct afm_db *afdb)
352 {
353         return afm_db_ensure_applications(afdb) ? NULL : json_object_get(afdb->applications.pubarr);
354 }
355
356 struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id)
357 {
358         struct json_object *result;
359         if (!afm_db_ensure_applications(afdb) && json_object_object_get_ex(afdb->applications.direct, id, &result))
360                 return json_object_get(result);
361         return NULL;
362 }
363
364 struct json_object *afm_db_get_application_public(struct afm_db *afdb, const char *id)
365 {
366         struct json_object *result = afm_db_get_application(afdb, id);
367         return result && json_object_object_get_ex(result, "public", &result) ? json_object_get(result) : NULL;
368 }
369
370
371
372
373 #if defined(TESTAPPFWK)
374 #include <stdio.h>
375 int main()
376 {
377 struct afm_db *afdb = afm_db_create();
378 afm_db_add_root(afdb,FWK_APP_DIR);
379 afm_db_update_applications(afdb);
380 printf("array = %s\n", json_object_to_json_string_ext(afdb->applications.pubarr, 3));
381 printf("direct = %s\n", json_object_to_json_string_ext(afdb->applications.direct, 3));
382 printf("byapp = %s\n", json_object_to_json_string_ext(afdb->applications.byapp, 3));
383 return 0;
384 }
385 #endif
386