c764ea833a4c057888ef98d265e489afb9f3bae1
[src/app-framework-main.git] / src / wgt-json.c
1 /*
2  Copyright 2015, 2016, 2017 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 "required_permission" */
391 static int add_required_permission(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 }, /* skip #target */
395                 { .name = NULL, .action = add_param_object, .closure = (void*)string_required_permission }
396         };
397         return add_targeted_params(targets, feat, actions);
398 }
399
400 /* Treats the feature "defined_permission" */
401 static int add_defined_permission(struct json_object *defperm, const struct wgt_desc_feature *feat)
402 {
403         static struct paramaction actions[] = {
404                 { .name = NULL, .action = add_param_array, .closure = NULL }
405         };
406         return apply_params(defperm, feat->params, actions);
407 }
408
409 /***********************************************************************************************************/
410
411 /*
412  * Create the json object from 'desc'
413  */
414 static struct json_object *to_json(const struct wgt_desc *desc)
415 {
416         size_t prefixlen;
417         const struct wgt_desc_feature *feat;
418         const char *featname;
419         struct json_object *result, *targets, *permissions;
420         int rc, rc2;
421
422         /* create the application structure */
423         if(!(result = json_object_new_object())
424         || !(targets = j_add_new_array(result, string_targets))
425         || !(permissions = j_add_new_array(result, string_defined_permission))
426         )
427                 goto error;
428
429         /* first pass: declarations */
430         rc = create_main_target(targets, desc);
431         prefixlen = strlen(string_AGL_widget_prefix);
432         for (feat = desc->features ; feat ; feat = feat->next) {
433                 featname = feat->name;
434                 if (!memcmp(featname, string_AGL_widget_prefix, prefixlen)) {
435                         if (!feat->required) {
436                                 ERROR("feature %s can't be optional", featname);
437                                 if (!rc)
438                                         rc = -EINVAL;
439                         }
440                         featname += prefixlen;
441                         if (!strcmp(featname, string_provided_unit)) {
442                                 rc2 = create_target(targets, feat);
443                                 if (rc2 < 0 && !rc)
444                                         rc = rc2;
445                         }
446                 }
447         }
448
449         /* second pass: definitions */
450         for (feat = desc->features ; feat ; feat = feat->next) {
451                 featname = feat->name;
452                 if (!memcmp(featname, string_AGL_widget_prefix, prefixlen)) {
453                         featname += prefixlen;
454                         if (!strcmp(featname, string_defined_permission)) {
455                                 rc2 = add_defined_permission(permissions, feat);
456                         }
457                         else if (!strcmp(featname, string_provided_unit)) {
458                                 rc2 = add_provided_unit(targets, feat);
459                         }
460                         else if (!strcmp(featname, string_provided_api)) {
461                                 rc2 = add_provided_api(targets, feat);
462                         }
463                         else if (!strcmp(featname, string_required_api)) {
464                                 rc2 = add_required_api(targets, feat);
465                         }
466                         else if (!strcmp(featname, string_required_permission)) {
467                                 rc2 = add_required_permission(targets, feat);
468                         } else {
469                                 /* gently ignore other features */
470                                 rc2 = 0;
471                         }
472                         if (rc2 < 0 && !rc)
473                                 rc = rc2;
474                 }
475         }
476
477         /* fills the main */
478         rc2 = j_add_many_strings_m(result,
479                 string_id, desc->id,
480                 string_idaver, desc->idaver,
481                 string_version, desc->version,
482                 "ver", desc->ver,
483                 "author.content", desc->author,
484                 "author.href", desc->author_href,
485                 "author.email", desc->author_email,
486                 "license.content", desc->license,
487                 "license.href", desc->license_href,
488                 "defaultlocale", desc->defaultlocale,
489                 "name.content", desc->name,
490                 "name.short", desc->name_short,
491                 "description", desc->description,
492                 NULL) ? 0 : -errno;
493         if (rc2 < 0 && !rc)
494                 rc = rc2;
495
496         /* returns the result if there is no error*/
497         if (!rc)
498                 return result;
499
500 error:
501         json_object_put(result);
502         return NULL;
503 }
504
505 /* get the json_object of the wgt_info 'info' or NULL if error */
506 struct json_object *wgt_info_to_json(struct wgt_info *info)
507 {
508         return to_json(wgt_info_desc(info));
509 }
510
511 /* get the json_object of the 'wgt' or NULL if error */
512 struct json_object *wgt_to_json(struct wgt *wgt)
513 {
514         struct json_object *result;
515         struct wgt_info *info;
516
517         info = wgt_info_create(wgt, 1, 1, 1);
518         if (info == NULL)
519                 result = NULL;
520         else {
521                 result = wgt_info_to_json(info);
522                 wgt_info_unref(info);
523         }
524         return result;
525 }
526
527 /* get the json_object of the widget of 'path' relative to 'dfd' or NULL if error */
528 struct json_object *wgt_path_at_to_json(int dfd, const char *path)
529 {
530         struct json_object *result;
531         struct wgt_info *info;
532
533         info = wgt_info_createat(dfd, path, 1, 1, 1);
534         if (info == NULL)
535                 result = NULL;
536         else {
537                 result = wgt_info_to_json(info);
538                 wgt_info_unref(info);
539         }
540         return result;
541 }
542
543 /* get the json_object of the widget of 'path' or NULL if error */
544 struct json_object *wgt_path_to_json(const char *path)
545 {
546         return wgt_path_at_to_json(AT_FDCWD, path);
547 }
548
549