1 /*
2  * Copyright (C) 2011      Citrix Ltd.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; version 2.1 only. with the special
7  * exception on linking described in file LICENSE.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  */
14 
15 #include "libxl_osdeps.h" /* must come before any other headers */
16 
17 #include <math.h>
18 
19 #include <yajl/yajl_parse.h>
20 #include <yajl/yajl_gen.h>
21 
22 #include "libxl_internal.h"
23 
24 /* #define DEBUG_ANSWER */
25 
26 struct libxl__yajl_ctx {
27     libxl__gc *gc;
28     yajl_handle hand;
29     libxl__json_object *head;
30     libxl__json_object *current;
31 #ifdef DEBUG_ANSWER
32     yajl_gen g;
33 #endif
34 };
35 
36 #ifdef DEBUG_ANSWER
37 #if YAJL_VERSION < 20000
38 #  define DEBUG_GEN_ALLOC(ctx)                  \
39     if ((ctx)->g == NULL) {                     \
40         yajl_gen_config conf = { 1, "  " };     \
41         (ctx)->g = yajl_gen_alloc(&conf, NULL); \
42     }
43 #else  /* YAJL2 */
44 #  define DEBUG_GEN_ALLOC(ctx)                                    \
45     if ((ctx)->g == NULL) {                                       \
46         (ctx)->g = yajl_gen_alloc(NULL);                          \
47         yajl_gen_config((ctx)->g, yajl_gen_beautify, 1);          \
48         yajl_gen_config((ctx)->g, yajl_gen_indent_string, "  ");  \
49     }
50 #endif
51 #  define DEBUG_GEN_FREE(ctx) \
52     if ((ctx)->g) yajl_gen_free((ctx)->g)
53 #  define DEBUG_GEN(ctx, type)              yajl_gen_##type(ctx->g)
54 #  define DEBUG_GEN_VALUE(ctx, type, value) yajl_gen_##type(ctx->g, value)
55 #  define DEBUG_GEN_STRING(ctx, str, n)     yajl_gen_string(ctx->g, str, n)
56 #  define DEBUG_GEN_NUMBER(ctx, str, n)     yajl_gen_number(ctx->g, str, n)
57 #  define DEBUG_GEN_REPORT(yajl_ctx) \
58     do { \
59         const unsigned char *buf = NULL; \
60         size_t len = 0; \
61         yajl_gen_get_buf((yajl_ctx)->g, &buf, &len); \
62         LIBXL__LOG(libxl__gc_owner((yajl_ctx)->gc), LIBXL__LOG_DEBUG,
63 		   "response:\n", buf); \
64         yajl_gen_free((yajl_ctx)->g); \
65         (yajl_ctx)->g = NULL; \
66     } while (0)
67 #else
68 #  define DEBUG_GEN_ALLOC(ctx)                  ((void)0)
69 #  define DEBUG_GEN_FREE(ctx)                   ((void)0)
70 #  define DEBUG_GEN(ctx, type)                  ((void)0)
71 #  define DEBUG_GEN_VALUE(ctx, type, value)     ((void)0)
72 #  define DEBUG_GEN_STRING(ctx, value, length)  ((void)0)
73 #  define DEBUG_GEN_NUMBER(ctx, value, length)  ((void)0)
74 #  define DEBUG_GEN_REPORT(ctx)                 ((void)0)
75 #endif
76 
77 /*
78  * YAJL Helper
79  */
80 
81 yajl_gen_status libxl__yajl_gen_asciiz(yajl_gen hand, const char *str)
82 {
83     return yajl_gen_string(hand, (const unsigned char *)str, strlen(str));
84 }
85 
86 yajl_gen_status libxl__yajl_gen_enum(yajl_gen hand, const char *str)
87 {
88     if (str)
89         return libxl__yajl_gen_asciiz(hand, str);
90     else
91         return yajl_gen_null(hand);
92 }
93 
94 /*
95  * YAJL generators for builtin libxl types.
96  */
97 yajl_gen_status libxl_defbool_gen_json(yajl_gen hand,
98                                        libxl_defbool *db)
99 {
100     return libxl__yajl_gen_asciiz(hand, libxl_defbool_to_string(*db));
101 }
102 
103 int libxl__defbool_parse_json(libxl__gc *gc, const libxl__json_object *o,
104                               libxl_defbool *p)
105 {
106     const char *s;
107 
108     if (!libxl__json_object_is_string(o))
109         return ERROR_FAIL;
110 
111     s = libxl__json_object_get_string(o);
112 
113     if (!strncmp(s, LIBXL__DEFBOOL_STR_DEFAULT,
114                  strlen(LIBXL__DEFBOOL_STR_DEFAULT)))
115         p->val = LIBXL__DEFBOOL_DEFAULT;
116     else if (!strncmp(s, LIBXL__DEFBOOL_STR_TRUE,
117                       strlen(LIBXL__DEFBOOL_STR_TRUE)))
118         p->val = LIBXL__DEFBOOL_TRUE;
119     else if (!strncmp(s, LIBXL__DEFBOOL_STR_FALSE,
120                       strlen(LIBXL__DEFBOOL_STR_FALSE)))
121         p->val = LIBXL__DEFBOOL_FALSE;
122     else
123         return ERROR_FAIL;
124 
125     return 0;
126 }
127 
128 int libxl__bool_parse_json(libxl__gc *gc, const libxl__json_object *o,
129                            bool *p)
130 {
131     if (!libxl__json_object_is_bool(o))
132         return ERROR_FAIL;
133 
134     *p = libxl__json_object_get_bool(o);
135 
136     return 0;
137 }
138 
139 yajl_gen_status libxl_uuid_gen_json(yajl_gen hand,
140                                     libxl_uuid *uuid)
141 {
142     char buf[LIBXL_UUID_FMTLEN+1];
143     snprintf(buf, sizeof(buf), LIBXL_UUID_FMT, LIBXL_UUID_BYTES((*uuid)));
144     return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_UUID_FMTLEN);
145 }
146 
147 int libxl__uuid_parse_json(libxl__gc *gc, const libxl__json_object *o,
148                            libxl_uuid *p)
149 {
150     if (!libxl__json_object_is_string(o))
151         return ERROR_FAIL;
152 
153     return libxl_uuid_from_string(p, o->u.string);
154 }
155 
156 yajl_gen_status libxl_bitmap_gen_json(yajl_gen hand,
157                                       libxl_bitmap *bitmap)
158 {
159     yajl_gen_status s;
160     int i;
161 
162     s = yajl_gen_array_open(hand);
163     if (s != yajl_gen_status_ok) goto out;
164 
165     libxl_for_each_bit(i, *bitmap) {
166         if (libxl_bitmap_test(bitmap, i)) {
167             s = yajl_gen_integer(hand, i);
168             if (s != yajl_gen_status_ok) goto out;
169         }
170     }
171     s = yajl_gen_array_close(hand);
172 out:
173     return s;
174 }
175 
176 int libxl__bitmap_parse_json(libxl__gc *gc, const libxl__json_object *o,
177                             libxl_bitmap *p)
178 {
179     int i;
180     int size;
181     const libxl__json_object *t;
182     flexarray_t *array;
183 
184     if (!libxl__json_object_is_array(o))
185         return ERROR_FAIL;
186 
187     array = libxl__json_object_get_array(o);
188     if (!array->count) {
189         libxl_bitmap_init(p);
190         return 0;
191     }
192 
193     t = libxl__json_array_get(o, array->count - 1);
194     if (!libxl__json_object_is_integer(t))
195         return ERROR_FAIL;
196     size = libxl__json_object_get_integer(t) + 1;
197 
198     libxl_bitmap_alloc(CTX, p, size);
199 
200     for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
201         if (!libxl__json_object_is_integer(t))
202             return ERROR_FAIL;
203 
204         libxl_bitmap_set(p, libxl__json_object_get_integer(t));
205     }
206 
207     return 0;
208 }
209 
210 yajl_gen_status libxl_key_value_list_gen_json(yajl_gen hand,
211                                               libxl_key_value_list *pkvl)
212 {
213     libxl_key_value_list kvl = *pkvl;
214     yajl_gen_status s;
215     int i;
216 
217     s = yajl_gen_map_open(hand);
218     if (s != yajl_gen_status_ok) goto out;
219 
220     if (!kvl) goto empty;
221 
222     for (i = 0; kvl[i] != NULL; i += 2) {
223         s = libxl__yajl_gen_asciiz(hand, kvl[i]);
224         if (s != yajl_gen_status_ok) goto out;
225         if (kvl[i + 1])
226             s = libxl__yajl_gen_asciiz(hand, kvl[i+1]);
227         else
228             s = yajl_gen_null(hand);
229         if (s != yajl_gen_status_ok) goto out;
230     }
231 empty:
232     s = yajl_gen_map_close(hand);
233 out:
234     return s;
235 }
236 
237 int libxl__key_value_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
238                                      libxl_key_value_list *p)
239 {
240     libxl__json_map_node *node = NULL;
241     flexarray_t *maps = NULL;
242     int i, size;
243     libxl_key_value_list kvl;
244 
245     if (!libxl__json_object_is_map(o))
246         return ERROR_FAIL;
247 
248     maps = libxl__json_object_get_map(o);
249     size = maps->count * 2;
250     kvl = *p = libxl__calloc(NOGC, size+1, sizeof(char *));
251 
252     for (i = 0; i < maps->count; i++) {
253         int idx = i * 2;
254         if (flexarray_get(maps, i, (void**)&node) != 0)
255             return ERROR_FAIL;
256 
257         if (!libxl__json_object_is_string(node->obj) &&
258             !libxl__json_object_is_null(node->obj))
259             return ERROR_FAIL;
260 
261         kvl[idx] = libxl__strdup(NOGC, node->map_key);
262         if (libxl__json_object_is_string(node->obj))
263             kvl[idx+1] =
264                 libxl__strdup(NOGC, libxl__json_object_get_string(node->obj));
265         else
266             kvl[idx+1] = NULL;
267     }
268 
269     return 0;
270 }
271 
272 yajl_gen_status libxl_string_list_gen_json(yajl_gen hand, libxl_string_list *pl)
273 {
274     libxl_string_list l = *pl;
275     yajl_gen_status s;
276     int i;
277 
278     s = yajl_gen_array_open(hand);
279     if (s != yajl_gen_status_ok) goto out;
280 
281     if (!l) goto empty;
282 
283     for (i = 0; l[i] != NULL; i++) {
284         s = libxl__yajl_gen_asciiz(hand, l[i]);
285         if (s != yajl_gen_status_ok) goto out;
286     }
287 empty:
288     s = yajl_gen_array_close(hand);
289 out:
290     return s;
291 }
292 
293 int libxl__string_list_parse_json(libxl__gc *gc, const libxl__json_object *o,
294                                   libxl_string_list *p)
295 {
296     const libxl__json_object *t;
297     libxl_string_list l;
298     flexarray_t *array = NULL;
299     int i, size;
300 
301     if (!libxl__json_object_is_array(o))
302         return ERROR_FAIL;
303 
304     array = libxl__json_object_get_array(o);
305     size = array->count;
306 
307     if (size == 0) {
308         *p = NULL;
309         return 0;
310     }
311 
312     /* need one extra slot as sentinel */
313     l = *p = libxl__calloc(NOGC, size + 1, sizeof(char *));
314 
315     for (i = 0; (t = libxl__json_array_get(o, i)); i++) {
316         if (!libxl__json_object_is_string(t))
317             return ERROR_FAIL;
318 
319         l[i] = libxl__strdup(NOGC, libxl__json_object_get_string(t));
320     }
321 
322     return 0;
323 }
324 
325 yajl_gen_status libxl_mac_gen_json(yajl_gen hand, libxl_mac *mac)
326 {
327     char buf[LIBXL_MAC_FMTLEN+1];
328     snprintf(buf, sizeof(buf), LIBXL_MAC_FMT, LIBXL_MAC_BYTES((*mac)));
329     return yajl_gen_string(hand, (const unsigned char *)buf, LIBXL_MAC_FMTLEN);
330 }
331 
332 int libxl__mac_parse_json(libxl__gc *gc, const libxl__json_object *o,
333                           libxl_mac *p)
334 {
335     if (!libxl__json_object_is_string(o))
336         return ERROR_FAIL;
337 
338     return libxl__parse_mac(libxl__json_object_get_string(o), *p);
339 }
340 
341 yajl_gen_status libxl_hwcap_gen_json(yajl_gen hand,
342                                      libxl_hwcap *p)
343 {
344     yajl_gen_status s;
345     int i;
346 
347     s = yajl_gen_array_open(hand);
348     if (s != yajl_gen_status_ok) goto out;
349 
350     for(i=0; i<4; i++) {
351         s = yajl_gen_integer(hand, (*p)[i]);
352         if (s != yajl_gen_status_ok) goto out;
353     }
354     s = yajl_gen_array_close(hand);
355 out:
356     return s;
357 }
358 
359 int libxl__hwcap_parse_json(libxl__gc *gc, const libxl__json_object *o,
360                             libxl_hwcap *p)
361 {
362     int i;
363 
364     if (!libxl__json_object_is_array(o))
365         return ERROR_FAIL;
366 
367     for (i = 0; i<4; i++) {
368         const libxl__json_object *t;
369 
370         t = libxl__json_array_get(o, i);
371         if (!t || !libxl__json_object_is_integer(t))
372             return ERROR_FAIL;
373 
374         (*p)[i] = libxl__json_object_get_integer(t);
375     }
376 
377     return 0;
378 }
379 
380 yajl_gen_status libxl_ms_vm_genid_gen_json(yajl_gen hand, libxl_ms_vm_genid *p)
381 {
382     yajl_gen_status s;
383     int i;
384 
385     s = yajl_gen_array_open(hand);
386     if (s != yajl_gen_status_ok)
387         return s;
388 
389     for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
390         s = yajl_gen_integer(hand, p->bytes[i]);
391         if (s != yajl_gen_status_ok)
392             return s;
393     }
394 
395     return yajl_gen_array_close(hand);
396 }
397 
398 int libxl__ms_vm_genid_parse_json(libxl__gc *gc, const libxl__json_object *o,
399                                   libxl_ms_vm_genid *p)
400 {
401     unsigned int i;
402 
403     if (!libxl__json_object_is_array(o))
404         return ERROR_FAIL;
405 
406     for (i = 0; i < LIBXL_MS_VM_GENID_LEN; i++) {
407         const libxl__json_object *t;
408 
409         t = libxl__json_array_get(o, i);
410         if (!t || !libxl__json_object_is_integer(t))
411             return ERROR_FAIL;
412 
413         p->bytes[i] = libxl__json_object_get_integer(t);
414     }
415 
416     return 0;
417 }
418 
419 yajl_gen_status libxl__string_gen_json(yajl_gen hand,
420                                        const char *p)
421 {
422     if (p)
423         return libxl__yajl_gen_asciiz(hand, p);
424     else
425         return yajl_gen_null(hand);
426 }
427 
428 int libxl__string_parse_json(libxl__gc *gc, const libxl__json_object *o,
429                              char **p)
430 {
431     if (!libxl__json_object_is_string(o) && !libxl__json_object_is_null(o))
432         return ERROR_FAIL;
433 
434     if (libxl__json_object_is_null(o))
435         *p = NULL;
436     else
437         *p = libxl__strdup(NOGC, libxl__json_object_get_string(o));
438 
439     return 0;
440 }
441 
442 /*
443  * libxl__json_object helper functions
444  */
445 
446 libxl__json_object *libxl__json_object_alloc(libxl__gc *gc,
447                                              libxl__json_node_type type)
448 {
449     libxl__json_object *obj;
450 
451     obj = libxl__zalloc(gc, sizeof(*obj));
452 
453     obj->type = type;
454 
455     if (type == JSON_MAP || type == JSON_ARRAY) {
456         flexarray_t *array = flexarray_make(gc, 1, 1);
457         if (type == JSON_MAP)
458             obj->u.map = array;
459         else
460             obj->u.array = array;
461     }
462 
463     return obj;
464 }
465 
466 int libxl__json_object_append_to(libxl__gc *gc, libxl__json_object *obj,
467                                  libxl__yajl_ctx *ctx)
468 {
469     libxl__json_object *dst = ctx->current;
470 
471     if (dst) {
472         switch (dst->type) {
473         case JSON_MAP: {
474             libxl__json_map_node *last;
475 
476             if (dst->u.map->count == 0) {
477                 LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
478                            "Try to add a value to an empty map (with no key)");
479                 return ERROR_FAIL;
480             }
481             flexarray_get(dst->u.map, dst->u.map->count - 1, (void**)&last);
482             last->obj = obj;
483             break;
484         }
485         case JSON_ARRAY:
486             flexarray_append(dst->u.array, obj);
487             break;
488         default:
489             LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR,
490                        "Try append an object is not a map/array (%i)",
491                        dst->type);
492             return ERROR_FAIL;
493         }
494     }
495 
496     obj->parent = dst;
497 
498     if (libxl__json_object_is_map(obj) || libxl__json_object_is_array(obj))
499         ctx->current = obj;
500     if (ctx->head == NULL)
501         ctx->head = obj;
502 
503     return 0;
504 }
505 
506 void libxl__json_object_free(libxl__gc *gc, libxl__json_object *obj)
507 {
508     int idx = 0;
509 
510     if (obj == NULL)
511         return;
512     switch (obj->type) {
513     case JSON_STRING:
514     case JSON_NUMBER:
515         free(obj->u.string);
516         break;
517     case JSON_MAP: {
518         libxl__json_map_node *node = NULL;
519 
520         for (idx = 0; idx < obj->u.map->count; idx++) {
521             if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
522                 break;
523             libxl__json_object_free(gc, node->obj);
524             free(node->map_key);
525             free(node);
526             node = NULL;
527         }
528         flexarray_free(obj->u.map);
529         break;
530     }
531     case JSON_ARRAY: {
532         libxl__json_object *node = NULL;
533 
534         for (idx = 0; idx < obj->u.array->count; idx++) {
535             if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
536                 break;
537             libxl__json_object_free(gc, node);
538             node = NULL;
539         }
540         flexarray_free(obj->u.array);
541         break;
542     }
543     default:
544         break;
545     }
546     free(obj);
547 }
548 
549 libxl__json_object *libxl__json_array_get(const libxl__json_object *o, int i)
550 {
551     flexarray_t *array = NULL;
552     libxl__json_object *obj = NULL;
553 
554     if ((array = libxl__json_object_get_array(o)) == NULL) {
555         return NULL;
556     }
557 
558     if (i >= array->count)
559         return NULL;
560 
561     if (flexarray_get(array, i, (void**)&obj) != 0)
562         return NULL;
563 
564     return obj;
565 }
566 
567 libxl__json_map_node *libxl__json_map_node_get(const libxl__json_object *o,
568                                                int i)
569 {
570     flexarray_t *array = NULL;
571     libxl__json_map_node *obj = NULL;
572 
573     if ((array = libxl__json_object_get_map(o)) == NULL) {
574         return NULL;
575     }
576 
577     if (i >= array->count)
578         return NULL;
579 
580     if (flexarray_get(array, i, (void**)&obj) != 0)
581         return NULL;
582 
583     return obj;
584 }
585 
586 const libxl__json_object *libxl__json_map_get(const char *key,
587                                           const libxl__json_object *o,
588                                           libxl__json_node_type expected_type)
589 {
590     flexarray_t *maps = NULL;
591     int idx = 0;
592 
593     if (libxl__json_object_is_map(o)) {
594         libxl__json_map_node *node = NULL;
595 
596         maps = o->u.map;
597         for (idx = 0; idx < maps->count; idx++) {
598             if (flexarray_get(maps, idx, (void**)&node) != 0)
599                 return NULL;
600             if (strcmp(key, node->map_key) == 0) {
601                 if (expected_type == JSON_ANY
602                     || (node->obj && (node->obj->type & expected_type))) {
603                     return node->obj;
604                 } else {
605                     return NULL;
606                 }
607             }
608         }
609     }
610     return NULL;
611 }
612 
613 yajl_status libxl__json_object_to_yajl_gen(libxl__gc *gc,
614                                            yajl_gen hand,
615                                            libxl__json_object *obj)
616 {
617     int idx = 0;
618     yajl_status rc;
619 
620 #define CONVERT_YAJL_GEN_TO_STATUS(gen) \
621     ((gen) == yajl_gen_status_ok ? yajl_status_ok : yajl_status_error)
622 
623     switch (obj->type) {
624     case JSON_NULL:
625         return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_null(hand));
626     case JSON_BOOL:
627         return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_bool(hand, obj->u.b));
628     case JSON_INTEGER:
629         return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_integer(hand, obj->u.i));
630     case JSON_DOUBLE:
631         return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_double(hand, obj->u.d));
632     case JSON_NUMBER:
633         return CONVERT_YAJL_GEN_TO_STATUS(
634                   yajl_gen_number(hand, obj->u.string, strlen(obj->u.string)));
635     case JSON_STRING:
636         return CONVERT_YAJL_GEN_TO_STATUS(
637                         libxl__yajl_gen_asciiz(hand, obj->u.string));
638     case JSON_MAP: {
639         libxl__json_map_node *node = NULL;
640 
641         rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_open(hand));
642         if (rc != yajl_status_ok)
643             return rc;
644         for (idx = 0; idx < obj->u.map->count; idx++) {
645             if (flexarray_get(obj->u.map, idx, (void**)&node) != 0)
646                 break;
647 
648             rc = CONVERT_YAJL_GEN_TO_STATUS(
649                             libxl__yajl_gen_asciiz(hand, node->map_key));
650             if (rc != yajl_status_ok)
651                 return rc;
652             rc = libxl__json_object_to_yajl_gen(gc, hand, node->obj);
653             if (rc != yajl_status_ok)
654                 return rc;
655         }
656         return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_map_close(hand));
657     }
658     case JSON_ARRAY: {
659         libxl__json_object *node = NULL;
660 
661         rc = CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_open(hand));
662         if (rc != yajl_status_ok)
663             return rc;
664         for (idx = 0; idx < obj->u.array->count; idx++) {
665             if (flexarray_get(obj->u.array, idx, (void**)&node) != 0)
666                 break;
667             rc = libxl__json_object_to_yajl_gen(gc, hand, node);
668             if (rc != yajl_status_ok)
669                 return rc;
670         }
671         return CONVERT_YAJL_GEN_TO_STATUS(yajl_gen_array_close(hand));
672     }
673     case JSON_ANY:
674         /* JSON_ANY is not a valid value for obj->type. */
675         ;
676     }
677     abort();
678 #undef CONVERT_YAJL_GEN_TO_STATUS
679 }
680 
681 
682 /*
683  * JSON callbacks
684  */
685 
686 static int json_callback_null(void *opaque)
687 {
688     libxl__yajl_ctx *ctx = opaque;
689     libxl__json_object *obj;
690 
691     DEBUG_GEN(ctx, null);
692 
693     obj = libxl__json_object_alloc(ctx->gc, JSON_NULL);
694 
695     if (libxl__json_object_append_to(ctx->gc, obj, ctx))
696         return 0;
697 
698     return 1;
699 }
700 
701 static int json_callback_boolean(void *opaque, int boolean)
702 {
703     libxl__yajl_ctx *ctx = opaque;
704     libxl__json_object *obj;
705 
706     DEBUG_GEN_VALUE(ctx, bool, boolean);
707 
708     obj = libxl__json_object_alloc(ctx->gc, JSON_BOOL);
709     obj->u.b = boolean;
710 
711     if (libxl__json_object_append_to(ctx->gc, obj, ctx))
712         return 0;
713 
714     return 1;
715 }
716 
717 static bool is_decimal(const char *s, unsigned len)
718 {
719     const char *end = s + len;
720     for (; s < end; s++) {
721         if (*s == '.')
722             return true;
723     }
724     return false;
725 }
726 
727 static int json_callback_number(void *opaque, const char *s, libxl_yajl_length len)
728 {
729     libxl__yajl_ctx *ctx = opaque;
730     libxl__json_object *obj = NULL;
731     char *t = NULL;
732 
733     DEBUG_GEN_NUMBER(ctx, s, len);
734 
735     if (is_decimal(s, len)) {
736         double d = strtod(s, NULL);
737 
738         if ((d == HUGE_VALF || d == HUGE_VALL) && errno == ERANGE) {
739             goto error;
740         }
741 
742         obj = libxl__json_object_alloc(ctx->gc, JSON_DOUBLE);
743         obj->u.d = d;
744     } else {
745         long long i = strtoll(s, NULL, 10);
746 
747         if ((i == LLONG_MIN || i == LLONG_MAX) && errno == ERANGE) {
748             goto error;
749         }
750 
751         obj = libxl__json_object_alloc(ctx->gc, JSON_INTEGER);
752         obj->u.i = i;
753     }
754     goto out;
755 
756 error:
757     /* If the conversion fail, we just store the original string. */
758     obj = libxl__json_object_alloc(ctx->gc, JSON_NUMBER);
759 
760     t = libxl__zalloc(ctx->gc, len + 1);
761     strncpy(t, s, len);
762     t[len] = 0;
763 
764     obj->u.string = t;
765 
766 out:
767     if (libxl__json_object_append_to(ctx->gc, obj, ctx))
768         return 0;
769 
770     return 1;
771 }
772 
773 static int json_callback_string(void *opaque, const unsigned char *str,
774                                 libxl_yajl_length len)
775 {
776     libxl__yajl_ctx *ctx = opaque;
777     char *t = NULL;
778     libxl__json_object *obj = NULL;
779 
780     t = libxl__zalloc(ctx->gc, len + 1);
781 
782     DEBUG_GEN_STRING(ctx, str, len);
783 
784     strncpy(t, (const char *) str, len);
785     t[len] = 0;
786 
787     obj = libxl__json_object_alloc(ctx->gc, JSON_STRING);
788     obj->u.string = t;
789 
790     if (libxl__json_object_append_to(ctx->gc, obj, ctx))
791         return 0;
792 
793     return 1;
794 }
795 
796 static int json_callback_map_key(void *opaque, const unsigned char *str,
797                                  libxl_yajl_length len)
798 {
799     libxl__yajl_ctx *ctx = opaque;
800     char *t = NULL;
801     libxl__json_object *obj = ctx->current;
802     libxl__gc *gc = ctx->gc;
803 
804     t = libxl__zalloc(gc, len + 1);
805 
806     DEBUG_GEN_STRING(ctx, str, len);
807 
808     strncpy(t, (const char *) str, len);
809     t[len] = 0;
810 
811     if (libxl__json_object_is_map(obj)) {
812         libxl__json_map_node *node;
813 
814         GCNEW(node);
815         node->map_key = t;
816         node->obj = NULL;
817 
818         flexarray_append(obj->u.map, node);
819     } else {
820         LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
821                    "Current json object is not a map");
822         return 0;
823     }
824 
825     return 1;
826 }
827 
828 static int json_callback_start_map(void *opaque)
829 {
830     libxl__yajl_ctx *ctx = opaque;
831     libxl__json_object *obj = NULL;
832 
833     DEBUG_GEN(ctx, map_open);
834 
835     obj = libxl__json_object_alloc(ctx->gc, JSON_MAP);
836 
837     if (libxl__json_object_append_to(ctx->gc, obj, ctx))
838         return 0;
839 
840     return 1;
841 }
842 
843 static int json_callback_end_map(void *opaque)
844 {
845     libxl__yajl_ctx *ctx = opaque;
846 
847     DEBUG_GEN(ctx, map_close);
848 
849     if (ctx->current) {
850         ctx->current = ctx->current->parent;
851     } else {
852         LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
853                    "No current libxl__json_object, cannot use his parent.");
854         return 0;
855     }
856 
857     return 1;
858 }
859 
860 static int json_callback_start_array(void *opaque)
861 {
862     libxl__yajl_ctx *ctx = opaque;
863     libxl__json_object *obj = NULL;
864 
865     DEBUG_GEN(ctx, array_open);
866 
867     obj = libxl__json_object_alloc(ctx->gc, JSON_ARRAY);
868 
869     if (libxl__json_object_append_to(ctx->gc, obj, ctx))
870         return 0;
871 
872     return 1;
873 }
874 
875 static int json_callback_end_array(void *opaque)
876 {
877     libxl__yajl_ctx *ctx = opaque;
878 
879     DEBUG_GEN(ctx, array_close);
880 
881     if (ctx->current) {
882         ctx->current = ctx->current->parent;
883     } else {
884         LIBXL__LOG(libxl__gc_owner(ctx->gc), LIBXL__LOG_ERROR,
885                    "No current libxl__json_object, cannot use his parent.");
886         return 0;
887     }
888 
889     return 1;
890 }
891 
892 static yajl_callbacks callbacks = {
893     json_callback_null,
894     json_callback_boolean,
895     NULL,
896     NULL,
897     json_callback_number,
898     json_callback_string,
899     json_callback_start_map,
900     json_callback_map_key,
901     json_callback_end_map,
902     json_callback_start_array,
903     json_callback_end_array
904 };
905 
906 static void yajl_ctx_free(libxl__yajl_ctx *yajl_ctx)
907 {
908     if (yajl_ctx->hand) {
909         yajl_free(yajl_ctx->hand);
910         yajl_ctx->hand = NULL;
911     }
912     DEBUG_GEN_FREE(yajl_ctx);
913 }
914 
915 libxl__json_object *libxl__json_parse(libxl__gc *gc, const char *s)
916 {
917     yajl_status status;
918     libxl__yajl_ctx yajl_ctx;
919     libxl__json_object *o = NULL;
920     unsigned char *str = NULL;
921 
922     memset(&yajl_ctx, 0, sizeof (yajl_ctx));
923     yajl_ctx.gc = gc;
924 
925     DEBUG_GEN_ALLOC(&yajl_ctx);
926 
927     if (yajl_ctx.hand == NULL) {
928         yajl_ctx.hand = libxl__yajl_alloc(&callbacks, NULL, &yajl_ctx);
929     }
930     status = yajl_parse(yajl_ctx.hand, (const unsigned char *)s, strlen(s));
931     if (status != yajl_status_ok)
932         goto out;
933 
934     status = yajl_complete_parse(yajl_ctx.hand);
935     if (status != yajl_status_ok)
936         goto out;
937 
938     o = yajl_ctx.head;
939 
940     DEBUG_GEN_REPORT(&yajl_ctx);
941 
942     yajl_ctx.head = NULL;
943 
944     yajl_ctx_free(&yajl_ctx);
945     return o;
946 
947 out:
948     str = yajl_get_error(yajl_ctx.hand, 1, (const unsigned char*)s, strlen(s));
949 
950     LIBXL__LOG(libxl__gc_owner(gc), LIBXL__LOG_ERROR, "yajl error: %s", str);
951     yajl_free_error(yajl_ctx.hand, str);
952     yajl_ctx_free(&yajl_ctx);
953     return NULL;
954 }
955 
956 static const char *yajl_gen_status_to_string(yajl_gen_status s)
957 {
958         switch (s) {
959         case yajl_gen_status_ok: abort();
960         case yajl_gen_keys_must_be_strings:
961             return "keys must be strings";
962         case yajl_max_depth_exceeded:
963             return "max depth exceeded";
964         case yajl_gen_in_error_state:
965             return "in error state";
966         case yajl_gen_generation_complete:
967             return "generation complete";
968         case yajl_gen_invalid_number:
969             return "invalid number";
970 #if 0 /* This is in the docs but not implemented in the version I am running. */
971         case yajl_gen_no_buf:
972             return "no buffer";
973         case yajl_gen_invalid_string:
974             return "invalid string";
975 #endif
976         default:
977             return "unknown error";
978         }
979 }
980 
981 char *libxl__object_to_json(libxl_ctx *ctx, const char *type,
982                             libxl__gen_json_callback gen, void *p)
983 {
984     const unsigned char *buf;
985     char *ret = NULL;
986     libxl_yajl_length len = 0;
987     yajl_gen_status s;
988     yajl_gen hand;
989 
990     hand = libxl_yajl_gen_alloc(NULL);
991     if (!hand)
992         return NULL;
993 
994     s = gen(hand, p);
995     if (s != yajl_gen_status_ok)
996         goto out;
997 
998     s = yajl_gen_get_buf(hand, &buf, &len);
999     if (s != yajl_gen_status_ok)
1000         goto out;
1001     ret = strdup((const char *)buf);
1002 
1003 out:
1004     yajl_gen_free(hand);
1005 
1006     if (s != yajl_gen_status_ok) {
1007         LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
1008                    "unable to convert %s to JSON representation. "
1009                    "YAJL error code %d: %s", type,
1010                    s, yajl_gen_status_to_string(s));
1011     } else if (!ret) {
1012         LIBXL__LOG(ctx, LIBXL__LOG_ERROR,
1013                    "unable to allocate space for to JSON representation of %s",
1014                    type);
1015     }
1016 
1017     return ret;
1018 }
1019 
1020 yajl_gen_status libxl__uint64_gen_json(yajl_gen hand, uint64_t val)
1021 {
1022     char *num;
1023     int len;
1024     yajl_gen_status s;
1025 
1026 
1027     len = asprintf(&num, "%"PRIu64, val);
1028     if (len == -1) {
1029         s = yajl_gen_in_error_state;
1030         goto out;
1031     }
1032 
1033     s = yajl_gen_number(hand, num, len);
1034 
1035     free(num);
1036 
1037 out:
1038     return s;
1039 }
1040 
1041 int libxl__object_from_json(libxl_ctx *ctx, const char *type,
1042                             libxl__json_parse_callback parse,
1043                             void *p, const char *s)
1044 {
1045     GC_INIT(ctx);
1046     libxl__json_object *o;
1047     int rc;
1048 
1049     o = libxl__json_parse(gc, s);
1050     if (!o) {
1051         LOG(ERROR,
1052             "unable to generate libxl__json_object from JSON representation of %s.",
1053             type);
1054         rc = ERROR_FAIL;
1055         goto out;
1056     }
1057 
1058     rc = parse(gc, o, p);
1059     if (rc) {
1060         LOG(ERROR, "unable to convert libxl__json_object to %s. (rc=%d)", type, rc);
1061         rc = ERROR_FAIL;
1062         goto out;
1063     }
1064 
1065     rc = 0;
1066 out:
1067     GC_FREE;
1068     return rc;
1069 }
1070 
1071 int libxl__int_parse_json(libxl__gc *gc, const libxl__json_object *o,
1072                           void *p)
1073 {
1074     long long i;
1075 
1076     if (!libxl__json_object_is_integer(o))
1077         return ERROR_FAIL;
1078 
1079     i = libxl__json_object_get_integer(o);
1080 
1081     if (i > INT_MAX || i < INT_MIN)
1082         return ERROR_FAIL;
1083 
1084     *((int *)p) = i;
1085 
1086     return 0;
1087 }
1088 
1089 /* Macro to generate:
1090  *  libxl__uint8_parse_json
1091  *  libxl__uint16_parse_json
1092  *  libxl__uint32_parse_json
1093  */
1094 #define PARSE_UINT(width)                                               \
1095     int libxl__uint ## width ## _parse_json(libxl__gc *gc,              \
1096                                             const libxl__json_object *o,\
1097                                             void *p)                    \
1098     {                                                                   \
1099         long long i;                                                    \
1100                                                                         \
1101         if (!libxl__json_object_is_integer(o))                          \
1102             return ERROR_FAIL;                                          \
1103                                                                         \
1104         i = libxl__json_object_get_integer(o);                          \
1105                                                                         \
1106         if (i < 0 || i > UINT ## width ## _MAX)                         \
1107             return ERROR_FAIL;                                          \
1108                                                                         \
1109         *((uint ## width ## _t *)p) = i;                                \
1110                                                                         \
1111         return 0;                                                       \
1112     }
1113 
1114 PARSE_UINT(8);
1115 PARSE_UINT(16);
1116 PARSE_UINT(32);
1117 
1118 int libxl__uint64_parse_json(libxl__gc *gc, const libxl__json_object *o,
1119                              void *p)
1120 {
1121     if (!libxl__json_object_is_integer(o) &&
1122         !libxl__json_object_is_number(o))
1123         return ERROR_FAIL;
1124 
1125     if (libxl__json_object_is_integer(o)) {
1126         long long i = libxl__json_object_get_integer(o);
1127 
1128         if (i < 0)
1129             return ERROR_FAIL;
1130 
1131         *((uint64_t *)p) = i;
1132     } else {
1133         const char *s;
1134         unsigned long long i;
1135         int saved_errno = errno;
1136 
1137         s = libxl__json_object_get_number(o);
1138 
1139         errno = 0;
1140         i = strtoull(s, NULL, 10);
1141 
1142         if (i == ULLONG_MAX && errno == ERANGE)
1143             return ERROR_FAIL;
1144 
1145         errno = saved_errno;
1146         *((uint64_t *)p) = i;
1147     }
1148 
1149     return 0;
1150 }
1151 
1152 /*
1153  * Local variables:
1154  * mode: C
1155  * c-basic-offset: 4
1156  * indent-tabs-mode: nil
1157  * End:
1158  */
1159