Bindings V2: Refactor session flags
[src/app-framework-binder.git] / src / genskel / genskel.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 /*
18  * This simple program expands the object { "$ref": "#/path/to/a/target" }
19  *
20  * For example:
21  *
22  *  {
23  *    "type":{
24  *      "a": "int",
25  *      "b": { "$ref": "#/type/a" }
26  *    }
27  *  }
28  *
29  * will be exapanded to
30  *
31  *  {
32  *    "type":{
33  *      "a": "int",
34  *      "b": "int"
35  *    }
36  *  }
37  * 
38  * Invocation:   program  [file|-]...
39  *
40  * without arguments, it reads the input.
41  */
42
43 #define _GNU_SOURCE
44 #include <string.h>
45 #include <stdio.h>
46 #include <unistd.h>
47
48 #include <json-c/json.h>
49
50 #define oom(x) do{if(!(x)){fprintf(stderr,"out of memory\n");exit(1);}}while(0)
51
52 /**
53  * records path to the expanded node
54  */
55 struct path
56 {
57         struct json_object *object;     /**< node being expanded */
58         struct path *upper;             /**< link to upper expanded nodes */
59 };
60
61 /**
62  * root of the JSON being parsed
63  */
64 struct json_object *root = NULL;
65 struct json_object *d_perms = NULL;
66 struct json_object *a_perms = NULL;
67 const char *init = NULL;
68 const char *start = NULL;
69 const char *onevent = NULL;
70 const char *api = NULL;
71 const char *scope = NULL;
72 const char *prefix = NULL;
73 const char *postfix = NULL;
74 int priv = -1;
75
76 /**
77  * Search for a reference of type "#/a/b/c" int the
78  * parsed JSON object
79  */
80 struct json_object *search(const char *path)
81 {
82         char *d;
83         struct json_object *i;
84
85         /* does it match #/ at the beginning? */
86         if (path[0] != '#' || (path[0] && path[1] != '/'))
87                 return NULL;
88
89         /* search from root to target */
90         i = root;
91         d = strdupa(path+2);
92         d = strtok(d, "/");
93         while(i && d) {
94                 if (!json_object_object_get_ex(i, d, &i))
95                         return NULL; 
96                 d = strtok(NULL, "/");
97         }
98         return i;
99 }
100
101 /**
102  * Expands the node designated by path and returns its expanded form
103  */
104 struct json_object *expand_$ref(struct path path)
105 {
106         struct path *p;
107         struct json_object *o, *x;
108         int n, i;
109         struct json_object_iterator ji, jn; 
110
111         /* expansion depends of the type of the node */
112         switch (json_object_get_type(path.object)) {
113         case json_type_object:
114                 /* for object, look if it contains a property "$ref" */
115                 if (json_object_object_get_ex(path.object, "$ref", &o)) {
116                         /* yes, reference, try to substitute its target */
117                         if (!json_object_is_type(o, json_type_string)) {
118                                 fprintf(stderr, "found a $ref not being string. Is: %s\n", json_object_get_string(o));
119                                 exit(1);
120                         }
121                         x = search(json_object_get_string(o));
122                         if (!x) {
123                                 fprintf(stderr, "$ref not found. Was: %s\n", json_object_get_string(o));
124                                 exit(1);
125                         }
126                         p = &path;
127                         while(p) {
128                                 if (x == p->object) {
129                                         fprintf(stderr, "$ref recursive. Was: %s\n", json_object_get_string(o));
130                                         exit(1);
131                                 }
132                                 p = p->upper;
133                         }
134                         /* cool found, return a new instance of the target */
135                         return json_object_get(x);
136                 }
137                 /* no, expand the values */
138                 ji = json_object_iter_begin(path.object);
139                 jn = json_object_iter_end(path.object);
140                 while (!json_object_iter_equal(&ji, &jn)) {
141                         o = json_object_iter_peek_value(&ji);
142                         x = expand_$ref((struct path){ .object = o, .upper = &path });
143                         if (x != o)
144                                 json_object_object_add(path.object, json_object_iter_peek_name(&ji), x);
145                         json_object_iter_next(&ji);
146                 }
147                 break;
148         case json_type_array:
149                 /* expand the values of arrays */
150                 i = 0;
151                 n = json_object_array_length(path.object);
152                 while (i != n) {
153                         o = json_object_array_get_idx(path.object, i);
154                         x = expand_$ref((struct path){ .object = o, .upper = &path });
155                         if (x != o)
156                                 json_object_array_put_idx(path.object, i, x);
157                         i++;
158                 }
159                 break;
160         default:
161                 /* otherwise no expansion */
162                 break;
163         }
164
165         /* return the given node */
166         return path.object;
167 }
168
169
170 char *make_desc(struct json_object *o)
171 {
172         const char *a, *b;
173         char *desc, c, buf[3];
174         size_t len;
175         int i, pos, e;
176
177         a = b = json_object_to_json_string_ext(root, 0);
178         len = 1;
179         while((c = *b++)) {
180                 len += 1 + ('"' == c);
181         }
182
183         len += 7 * (1 + len / 72);
184         desc = malloc(len);
185         oom(desc);
186
187         len = pos = 0;
188         b = a;
189         while((c = *b++)) {
190                 if (c == '"') {
191                         buf[0] = '\\';
192                         buf[1] = '"';
193                         buf[2] = 0;
194                 }
195                 else if (c == '\\') {
196                         switch ((c = *b++)) {
197                         case 0:
198                                 b--;
199                                 break;
200                         case '/':
201                                 buf[0] = '/';
202                                 buf[1] = 0;
203                                 break;
204                         default:
205                                 buf[0] = '\\';
206                                 buf[1] = c;
207                                 buf[2] = 0;
208                                 break;
209                         }
210                 }
211                 else {
212                         buf[0] = c;
213                         buf[1] = 0;
214                 }
215                 i = e = 0;
216                 while (buf[i]) {
217                         if (pos >= 77 && !e) {
218                                 desc[len++] = '"';
219                                 desc[len++] = '\n';
220                                 pos = 0;
221                         }
222                         if (pos == 0) {
223                                 desc[len++] = ' ';
224                                 desc[len++] = ' ';
225                                 desc[len++] = ' ';
226                                 desc[len++] = ' ';
227                                 desc[len++] = '"';
228                                 pos = 5;
229                         }
230                         c = buf[i++];
231                         desc[len++] = c;
232                         e = !e && c == '\\';
233                         pos++;
234                 }
235         }
236         desc[len++] = '"';
237         desc[len++] = '\n';
238         desc[len] = 0;
239         return desc;
240 }
241
242 struct json_object *permissions_of_verb(struct json_object *obj)
243 {
244         struct json_object *x, *y;
245
246         if (json_object_object_get_ex(obj, "x-permissions", &x))
247                 return x;
248
249         if (json_object_object_get_ex(obj, "get", &x))
250                 if (json_object_object_get_ex(x, "x-permissions", &y))
251                         return y;
252
253         return NULL;
254 }
255
256 void print_perms()
257 {
258         int i, n;
259
260         n = a_perms ? json_object_array_length(a_perms) : 0;
261         if (n) {
262                 printf("static const struct afb_auth _afb_auths_v2_%s[] = {\n" , api);
263                 i = 0;
264                 while (i < n) {
265                         printf("\t{ %s }", json_object_get_string(json_object_array_get_idx(a_perms, i)));
266                         printf(",\n"+(++i == n));
267                 }
268                 printf("};\n\n");
269         }
270 }
271
272 struct json_object *new_perm(struct json_object *obj, const char *desc)
273 {
274         const char *tag;
275         char *b;
276         struct json_object *x, *y;
277
278         tag = obj ? json_object_to_json_string_ext(obj, 0) : desc;
279         if (!json_object_object_get_ex(d_perms, tag, &y)) {
280                 if (!d_perms) {
281                         d_perms = json_object_new_object();
282                         a_perms = json_object_new_array();
283                 }       
284
285                 asprintf(&b, "&_afb_auths_v2_%s[%d]", api, json_object_array_length(a_perms));
286                 x = json_object_new_string(desc);
287                 y = json_object_new_string(b);
288                 json_object_array_add(a_perms, x);
289                 json_object_object_add(d_perms, tag, y);
290                 free(b);
291         }
292         return y;
293 }
294
295 struct json_object *decl_perm(struct json_object *obj);
296
297 struct json_object *decl_perm_a(const char *op, struct json_object *obj)
298 {
299         int i, n;
300         char *a;
301         struct json_object *x, *y;
302
303         x = NULL;
304         i = n = obj ? json_object_array_length(obj) : 0;
305         while (i) {
306                 y = decl_perm(json_object_array_get_idx(obj, --i));
307                 if (!y)
308                         ;
309                 else if (!x)
310                         x = y;
311                 else if (x != y) {
312                         asprintf(&a, ".type = afb_auth_%s, .first = %s, .next = %s",
313                                  op, json_object_get_string(y), json_object_get_string(x));
314                         x = new_perm(NULL, a);
315                         free(a);
316                 }
317         }
318         return x;
319 }
320
321 struct json_object *decl_perm(struct json_object *obj)
322 {
323         char *a;
324         struct json_object *x, *y;
325
326         if (json_object_object_get_ex(d_perms, json_object_to_json_string_ext(obj, 0), &x))
327                 return x;
328
329         if (json_object_object_get_ex(obj, "permission", &x)) {
330                 asprintf(&a, ".type = afb_auth_Permission, .text = \"%s\"", json_object_get_string(x));
331                 y = new_perm(obj, a);
332                 free(a);
333         }
334         else if (json_object_object_get_ex(obj, "anyOf", &x)) {
335                 y = decl_perm_a("Or", x);
336         }
337         else if (json_object_object_get_ex(obj, "allOf", &x)) {
338                 y = decl_perm_a("And", x);
339         }
340         else if (json_object_object_get_ex(obj, "not", &x)) {
341                 x = decl_perm(x);
342                 asprintf(&a, ".type = afb_auth_Not, .first = %s", json_object_get_string(x));
343                 y = new_perm(obj, a);
344                 free(a);
345         }
346         else if (json_object_object_get_ex(obj, "LOA", &x))
347                 y = NULL;
348         else if (json_object_object_get_ex(obj, "session", &x))
349                 y = NULL;
350         else
351                 y = NULL;
352
353         return y;
354 }
355
356 void declare_permissions(const char *name, struct json_object *obj)
357 {
358         struct json_object *p;
359
360         p = permissions_of_verb(obj);
361         if (p)
362                 decl_perm(p);
363 }
364
365
366 #define SESSION_CLOSE  0x000001
367 #define SESSION_RENEW  0x000010
368 #define SESSION_CHECK  0x000100
369 #define SESSION_LOA_1  0x001000
370 #define SESSION_LOA_2  0x011000
371 #define SESSION_LOA_3  0x111000
372 #define SESSION_MASK   0x111111
373
374
375 int get_session(struct json_object *obj);
376
377 int get_session_a(int and, struct json_object *obj)
378 {
379         int i, n, x, y;
380
381         n = obj ? json_object_array_length(obj) : 0;
382         if (n == 0)
383                 return 0;
384
385         i = n;
386         x = get_session(json_object_array_get_idx(obj, --i));
387         while (i) {
388                 y = get_session(json_object_array_get_idx(obj, --i));
389                 if (and)
390                         x &= y;
391                 else
392                         x |= y;
393         }
394         return x;
395 }
396
397 int get_session(struct json_object *obj)
398 {
399         int y;
400         const char *a;
401         struct json_object *x;
402
403         y = 0;
404         if (json_object_object_get_ex(obj, "anyOf", &x)) {
405                 y = get_session_a(1, x);
406         }
407         else if (json_object_object_get_ex(obj, "allOf", &x)) {
408                 y = get_session_a(0, x);
409         }
410         else if (json_object_object_get_ex(obj, "not", &x)) {
411                 y = ~get_session(x) & SESSION_MASK;
412         }
413         else if (json_object_object_get_ex(obj, "LOA", &x)) {
414                 switch (json_object_get_int(x)) {
415                 case 3: y = SESSION_LOA_3; break;
416                 case 2: y = SESSION_LOA_2; break;
417                 case 1: y = SESSION_LOA_1; break;
418                 default: break;
419                 }
420         }
421         else if (json_object_object_get_ex(obj, "session", &x)) {
422                 a = json_object_get_string(x);
423                 if (!strcmp(a, "check"))
424                         y = SESSION_CHECK;
425                 else if (!strcmp(a, "close"))
426                         y = SESSION_CLOSE;
427         }
428         else if (json_object_object_get_ex(obj, "token", &x)) {
429                 a = json_object_get_string(x);
430                 if (!strcmp(a, "refresh"))
431                         y = SESSION_RENEW;
432         }
433
434         return y;
435 }
436
437 void print_session(struct json_object *p)
438 {
439         int s, c, l;
440
441         s = p ? get_session(p) : 0;
442         c = 1;
443         if (s & SESSION_CHECK) {
444                 printf("%s", "|AFB_SESSION_CHECK_V2" + c);
445                 c = 0;
446         }
447         if (s & SESSION_LOA_3 & ~SESSION_LOA_2)
448                 l = 3;
449         else if (s & SESSION_LOA_2 & ~SESSION_LOA_1)
450                 l = 2;
451         else if (s & SESSION_LOA_1)
452                 l = 1;
453         else
454                 l = 0;
455         if (l) {
456                 printf("%s%d_V2", "|AFB_SESSION_LOA_" + c, l);
457                 c = 0;
458         }
459         if (s & SESSION_CLOSE) {
460                 printf("%s", "|AFB_SESSION_CLOSE_V2" + c);
461                 c = 0;
462         }
463         if (s & SESSION_RENEW) {
464                 printf("%s", "|AFB_SESSION_REFRESH_V2" + c);
465                 c = 0;
466         }
467         if (c)
468                 printf("AFB_SESSION_NONE_V2");
469 }
470
471 void print_verb(const char *name)
472 {
473         printf("%s%s%s" , prefix, name, postfix);
474 }
475
476 void print_declare_verb(const char *name, struct json_object *obj)
477 {
478         printf("%s void ", scope);
479         print_verb(name);
480         printf("(struct afb_req req);\n");
481 }
482
483 void print_struct_verb(const char *name, struct json_object *obj)
484 {
485         struct json_object *p;
486
487         p = permissions_of_verb(obj);
488         printf(
489                 "    {\n"
490                 "        .verb = \"%s\",\n"
491                 "        .callback = "
492                 , name
493         );
494         print_verb(name);
495         printf(
496                 ",\n"
497                 "        .auth = %s,\n"
498                 "        .session = "
499                 , p ? json_object_get_string(decl_perm(p)) : "NULL"
500         );
501         print_session(p);
502         printf(
503                 "\n"
504                 "    },\n"
505         );
506 }
507
508 void enum_verbs(void (*func)(const char *name, struct json_object *obj))
509 {
510         struct json_object_iterator ji, jn;
511         struct json_object *paths, *obj;
512         const char *name;
513
514         /* search the verbs */
515         paths = search("#/paths");
516         if (!paths)
517                 return;
518
519         /* list the verbs and sort it */
520         ji = json_object_iter_begin(paths);
521         jn = json_object_iter_end(paths);
522         while (!json_object_iter_equal(&ji, &jn)) {
523                 name = json_object_iter_peek_name(&ji);
524                 obj = json_object_iter_peek_value(&ji);
525                 name += (*name == '/');
526                 func(name, obj);
527                 json_object_iter_next(&ji);
528         }
529 }
530
531 void getvarbool(int *var, const char *path, int defval)
532 {
533         struct json_object *o;
534
535         if (*var != 0 && *var != 1) {
536                 o = search(path);
537                 if (o && json_object_is_type(o, json_type_boolean))
538                         *var = json_object_get_boolean(o);
539                 else
540                         *var = !!defval;
541         }
542 }
543
544 void getvar(const char **var, const char *path, const char *defval)
545 {
546         struct json_object *o;
547
548         if (!*var) {
549                 o = search(path);
550                 if (o && json_object_is_type(o, json_type_string))
551                         *var = json_object_get_string(o);
552                 else
553                         *var = defval;
554         }
555 }
556
557 /**
558  * process a file and prints its expansion on stdout
559  */
560 void process(char *filename)
561 {
562         char *desc;
563
564         /* translate - */
565         if (!strcmp(filename, "-"))
566                 filename = "/dev/stdin";
567
568         /* check access */
569         if (access(filename, R_OK)) {
570                 fprintf(stderr, "can't access file %s\n", filename);
571                 exit(1);
572         }
573
574         /* read the file */
575         root = json_object_from_file(filename);
576         if (!root) {
577                 fprintf(stderr, "reading file %s produced null\n", filename);
578                 exit(1);
579         }
580
581         /* create the description */
582         desc = make_desc(root);
583
584         /* expand references */
585         root = expand_$ref((struct path){ .object = root, .upper = NULL });
586
587         /* get some names */
588         getvar(&api, "#/info/x-binding-c-generator/api", NULL);
589         getvar(&init, "#/info/x-binding-c-generator/init", NULL);
590         getvar(&start, "#/info/x-binding-c-generator/start", NULL);
591         getvar(&onevent, "#/info/x-binding-c-generator/onevent", NULL);
592         getvar(&scope, "#/info/x-binding-c-generator/scope", "static");
593         getvar(&prefix, "#/info/x-binding-c-generator/prefix", "afb_verb_");
594         getvar(&postfix, "#/info/x-binding-c-generator/postfix", "_cb");
595         getvarbool(&priv, "#/info/x-binding-c-generator/private", 0);
596         getvar(&api, "#/info/title", "?");
597
598         /* get the API name */
599         printf(
600                 "\n"
601                 "static const char _afb_description_v2_%s[] =\n"
602                 "%s"
603                 ";\n"
604                 "\n"
605                 , api, desc
606         );
607         enum_verbs(declare_permissions);
608         print_perms();
609         enum_verbs(print_declare_verb);
610         printf(
611                 "\n"
612                 "static const struct afb_verb_v2 _afb_verbs_v2_%s[] = {\n"
613                 , api
614         );
615         enum_verbs(print_struct_verb);
616         printf(
617                 "    { .verb = NULL }\n"
618                 "};\n"
619         );
620         printf(
621                 "\n"
622                 "%sconst struct afb_binding_v2 %s%s = {\n"
623                 "    .api = \"%s\",\n"
624                 "    .specification = _afb_description_v2_%s,\n"
625                 "    .verbs = _afb_verbs_v2_%s,\n"
626                 "    .init = %s,\n"
627                 "    .start = %s,\n"
628                 "    .onevent = %s,\n"
629                 "};\n"
630                 "\n"
631                 , priv ? "static " : ""
632                 , priv ? "_afb_binding_v2_" : "afbBindingV2"
633                 , priv ? api : ""
634                 , api
635                 , api
636                 , api
637                 , init ?: "NULL"
638                 , start ?: "NULL"
639                 , onevent ?: "NULL"
640         );
641
642         /* clean up */
643         json_object_put(root);
644         free(desc);
645 }
646
647 /** process the list of files or stdin if none */
648 int main(int ac, char **av)
649 {
650         if (!*++av)
651                 process("-");
652         else {
653                 do { process(*av); } while(*++av);
654         }       
655         return 0;
656 }
657
658
659
660
661