refactoring and moving forward
[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         char *appid, *end;
173
174         /* connect to the widget */
175         info = wgt_info_createat(AT_FDCWD, path, 0, 1, 0);
176         if (info == NULL) {
177                 if (errno == ENOENT)
178                         return 0; /* silently ignore bad directories */
179                 goto error;
180         }
181         desc = wgt_info_desc(info);
182
183         /* create the application id */
184         appid = alloca(2 + strlen(desc->id) + strlen(desc->version));
185         end = stpcpy(appid, desc->id);
186         *end++ = '@';
187         strcpy(end, desc->version);
188
189         /* create the application structure */
190         priv = json_object_new_object();
191         if (!priv)
192                 goto error2;
193
194         pub = json_object_new_object();
195         if (!priv)
196                 goto error2;
197
198         if (json_add(priv, "public", pub)) {
199                 json_object_put(pub);
200                 goto error2;
201         }
202
203         plugs = json_object_new_array();
204         if (!priv)
205                 goto error2;
206
207         if (json_add(priv, "plugins", plugs)) {
208                 json_object_put(plugs);
209                 goto error2;
210         }
211
212         if(json_add_str(priv, "id", desc->id)
213         || json_add_str(priv, "path", path)
214         || json_add_str(priv, "content", desc->content_src)
215         || json_add_str(priv, "type", desc->content_type)
216         || json_add_str(pub, "id", appid)
217         || json_add_str(pub, "version", desc->version)
218         || json_add_int(pub, "width", desc->width)
219         || json_add_int(pub, "height", desc->height)
220         || json_add_str(pub, "name", desc->name)
221         || json_add_str(pub, "description", desc->description)
222         || json_add_str(pub, "shortname", desc->name_short)
223         || json_add_str(pub, "author", desc->author))
224                 goto error2;
225
226         feat = desc->features;
227         while (feat) {
228                 static const char prefix[] = FWK_PREFIX_PLUGIN;
229                 if (!memcmp(feat->name, prefix, sizeof prefix - 1)) {
230                         str = json_object_new_string (feat->name + sizeof prefix - 1);
231                         if (str == NULL)
232                                 goto error2;
233                         if (json_object_array_add(plugs, str)) {
234                                 json_object_put(str);
235                                 goto error2;
236                         }
237                 }
238                 feat = feat->next;
239         }
240
241         /* record the application structure */
242         if (!json_object_object_get_ex(apps->byapp, desc->id, &bya)) {
243                 bya = json_object_new_object();
244                 if (!bya)
245                         goto error2;
246                 if (json_add(apps->byapp, desc->id, bya)) {
247                         json_object_put(bya);
248                         goto error2;
249                 }
250         }
251
252         if (json_add(apps->direct, appid, priv))
253                 goto error2;
254         json_object_get(priv);
255
256         if (json_add(bya, desc->version, priv)) {
257                 json_object_put(priv);
258                 goto error2;
259         }
260
261         if (json_object_array_add(apps->pubarr, pub))
262                 goto error2;
263
264         wgt_info_unref(info);
265         return 0;
266
267 error2:
268         json_object_put(priv);
269         wgt_info_unref(info);
270 error:
271         return -1;
272 }
273
274 struct enumdata {
275         char path[PATH_MAX];
276         int length;
277         struct afapps apps;
278 };
279
280 static int enumentries(struct enumdata *data, int (*callto)(struct enumdata *))
281 {
282         DIR *dir;
283         int rc;
284         char *beg, *end;
285         struct dirent entry, *e;
286
287         /* opens the directory */
288         dir = opendir(data->path);
289         if (!dir)
290                 return -1;
291
292         /* prepare appending entry names */
293         beg = data->path + data->length;
294         *beg++ = '/';
295
296         /* enumerate entries */
297         rc = readdir_r(dir, &entry, &e);
298         while (!rc && e) {
299                 if (entry.d_name[0] != '.' || (entry.d_name[1] && (entry.d_name[1] != '.' || entry.d_name[2]))) {
300                         /* prepare callto */
301                         end = stpcpy(beg, entry.d_name);
302                         data->length = end - data->path;
303                         /* call the function */
304                         rc = callto(data);
305                         if (rc)
306                                 break;
307                 }       
308                 rc = readdir_r(dir, &entry, &e);
309         }
310         closedir(dir);
311         return rc;
312 }
313
314 static int recordapp(struct enumdata *data)
315 {
316         return addapp(&data->apps, data->path);
317 }
318
319 /* enumerate the versions */
320 static int enumvers(struct enumdata *data)
321 {
322         int rc = enumentries(data, recordapp);
323         return !rc || errno != ENOTDIR ? 0 : rc;
324 }
325
326 /* regenerate the list of applications */
327 int afm_db_update_applications(struct afm_db *afdb)
328 {
329         int rc;
330         struct enumdata edata;
331         struct afapps oldapps;
332         struct afm_db_dir *dir;
333
334         /* create the result */
335         edata.apps.pubarr = json_object_new_array();
336         edata.apps.direct = json_object_new_object();
337         edata.apps.byapp = json_object_new_object();
338         if (edata.apps.pubarr == NULL || edata.apps.direct == NULL || edata.apps.byapp == NULL) {
339                 errno = ENOMEM;
340                 goto error;
341         }
342         /* for each root */
343         for (dir = afdb->dirhead ; dir != NULL ; dir = dir->next) {
344                 if (dir->type == type_root) {
345                         edata.length = stpcpy(edata.path, dir->path) - edata.path;
346                         assert(edata.length < sizeof edata.path);
347                         /* enumerate the applications */
348                         rc = enumentries(&edata, enumvers);
349                         if (rc)
350                                 goto error;
351                 } else {
352                         rc = addapp(&edata.apps, dir->path);
353                 }
354         }
355         /* commit the result */
356         oldapps = afdb->applications;
357         afdb->applications = edata.apps;
358         json_object_put(oldapps.pubarr);
359         json_object_put(oldapps.direct);
360         json_object_put(oldapps.byapp);
361         return 0;
362
363 error:
364         json_object_put(edata.apps.pubarr);
365         json_object_put(edata.apps.direct);
366         json_object_put(edata.apps.byapp);
367         return -1;
368 }
369
370 int afm_db_ensure_applications(struct afm_db *afdb)
371 {
372         return afdb->applications.pubarr ? 0 : afm_db_update_applications(afdb);
373 }
374
375 struct json_object *afm_db_application_list(struct afm_db *afdb)
376 {
377         return afm_db_ensure_applications(afdb) ? NULL : json_object_get(afdb->applications.pubarr);
378 }
379
380 struct json_object *afm_db_get_application(struct afm_db *afdb, const char *id)
381 {
382         struct json_object *result;
383         if (!afm_db_ensure_applications(afdb) && json_object_object_get_ex(afdb->applications.direct, id, &result))
384                 return json_object_get(result);
385         return NULL;
386 }
387
388 struct json_object *afm_db_get_application_public(struct afm_db *afdb, const char *id)
389 {
390         struct json_object *result = afm_db_get_application(afdb, id);
391         return result && json_object_object_get_ex(result, "public", &result) ? json_object_get(result) : NULL;
392 }
393
394
395
396
397 #if defined(TESTAPPFWK)
398 #include <stdio.h>
399 int main()
400 {
401 struct afm_db *afdb = afm_db_create();
402 afm_db_add_root(afdb,FWK_APP_DIR);
403 afm_db_update_applications(afdb);
404 printf("array = %s\n", json_object_to_json_string_ext(afdb->applications.pubarr, 3));
405 printf("direct = %s\n", json_object_to_json_string_ext(afdb->applications.direct, 3));
406 printf("byapp = %s\n", json_object_to_json_string_ext(afdb->applications.byapp, 3));
407 return 0;
408 }
409 #endif
410