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