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