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