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