Update date in copyrights
[src/app-framework-main.git] / src / wgt-json.c
1 /*
2  Copyright (C) 2015-2019 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 #define _GNU_SOURCE
20
21 #include <stdlib.h>
22 #include <assert.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <stdarg.h>
28 #include <sys/types.h>
29
30 #include <json-c/json.h>
31
32 #include "utils-json.h"
33 #include "wgt-info.h"
34 #include "wgt-json.h"
35 #include "wgt-strings.h"
36 #include "verbose.h"
37
38 /*
39  * This describes an action to be performed for a parameter of name.
40  * The action takes a not null json object, the param matched and a given closure.
41  */
42 struct paramaction
43 {
44         const char *name;       /* the name of the parameter trigerring the action or null (mark end) */
45         int (*action)(struct json_object *obj, const struct wgt_desc_param *param, void *closure); /* the action to perform or null (no action) */
46         void *closure;          /* the closure to pass to the action */
47 };
48
49
50 /*
51  * Apply the first matching 'actions' to each of the given 'params' and for the given json 'obj' (that shouldn't be null)
52  * Returns 0 in case of success or -1 on error.
53  */
54 static int apply_params(struct json_object *obj, const struct wgt_desc_param *params, const struct paramaction *actions)
55 {
56         int rc;
57         const struct paramaction *a;
58
59         if (!obj) {
60                 /* obj can't be null */
61                 errno = EINVAL;
62                 rc = -1;
63         } else {
64                 /* iterate over the params */
65                 rc = 0;
66                 while(params) {
67                         /* search the first match */
68                         for (a = actions ; a->name && strcmp(a->name, params->name) ; a++);
69                         /* invoke the action (or not if null) */
70                         if (a->action && a->action(obj, params, a->closure) < 0)
71                                 rc = -1;
72                         /* next */
73                         params = params->next;
74                 }
75         }
76         return rc;
77 }
78
79 /*
80  * Get the array at the 'mkey' in 'obj'. 'create' it if needed and requested.
81  * Returns 0 in case of success or a negative number otherwise.
82  */
83 static int get_array(struct json_object **result, struct json_object *obj, const char *mkey, int create)
84 {
85         /* enter the multiple key */
86         int rc = j_enter_m(&obj, &mkey, create);
87         if (rc < 0)
88                 /* parent keys doesn't exist */
89                 *result = NULL;
90         else if (!json_object_object_get_ex(obj, mkey, result)) {
91                 /* parent keys exist but not the final key */
92                 if (!create) {
93                         *result = NULL;
94                         rc = -ENOENT;
95                 } else {
96                         /* try to create the array */
97                         if (!(*result = j_add_new_array(obj, mkey)))
98                                 rc = -ENOMEM;
99                 }
100         }
101         return rc;
102 }
103
104 /*
105  * Returns the target name (#target) of the feature 'feat'.
106  * Returns 'defval' if not found or NULL is duplicated
107  */
108 static const char *get_target_name(const struct wgt_desc_feature *feat, const char *defval)
109 {
110         const struct wgt_desc_param *param = feat->params;
111
112         /* search the parameter of name '#target' */
113         while (param && strcmp(param->name, string_sharp_target))
114                 param = param->next;
115         if (param) {
116                 /* found, get itsd value */
117                 defval = param->value;
118
119                 /* check it is defined only one time */
120                 /* TODO: validate it in an other place? */
121                 param = param->next;
122                 while (param) {
123                         if (!strcmp(param->name, string_sharp_target)) {
124                                 defval = NULL;
125                                 break;
126                         }
127                         param = param->next;
128                 }
129         }
130         return defval;
131 }
132
133 /*
134  * Search in 'array' the first object having a field of name 'key' and of string value 'val'
135  * Returns NULL if not found
136  */
137 static struct json_object *get_array_item_by_key(struct json_object *array, const char *key, const char *val)
138 {
139         struct json_object *result, *k;
140         int i, n;
141
142         n = json_object_array_length(array);
143         for (i = 0 ; i < n ; i++) {
144                 result = json_object_array_get_idx(array, i);
145                 if (result && json_object_object_get_ex(result, key, &k)
146                         && json_object_get_type(k) == json_type_string
147                         && !strcmp(json_object_get_string(k), val))
148                         return result;
149         }
150         return NULL;
151 }
152
153 /*
154  * Get or create from the array 'targets' the structure for the target of 'name'
155  * Should create if 'create' is not not otherwise just get.
156  * Returns zero on success or a errno negative code
157  */
158 static int get_target(struct json_object **result, struct json_object *targets, const char *name, int create)
159 {
160         int rc;
161         struct json_object *t;
162
163         /* search in targets a structure of target name */
164         t = get_array_item_by_key(targets, string_sharp_target, name);
165         if (t) {
166                 /* found */
167                 if (!create)
168                         rc = 0;
169                 else {
170                         ERROR("duplicated target name: %s", name);
171                         t = NULL;
172                         rc = -EEXIST;
173                 }
174         } else {
175                 /* not found */
176                 if (!create) {
177                         ERROR("target name not found: %s", name);
178                         rc = -ENOENT;
179                 } else {
180                         /* create a new value */
181                         t = j_add_new_object(targets, NULL);
182                         if (t && j_add_string(t, string_sharp_target, name))
183                                 rc = 0;
184                         else {
185                                 json_object_put(t);
186                                 t = NULL;
187                                 rc = -ENOMEM;
188                         }
189                 }
190         }
191         *result = t;
192         return rc;
193 }
194
195 /* create the target value for the feature */
196 static int create_target(struct json_object *targets, const struct wgt_desc_feature *feat)
197 {
198         const char *id;
199         struct json_object *target;
200
201         /* search within the feature the parameter naming the target */
202         id = get_target_name(feat, NULL);
203         if (id == NULL) {
204                 ERROR("target of feature %s is %s", feat->name, get_target_name(feat, feat->name) ? "repeated" : "missing");
205                 return -EINVAL;
206         }
207
208         /* create the target */
209         return get_target(&target, targets, id, 1);
210 }
211
212 /*
213  * Adds icon data to the target
214  */
215 static int add_icon(struct json_object *target, const struct wgt_desc_icon *icon)
216 {
217         int rc;
218         struct json_object *object, *array;
219
220         rc = get_array(&array, target, string_icon, 1);
221         if (rc >= 0) {
222                 object = json_object_new_object();
223                 if(!object
224                 || !j_add_string(object, string_src, icon->src)
225                 || (icon->width > 0 && !j_add_integer(object, string_width, icon->width))
226                 || (icon->height > 0 && !j_add_integer(object, string_height, icon->height))
227                 || !j_add(array, NULL, object)) {
228                         json_object_put(object);
229                         rc = -ENOMEM;
230                 }
231         }
232         return rc;
233 }
234
235 /*
236  * Creates the main target
237  */
238 static int create_main_target(struct json_object *targets, const struct wgt_desc *desc)
239 {
240         int rc;
241         struct wgt_desc_icon *icon;
242         struct json_object *target;
243
244         /* create the target 'main' */
245         rc = get_target(&target, targets, string_main, 1);
246
247         /* add icons if any */
248         icon = desc->icons;
249         while (rc >= 0 && icon) {
250                 rc = add_icon(target, icon);
251                 icon = icon->next;
252         }
253
254         /* add content */
255         if (rc >= 0)
256                 rc = j_add_many_strings_m(target,
257                         "content.src", desc->content_src,
258                         "content.type", desc->content_type,
259                         "content.encoding", desc->content_encoding,
260                         NULL) ? 0 : -errno;
261
262         /* add other info */
263         if (rc >= 0)
264                 if((desc->width && !j_add_integer(target, string_width, desc->width))
265                 || (desc->height && !j_add_integer(target, string_height, desc->height))
266                 || (desc->viewmodes && !j_add_string(target, string_viewmodes,  desc->viewmodes))
267                 || (desc->defaultlocale && !j_add_string(target, string_defaultlocale,  desc->defaultlocale)))
268                         rc = -ENOMEM;
269
270         return rc;
271 }
272
273 /***********************************************************************************************************/
274
275 /*
276  * translate a param to an object { "name": name, "value": value }
277  */
278 static struct json_object *object_of_param(const struct wgt_desc_param *param)
279 {
280         struct json_object *value;
281
282         value = json_object_new_object();
283         if (value
284          && j_add_string(value, string_name, param->name)
285          && j_add_string(value, string_value, param->value))
286                 return value;
287
288         json_object_put(value);
289         return NULL;
290 }
291
292 /*
293  * Add the field of mkey 'param'.name with 'param'.value to the object 'obj'
294  */
295 static int add_param_simple(struct json_object *obj, const struct wgt_desc_param *param, void *closure)
296 {
297         return j_add_string_m(obj, param->name, param->value);
298 }
299
300 /* add a param object to an array of param objects */
301 static int add_param_array(struct json_object *obj, const struct wgt_desc_param *param, void *closure)
302 {
303         const char *array_name = closure;
304         struct json_object *array, *value;
305
306         /* the array is either pointed by 'closure=array_name' or the given 'obj' */
307         if (!array_name)
308                 array = obj;
309         else if (!json_object_object_get_ex(obj, array_name, &array)) {
310                 array = j_add_new_array(obj, array_name);
311                 if (!array)
312                         return -ENOMEM;
313         }
314
315         /* append the param object */
316         value = object_of_param(param);
317         if (value && j_add(array, NULL, value))
318                 return 0;
319
320         json_object_put(value);
321         return -ENOMEM;
322 }
323
324 /* add a param object to an object of param objects, indexed by the param name */
325 static int add_param_object(struct json_object *obj, const struct wgt_desc_param *param, void *closure)
326 {
327         const char *object_name = closure;
328         struct json_object *object, *value;
329
330         /* the object is either pointed by 'object_name=closure' or the given 'obj' */
331         if (!object_name)
332                 object = obj;
333         else if (!json_object_object_get_ex(obj, object_name, &object)) {
334                 object = j_add_new_object(obj, object_name);
335                 if (!object)
336                         return -ENOMEM;
337         }
338
339         /* append the param object */
340         value = object_of_param(param);
341         if (value && j_add(object, param->name, value))
342                 return 0;
343
344         json_object_put(value);
345         return -ENOMEM;
346 }
347
348 /* Retrieve within 'targets' the target of the feature 'feat' and applies 'actions' to it */
349 static int add_targeted_params(struct json_object *targets, const struct wgt_desc_feature *feat, struct paramaction actions[])
350 {
351         int rc;
352         const char *id;
353         struct json_object *obj;
354
355         id = get_target_name(feat, string_main);
356         rc = get_target(&obj, targets, id, 0);
357         return rc < 0 ? rc : apply_params(obj, feat->params, actions);
358 }
359
360 /* Treats the feature "provided_unit" */
361 static int add_provided_unit(struct json_object *targets, const struct wgt_desc_feature *feat)
362 {
363         static struct paramaction actions[] = {
364                 { .name = string_sharp_target, .action = NULL, .closure = NULL }, /* skip #target */
365                 { .name = NULL, .action = add_param_simple, .closure = NULL }
366         };
367         return add_targeted_params(targets, feat, actions);
368 }
369
370 /* Treats the feature "provided_api" */
371 static int add_provided_api(struct json_object *targets, const struct wgt_desc_feature *feat)
372 {
373         static struct paramaction actions[] = {
374                 { .name = string_sharp_target, .action = NULL, .closure = NULL }, /* skip #target */
375                 { .name = NULL, .action = add_param_array, .closure = (void*)string_provided_api }
376         };
377         return add_targeted_params(targets, feat, actions);
378 }
379
380 /* Treats the feature "required_api" */
381 static int add_required_api(struct json_object *targets, const struct wgt_desc_feature *feat)
382 {
383         static struct paramaction actions[] = {
384                 { .name = string_sharp_target, .action = NULL, .closure = NULL }, /* skip #target */
385                 { .name = NULL, .action = add_param_array, .closure = (void*)string_required_api }
386         };
387         return add_targeted_params(targets, feat, actions);
388 }
389
390 /* Treats the feature "provided_binding" */
391 static int add_provided_binding(struct json_object *targets, const struct wgt_desc_feature *feat)
392 {
393         static struct paramaction actions[] = {
394                 { .name = string_sharp_target, .action = NULL, .closure = NULL }, /* TODO: should be an error! */
395                 { .name = NULL, .action = add_param_array, .closure = (void*)string_provided_binding }
396         };
397         return add_targeted_params(targets, feat, actions);
398 }
399
400 /* Treats the feature "required_binding" */
401 static int add_required_binding(struct json_object *targets, const struct wgt_desc_feature *feat)
402 {
403         static struct paramaction actions[] = {
404                 { .name = string_sharp_target, .action = NULL, .closure = NULL }, /* skip #target */
405                 { .name = NULL, .action = add_param_array, .closure = (void*)string_required_binding }
406         };
407         return add_targeted_params(targets, feat, actions);
408 }
409
410 /* Treats the feature "required_permission" */
411 static int add_required_permission(struct json_object *targets, const struct wgt_desc_feature *feat)
412 {
413         static struct paramaction actions[] = {
414                 { .name = string_sharp_target, .action = NULL, .closure = NULL }, /* skip #target */
415                 { .name = NULL, .action = add_param_object, .closure = (void*)string_required_permission }
416         };
417         return add_targeted_params(targets, feat, actions);
418 }
419
420 /* Treats the feature "defined_permission" */
421 static int add_defined_permission(struct json_object *defperm, const struct wgt_desc_feature *feat)
422 {
423         static struct paramaction actions[] = {
424                 { .name = NULL, .action = add_param_array, .closure = NULL }
425         };
426         return apply_params(defperm, feat->params, actions);
427 }
428
429 /***********************************************************************************************************/
430
431 /*
432  * Create the json object from 'desc'
433  */
434 static struct json_object *to_json(const struct wgt_desc *desc)
435 {
436         size_t prefixlen;
437         const struct wgt_desc_feature *feat;
438         const char *featname;
439         struct json_object *result, *targets, *permissions;
440         int rc, rc2;
441
442         /* create the application structure */
443         if(!(result = json_object_new_object())
444         || !(targets = j_add_new_array(result, string_targets))
445         || !(permissions = j_add_new_array(result, string_defined_permission))
446         )
447                 goto error;
448
449         /* first pass: declarations */
450         rc = create_main_target(targets, desc);
451         prefixlen = strlen(string_AGL_widget_prefix);
452         for (feat = desc->features ; feat ; feat = feat->next) {
453                 featname = feat->name;
454                 if (!memcmp(featname, string_AGL_widget_prefix, prefixlen)) {
455                         if (!feat->required) {
456                                 ERROR("feature %s can't be optional", featname);
457                                 if (!rc)
458                                         rc = -EINVAL;
459                         }
460                         featname += prefixlen;
461                         if (!strcmp(featname, string_provided_unit)) {
462                                 rc2 = create_target(targets, feat);
463                                 if (rc2 < 0 && !rc)
464                                         rc = rc2;
465                         }
466                 }
467         }
468
469         /* second pass: definitions */
470         for (feat = desc->features ; feat ; feat = feat->next) {
471                 featname = feat->name;
472                 if (!memcmp(featname, string_AGL_widget_prefix, prefixlen)) {
473                         featname += prefixlen;
474                         if (!strcmp(featname, string_defined_permission)) {
475                                 rc2 = add_defined_permission(permissions, feat);
476                         }
477                         else if (!strcmp(featname, string_provided_unit)) {
478                                 rc2 = add_provided_unit(targets, feat);
479                         }
480                         else if (!strcmp(featname, string_provided_api)) {
481                                 rc2 = add_provided_api(targets, feat);
482                         }
483                         else if (!strcmp(featname, string_provided_binding)) {
484                                 rc2 = add_provided_binding(targets, feat);
485                         }
486                         else if (!strcmp(featname, string_required_api)) {
487                                 rc2 = add_required_api(targets, feat);
488                         }
489                         else if (!strcmp(featname, string_required_binding)) {
490                                 rc2 = add_required_binding(targets, feat);
491                         }
492                         else if (!strcmp(featname, string_required_permission)) {
493                                 rc2 = add_required_permission(targets, feat);
494                         } else {
495                                 /* gently ignore other features */
496                                 rc2 = 0;
497                         }
498                         if (rc2 < 0 && !rc)
499                                 rc = rc2;
500                 }
501         }
502
503         /* fills the main */
504         rc2 = j_add_many_strings_m(result,
505                 string_id, desc->id,
506                 string_idaver, desc->idaver,
507                 string_version, desc->version,
508                 "ver", desc->ver,
509                 "author.content", desc->author,
510                 "author.href", desc->author_href,
511                 "author.email", desc->author_email,
512                 "license.content", desc->license,
513                 "license.href", desc->license_href,
514                 "defaultlocale", desc->defaultlocale,
515                 "name.content", desc->name,
516                 "name.short", desc->name_short,
517                 "description", desc->description,
518                 NULL) ? 0 : -errno;
519         if (rc2 < 0 && !rc)
520                 rc = rc2;
521
522         /* returns the result if there is no error*/
523         if (!rc)
524                 return result;
525
526 error:
527         json_object_put(result);
528         return NULL;
529 }
530
531 /* get the json_object of the wgt_info 'info' or NULL if error */
532 struct json_object *wgt_info_to_json(struct wgt_info *info)
533 {
534         return to_json(wgt_info_desc(info));
535 }
536
537 /* get the json_object of the 'wgt' or NULL if error */
538 struct json_object *wgt_to_json(struct wgt *wgt)
539 {
540         struct json_object *result;
541         struct wgt_info *info;
542
543         info = wgt_info_create(wgt, 1, 1, 1);
544         if (info == NULL)
545                 result = NULL;
546         else {
547                 result = wgt_info_to_json(info);
548                 wgt_info_unref(info);
549         }
550         return result;
551 }
552
553 /* get the json_object of the widget of 'path' relative to 'dfd' or NULL if error */
554 struct json_object *wgt_path_at_to_json(int dfd, const char *path)
555 {
556         struct json_object *result;
557         struct wgt_info *info;
558
559         info = wgt_info_createat(dfd, path, 1, 1, 1);
560         if (info == NULL)
561                 result = NULL;
562         else {
563                 result = wgt_info_to_json(info);
564                 wgt_info_unref(info);
565         }
566         return result;
567 }
568
569 /* get the json_object of the widget of 'path' or NULL if error */
570 struct json_object *wgt_path_to_json(const char *path)
571 {
572         return wgt_path_at_to_json(AT_FDCWD, path);
573 }
574
575