1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2016 Arun Raghavan <mail@arunraghavan.net>
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <math.h>
25
26 #include <pulse/json.h>
27 #include <pulse/xmalloc.h>
28 #include <pulsecore/core-util.h>
29 #include <pulsecore/hashmap.h>
30 #include <pulsecore/strbuf.h>
31
32 #define MAX_NESTING_DEPTH 20 /* Arbitrary number to make sure we don't have a stack overflow */
33
34 struct pa_json_object {
35 pa_json_type type;
36
37 union {
38 int int_value;
39 double double_value;
40 bool bool_value;
41 char *string_value;
42 pa_hashmap *object_values; /* name -> object */
43 pa_idxset *array_values; /* objects */
44 };
45 };
46
47 static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth);
48
json_object_new(void)49 static pa_json_object* json_object_new(void) {
50 pa_json_object *obj;
51
52 obj = pa_xnew0(pa_json_object, 1);
53
54 return obj;
55 }
56
is_whitespace(char c)57 static bool is_whitespace(char c) {
58 return c == '\t' || c == '\n' || c == '\r' || c == ' ';
59 }
60
is_digit(char c)61 static bool is_digit(char c) {
62 return c >= '0' && c <= '9';
63 }
64
is_end(const char c,const char * end)65 static bool is_end(const char c, const char *end) {
66 if (!end)
67 return c == '\0';
68 else {
69 while (*end) {
70 if (c == *end)
71 return true;
72 end++;
73 }
74 }
75
76 return false;
77 }
78
consume_string(const char * str,const char * expect)79 static const char* consume_string(const char *str, const char *expect) {
80 while (*expect) {
81 if (*str != *expect)
82 return NULL;
83
84 str++;
85 expect++;
86 }
87
88 return str;
89 }
90
parse_null(const char * str,pa_json_object * obj)91 static const char* parse_null(const char *str, pa_json_object *obj) {
92 str = consume_string(str, "null");
93
94 if (str)
95 obj->type = PA_JSON_TYPE_NULL;
96
97 return str;
98 }
99
parse_boolean(const char * str,pa_json_object * obj)100 static const char* parse_boolean(const char *str, pa_json_object *obj) {
101 const char *tmp;
102
103 tmp = consume_string(str, "true");
104
105 if (tmp) {
106 obj->type = PA_JSON_TYPE_BOOL;
107 obj->bool_value = true;
108 } else {
109 tmp = consume_string(str, "false");
110
111 if (str) {
112 obj->type = PA_JSON_TYPE_BOOL;
113 obj->bool_value = false;
114 }
115 }
116
117 return tmp;
118 }
119
parse_string(const char * str,pa_json_object * obj)120 static const char* parse_string(const char *str, pa_json_object *obj) {
121 pa_strbuf *buf = pa_strbuf_new();
122
123 str++; /* Consume leading '"' */
124
125 while (*str && *str != '"') {
126 if (*str != '\\') {
127 /* We only accept ASCII printable characters. */
128 if (*str < 0x20 || *str > 0x7E) {
129 pa_log("Invalid non-ASCII character: 0x%x", (unsigned int) *str);
130 goto error;
131 }
132
133 /* Normal character, juts consume */
134 pa_strbuf_putc(buf, *str);
135 } else {
136 /* Need to unescape */
137 str++;
138
139 switch (*str) {
140 case '"':
141 case '\\':
142 case '/':
143 pa_strbuf_putc(buf, *str);
144 break;
145
146 case 'b':
147 pa_strbuf_putc(buf, '\b' /* backspace */);
148 break;
149
150 case 'f':
151 pa_strbuf_putc(buf, '\f' /* form feed */);
152 break;
153
154 case 'n':
155 pa_strbuf_putc(buf, '\n' /* new line */);
156 break;
157
158 case 'r':
159 pa_strbuf_putc(buf, '\r' /* carriage return */);
160 break;
161
162 case 't':
163 pa_strbuf_putc(buf, '\t' /* horizontal tab */);
164 break;
165
166 case 'u':
167 pa_log("Unicode code points are currently unsupported");
168 goto error;
169
170 default:
171 pa_log("Unexepcted escape value: %c", *str);
172 goto error;
173 }
174 }
175
176 str++;
177 }
178
179 if (*str != '"') {
180 pa_log("Failed to parse remainder of string: %s", str);
181 goto error;
182 }
183
184 str++;
185
186 obj->type = PA_JSON_TYPE_STRING;
187 obj->string_value = pa_strbuf_to_string_free(buf);
188
189 return str;
190
191 error:
192 pa_strbuf_free(buf);
193 return NULL;
194 }
195
parse_number(const char * str,pa_json_object * obj)196 static const char* parse_number(const char *str, pa_json_object *obj) {
197 bool negative = false, has_fraction = false, has_exponent = false, valid = false;
198 unsigned int integer = 0;
199 unsigned int fraction = 0;
200 unsigned int fraction_digits = 0;
201 int exponent = 0;
202
203 if (*str == '-') {
204 negative = true;
205 str++;
206 }
207
208 if (*str == '0') {
209 valid = true;
210 str++;
211 goto fraction;
212 }
213
214 while (is_digit(*str)) {
215 valid = true;
216
217 if (integer > ((negative ? INT_MAX : UINT_MAX) / 10)) {
218 pa_log("Integer overflow while parsing number");
219 goto error;
220 }
221
222 integer = (integer * 10) + (*str - '0');
223 str++;
224 }
225
226 fraction:
227
228 if (!valid) {
229 pa_log("Missing digits while parsing number");
230 goto error;
231 }
232
233 if (*str == '.') {
234 has_fraction = true;
235 str++;
236 valid = false;
237
238 while (is_digit(*str)) {
239 valid = true;
240
241 if (fraction > (UINT_MAX / 10)) {
242 pa_log("Integer overflow while parsing fractional part of number");
243 goto error;
244 }
245
246 fraction = (fraction * 10) + (*str - '0');
247 fraction_digits++;
248 str++;
249 }
250
251 if (!valid) {
252 pa_log("No digit after '.' while parsing fraction");
253 goto error;
254 }
255 }
256
257 if (*str == 'e' || *str == 'E') {
258 bool exponent_negative = false;
259
260 has_exponent = true;
261 str++;
262 valid = false;
263
264 if (*str == '-') {
265 exponent_negative = true;
266 str++;
267 } else if (*str == '+')
268 str++;
269
270 while (is_digit(*str)) {
271 valid = true;
272
273 if (exponent > (INT_MAX / 10)) {
274 pa_log("Integer overflow while parsing exponent part of number");
275 goto error;
276 }
277
278 exponent = (exponent * 10) + (*str - '0');
279 str++;
280 }
281
282 if (!valid) {
283 pa_log("No digit in exponent while parsing fraction");
284 goto error;
285 }
286
287 if (exponent_negative)
288 exponent *= -1;
289 }
290
291 if (has_fraction || has_exponent) {
292 obj->type = PA_JSON_TYPE_DOUBLE;
293 obj->double_value =
294 (negative ? -1.0 : 1.0) * (integer + (double) fraction / pow(10, fraction_digits)) * pow(10, exponent);
295 } else {
296 obj->type = PA_JSON_TYPE_INT;
297 obj->int_value = (negative ? -1 : 1) * integer;
298 }
299
300 return str;
301
302 error:
303 return NULL;
304 }
305
parse_object(const char * str,pa_json_object * obj,unsigned int depth)306 static const char *parse_object(const char *str, pa_json_object *obj, unsigned int depth) {
307 pa_json_object *name = NULL, *value = NULL;
308
309 obj->object_values = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func,
310 pa_xfree, (pa_free_cb_t) pa_json_object_free);
311
312 while (*str != '}') {
313 str++; /* Consume leading '{' or ',' */
314
315 str = parse_value(str, ":", &name, depth + 1);
316 if (!str || pa_json_object_get_type(name) != PA_JSON_TYPE_STRING) {
317 pa_log("Could not parse key for object");
318 goto error;
319 }
320
321 /* Consume the ':' */
322 str++;
323
324 str = parse_value(str, ",}", &value, depth + 1);
325 if (!str) {
326 pa_log("Could not parse value for object");
327 goto error;
328 }
329
330 pa_hashmap_put(obj->object_values, pa_xstrdup(pa_json_object_get_string(name)), value);
331 pa_json_object_free(name);
332
333 name = NULL;
334 value = NULL;
335 }
336
337 /* Drop trailing '}' */
338 str++;
339
340 /* We now know the value was correctly parsed */
341 obj->type = PA_JSON_TYPE_OBJECT;
342
343 return str;
344
345 error:
346 pa_hashmap_free(obj->object_values);
347 obj->object_values = NULL;
348
349 if (name)
350 pa_json_object_free(name);
351 if (value)
352 pa_json_object_free(value);
353
354 return NULL;
355 }
356
parse_array(const char * str,pa_json_object * obj,unsigned int depth)357 static const char *parse_array(const char *str, pa_json_object *obj, unsigned int depth) {
358 pa_json_object *value;
359
360 obj->array_values = pa_idxset_new(NULL, NULL);
361
362 while (*str != ']') {
363 str++; /* Consume leading '[' or ',' */
364
365 /* Need to chew up whitespaces as a special case to deal with the
366 * possibility of an empty array */
367 while (is_whitespace(*str))
368 str++;
369
370 if (*str == ']')
371 break;
372
373 str = parse_value(str, ",]", &value, depth + 1);
374 if (!str) {
375 pa_log("Could not parse value for array");
376 goto error;
377 }
378
379 pa_idxset_put(obj->array_values, value, NULL);
380 }
381
382 /* Drop trailing ']' */
383 str++;
384
385 /* We now know the value was correctly parsed */
386 obj->type = PA_JSON_TYPE_ARRAY;
387
388 return str;
389
390 error:
391 pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
392 obj->array_values = NULL;
393 return NULL;
394 }
395
396 typedef enum {
397 JSON_PARSER_STATE_INIT,
398 JSON_PARSER_STATE_FINISH,
399 } json_parser_state;
400
parse_value(const char * str,const char * end,pa_json_object ** obj,unsigned int depth)401 static const char* parse_value(const char *str, const char *end, pa_json_object **obj, unsigned int depth) {
402 json_parser_state state = JSON_PARSER_STATE_INIT;
403 pa_json_object *o;
404
405 pa_assert(str != NULL);
406
407 o = json_object_new();
408
409 if (depth > MAX_NESTING_DEPTH) {
410 pa_log("Exceeded maximum permitted nesting depth of objects (%u)", MAX_NESTING_DEPTH);
411 goto error;
412 }
413
414 while (!is_end(*str, end)) {
415 switch (state) {
416 case JSON_PARSER_STATE_INIT:
417 if (is_whitespace(*str)) {
418 str++;
419 } else if (*str == 'n') {
420 str = parse_null(str, o);
421 state = JSON_PARSER_STATE_FINISH;
422 } else if (*str == 't' || *str == 'f') {
423 str = parse_boolean(str, o);
424 state = JSON_PARSER_STATE_FINISH;
425 } else if (*str == '"') {
426 str = parse_string(str, o);
427 state = JSON_PARSER_STATE_FINISH;
428 } else if (is_digit(*str) || *str == '-') {
429 str = parse_number(str, o);
430 state = JSON_PARSER_STATE_FINISH;
431 } else if (*str == '{') {
432 str = parse_object(str, o, depth);
433 state = JSON_PARSER_STATE_FINISH;
434 } else if (*str == '[') {
435 str = parse_array(str, o, depth);
436 state = JSON_PARSER_STATE_FINISH;
437 } else {
438 pa_log("Invalid JSON string: %s", str);
439 goto error;
440 }
441
442 if (!str)
443 goto error;
444
445 break;
446
447 case JSON_PARSER_STATE_FINISH:
448 /* Consume trailing whitespaces */
449 if (is_whitespace(*str)) {
450 str++;
451 } else {
452 goto error;
453 }
454 }
455 }
456
457 if (pa_json_object_get_type(o) == PA_JSON_TYPE_INIT) {
458 /* We didn't actually get any data */
459 pa_log("No data while parsing json string: '%s' till '%s'", str, pa_strnull(end));
460 goto error;
461 }
462
463 *obj = o;
464
465 return str;
466
467 error:
468 pa_json_object_free(o);
469 return NULL;
470 }
471
472
pa_json_parse(const char * str)473 pa_json_object* pa_json_parse(const char *str) {
474 pa_json_object *obj;
475
476 str = parse_value(str, NULL, &obj, 0);
477
478 if (!str) {
479 pa_log("JSON parsing failed");
480 return NULL;
481 }
482
483 if (*str != '\0') {
484 pa_log("Unable to parse complete JSON string, remainder is: %s", str);
485 pa_json_object_free(obj);
486 return NULL;
487 }
488
489 return obj;
490 }
491
pa_json_object_get_type(const pa_json_object * obj)492 pa_json_type pa_json_object_get_type(const pa_json_object *obj) {
493 return obj->type;
494 }
495
pa_json_object_free(pa_json_object * obj)496 void pa_json_object_free(pa_json_object *obj) {
497
498 switch (pa_json_object_get_type(obj)) {
499 case PA_JSON_TYPE_INIT:
500 case PA_JSON_TYPE_INT:
501 case PA_JSON_TYPE_DOUBLE:
502 case PA_JSON_TYPE_BOOL:
503 case PA_JSON_TYPE_NULL:
504 break;
505
506 case PA_JSON_TYPE_STRING:
507 pa_xfree(obj->string_value);
508 break;
509
510 case PA_JSON_TYPE_OBJECT:
511 pa_hashmap_free(obj->object_values);
512 break;
513
514 case PA_JSON_TYPE_ARRAY:
515 pa_idxset_free(obj->array_values, (pa_free_cb_t) pa_json_object_free);
516 break;
517
518 default:
519 pa_assert_not_reached();
520 }
521
522 pa_xfree(obj);
523 }
524
pa_json_object_get_int(const pa_json_object * o)525 int pa_json_object_get_int(const pa_json_object *o) {
526 pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_INT);
527 return o->int_value;
528 }
529
pa_json_object_get_double(const pa_json_object * o)530 double pa_json_object_get_double(const pa_json_object *o) {
531 pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_DOUBLE);
532 return o->double_value;
533 }
534
pa_json_object_get_bool(const pa_json_object * o)535 bool pa_json_object_get_bool(const pa_json_object *o) {
536 pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_BOOL);
537 return o->bool_value;
538 }
539
pa_json_object_get_string(const pa_json_object * o)540 const char* pa_json_object_get_string(const pa_json_object *o) {
541 pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_STRING);
542 return o->string_value;
543 }
544
pa_json_object_get_object_member(const pa_json_object * o,const char * name)545 const pa_json_object* pa_json_object_get_object_member(const pa_json_object *o, const char *name) {
546 pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_OBJECT);
547 return pa_hashmap_get(o->object_values, name);
548 }
549
pa_json_object_get_array_length(const pa_json_object * o)550 int pa_json_object_get_array_length(const pa_json_object *o) {
551 pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
552 return pa_idxset_size(o->array_values);
553 }
554
pa_json_object_get_array_member(const pa_json_object * o,int index)555 const pa_json_object* pa_json_object_get_array_member(const pa_json_object *o, int index) {
556 pa_assert(pa_json_object_get_type(o) == PA_JSON_TYPE_ARRAY);
557 return pa_idxset_get_by_index(o->array_values, index);
558 }
559
pa_json_object_equal(const pa_json_object * o1,const pa_json_object * o2)560 bool pa_json_object_equal(const pa_json_object *o1, const pa_json_object *o2) {
561 int i;
562
563 if (pa_json_object_get_type(o1) != pa_json_object_get_type(o2))
564 return false;
565
566 switch (pa_json_object_get_type(o1)) {
567 case PA_JSON_TYPE_NULL:
568 return true;
569
570 case PA_JSON_TYPE_BOOL:
571 return o1->bool_value == o2->bool_value;
572
573 case PA_JSON_TYPE_INT:
574 return o1->int_value == o2->int_value;
575
576 case PA_JSON_TYPE_DOUBLE:
577 return PA_DOUBLE_IS_EQUAL(o1->double_value, o2->double_value);
578
579 case PA_JSON_TYPE_STRING:
580 return pa_streq(o1->string_value, o2->string_value);
581
582 case PA_JSON_TYPE_ARRAY:
583 if (pa_json_object_get_array_length(o1) != pa_json_object_get_array_length(o2))
584 return false;
585
586 for (i = 0; i < pa_json_object_get_array_length(o1); i++) {
587 if (!pa_json_object_equal(pa_json_object_get_array_member(o1, i),
588 pa_json_object_get_array_member(o2, i)))
589 return false;
590 }
591
592 return true;
593
594 case PA_JSON_TYPE_OBJECT: {
595 void *state;
596 const char *key;
597 const pa_json_object *v1, *v2;
598
599 if (pa_hashmap_size(o1->object_values) != pa_hashmap_size(o2->object_values))
600 return false;
601
602 PA_HASHMAP_FOREACH_KV(key, v1, o1->object_values, state) {
603 v2 = pa_json_object_get_object_member(o2, key);
604 if (!v2 || !pa_json_object_equal(v1, v2))
605 return false;
606 }
607
608 return true;
609 }
610
611 default:
612 pa_assert_not_reached();
613 }
614 }
615