• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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