Tweak the default validation behaviour of the unpack API
[jansson.git] / src / pack_unpack.c
1 /*
2  * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
3  * Copyright (c) 2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
4  *
5  * Jansson is free software; you can redistribute it and/or modify
6  * it under the terms of the MIT license. See LICENSE for details.
7  */
8
9 #include <jansson.h>
10 #include "jansson_private.h"
11
12 typedef struct {
13     const char *fmt;
14     char token;
15     json_error_t *error;
16     size_t flags;
17     int line;
18     int column;
19 } scanner_t;
20
21 static const char *type_names[] = {
22     "object",
23     "array",
24     "string",
25     "integer",
26     "real",
27     "true",
28     "false",
29     "null"
30 };
31
32 #define type_name(x) type_names[json_typeof(x)]
33
34 static void next_token(scanner_t *s)
35 {
36     const char *t = s->fmt;
37     s->column++;
38
39     /* skip space and ignored chars */
40     while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
41         if(*t == '\n') {
42             s->line++;
43             s->column = 1;
44         }
45         else
46             s->column++;
47
48         t++;
49     }
50
51     s->token = *t;
52
53     t++;
54     s->fmt = t;
55 }
56
57 static void set_error(scanner_t *s, const char *fmt, ...)
58 {
59     va_list ap;
60     va_start(ap, fmt);
61     jsonp_error_vset(s->error, s->line, s->column, fmt, ap);
62     va_end(ap);
63 }
64
65 static json_t *pack(scanner_t *s, va_list *ap);
66
67 static json_t *pack_object(scanner_t *s, va_list *ap)
68 {
69     json_t *object = json_object();
70     next_token(s);
71
72     while(s->token != '}') {
73         const char *key;
74         json_t *value;
75
76         if(!s->token) {
77             set_error(s, "Unexpected end of format string");
78             goto error;
79         }
80
81         if(s->token != 's') {
82             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
83             goto error;
84         }
85
86         key = va_arg(*ap, const char *);
87         if(!key) {
88             set_error(s, "NULL object key");
89             goto error;
90         }
91
92         next_token(s);
93
94         value = pack(s, ap);
95         if(!value)
96             goto error;
97
98         if(json_object_set_new(object, key, value)) {
99             set_error(s, "Unable to add key \"%s\"", key);
100             goto error;
101         }
102
103         next_token(s);
104     }
105
106     return object;
107
108 error:
109     json_decref(object);
110     return NULL;
111 }
112
113 static json_t *pack_array(scanner_t *s, va_list *ap)
114 {
115     json_t *array = json_array();
116     next_token(s);
117
118     while(s->token != ']') {
119         json_t *value;
120
121         if(!s->token) {
122             set_error(s, "Unexpected end of format string");
123             goto error;
124         }
125
126         value = pack(s, ap);
127         if(!value)
128             goto error;
129
130         if(json_array_append_new(array, value)) {
131             set_error(s, "Unable to append to array");
132             goto error;
133         }
134
135         next_token(s);
136     }
137     return array;
138
139 error:
140     json_decref(array);
141     return NULL;
142 }
143
144 static json_t *pack(scanner_t *s, va_list *ap)
145 {
146     switch(s->token) {
147         case '{':
148             return pack_object(s, ap);
149
150         case '[':
151             return pack_array(s, ap);
152
153         case 's': /* string */
154         {
155             const char *str = va_arg(*ap, const char *);
156             if(!str) {
157                 set_error(s, "NULL string");
158                 return NULL;
159             }
160             return json_string(str);
161         }
162
163         case 'n': /* null */
164             return json_null();
165
166         case 'b': /* boolean */
167             return va_arg(*ap, int) ? json_true() : json_false();
168
169         case 'i': /* integer from int */
170             return json_integer(va_arg(*ap, int));
171
172         case 'I': /* integer from json_int_t */
173             return json_integer(va_arg(*ap, json_int_t));
174
175         case 'f': /* real */
176             return json_real(va_arg(*ap, double));
177
178         case 'O': /* a json_t object; increments refcount */
179             return json_incref(va_arg(*ap, json_t *));
180
181         case 'o': /* a json_t object; doesn't increment refcount */
182             return va_arg(*ap, json_t *);
183
184         default:
185             set_error(s, "Unrecognized format character '%c'", s->token);
186             return NULL;
187     }
188 }
189
190 static int unpack(scanner_t *s, json_t *root, va_list *ap);
191
192 static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
193 {
194     int ret = -1;
195     int strict = 0;
196
197     /* Use a set (emulated by a hashtable) to check that all object
198        keys are accessed. Checking that the correct number of keys
199        were accessed is not enough, as the same key can be unpacked
200        multiple times.
201     */
202     hashtable_t key_set;
203
204     if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) {
205         set_error(s, "Out of memory");
206         return -1;
207     }
208
209     if(!json_is_object(root)) {
210         set_error(s, "Expected object, got %s", type_name(root));
211         goto out;
212     }
213     next_token(s);
214
215     while(s->token != '}') {
216         const char *key;
217         json_t *value;
218
219         if(strict != 0) {
220             set_error(s, "Expected '}' after '%c', got '%c'",
221                       (strict == 1 ? '!' : '*'), s->token);
222             goto out;
223         }
224
225         if(!s->token) {
226             set_error(s, "Unexpected end of format string");
227             goto out;
228         }
229
230         if(s->token == '!' || s->token == '*') {
231             strict = (s->token == '!' ? 1 : -1);
232             next_token(s);
233             continue;
234         }
235
236         if(s->token != 's') {
237             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
238             goto out;
239         }
240
241         key = va_arg(*ap, const char *);
242         if(!key) {
243             set_error(s, "NULL object key");
244             goto out;
245         }
246
247         next_token(s);
248
249         value = json_object_get(root, key);
250         if(unpack(s, value, ap))
251             goto out;
252
253         hashtable_set(&key_set, (void *)key, NULL);
254         next_token(s);
255     }
256
257     if(strict == 0 && (s->flags & JSON_STRICT))
258         strict = 1;
259
260     if(strict == 1 && key_set.size != json_object_size(root)) {
261         long diff = (long)json_object_size(root) - (long)key_set.size;
262         set_error(s, "%li object items left unpacked", diff);
263         goto out;
264     }
265
266     ret = 0;
267
268 out:
269     hashtable_close(&key_set);
270     return ret;
271 }
272
273 static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
274 {
275     size_t i = 0;
276     int strict = 0;
277
278     if(!json_is_array(root)) {
279         set_error(s, "Expected array, got %s", type_name(root));
280         return -1;
281     }
282     next_token(s);
283
284     while(s->token != ']') {
285         json_t *value;
286
287         if(strict != 0) {
288             set_error(s, "Expected ']' after '%c', got '%c'",
289                       (strict == 1 ? '!' : '*'),
290                       s->token);
291             return -1;
292         }
293
294         if(!s->token) {
295             set_error(s, "Unexpected end of format string");
296             return -1;
297         }
298
299         if(s->token == '!' || s->token == '*') {
300             strict = (s->token == '!' ? 1 : -1);
301             next_token(s);
302             continue;
303         }
304
305         value = json_array_get(root, i);
306         if(!value) {
307             set_error(s, "Array index %lu out of range", (unsigned long)i);
308             return -1;
309         }
310
311         if(unpack(s, value, ap))
312             return -1;
313
314         next_token(s);
315         i++;
316     }
317
318     if(strict == 0 && (s->flags & JSON_STRICT))
319         strict = 1;
320
321     if(strict == 1 && i != json_array_size(root)) {
322         long diff = (long)json_array_size(root) - (long)i;
323         set_error(s, "%li array items left upacked", diff);
324         return -1;
325     }
326
327     return 0;
328 }
329
330 static int unpack(scanner_t *s, json_t *root, va_list *ap)
331 {
332     switch(s->token)
333     {
334         case '{':
335             return unpack_object(s, root, ap);
336
337         case '[':
338             return unpack_array(s, root, ap);
339
340         case 's':
341             if(!json_is_string(root)) {
342                 set_error(s, "Expected string, got %s", type_name(root));
343                 return -1;
344             }
345
346             if(!(s->flags & JSON_VALIDATE_ONLY)) {
347                 const char **str;
348
349                 str = va_arg(*ap, const char **);
350                 if(!str) {
351                     set_error(s, "NULL string");
352                     return -1;
353                 }
354
355                 *str = json_string_value(root);
356             }
357             return 0;
358
359         case 'i':
360             if(!json_is_integer(root)) {
361                 set_error(s, "Expected integer, got %s", type_name(root));
362                 return -1;
363             }
364
365             if(!(s->flags & JSON_VALIDATE_ONLY))
366                 *va_arg(*ap, int*) = json_integer_value(root);
367
368             return 0;
369
370         case 'I':
371             if(!json_is_integer(root)) {
372                 set_error(s, "Expected integer, got %s", type_name(root));
373                 return -1;
374             }
375
376             if(!(s->flags & JSON_VALIDATE_ONLY))
377                 *va_arg(*ap, json_int_t*) = json_integer_value(root);
378
379             return 0;
380
381         case 'b':
382             if(!json_is_boolean(root)) {
383                 set_error(s, "Expected true or false, got %s", type_name(root));
384                 return -1;
385             }
386
387             if(!(s->flags & JSON_VALIDATE_ONLY))
388                 *va_arg(*ap, int*) = json_is_true(root);
389
390             return 0;
391
392         case 'f':
393             if(!json_is_real(root)) {
394                 set_error(s, "Expected real, got %s", type_name(root));
395                 return -1;
396             }
397
398             if(!(s->flags & JSON_VALIDATE_ONLY))
399                 *va_arg(*ap, double*) = json_real_value(root);
400
401             return 0;
402
403         case 'F':
404             if(!json_is_number(root)) {
405                 set_error(s, "Expected real or integer, got %s",
406                           type_name(root));
407                 return -1;
408             }
409
410             if(!(s->flags & JSON_VALIDATE_ONLY))
411                 *va_arg(*ap, double*) = json_number_value(root);
412
413             return 0;
414
415         case 'O':
416             if(!(s->flags & JSON_VALIDATE_ONLY))
417                 json_incref(root);
418             /* Fall through */
419
420         case 'o':
421             if(!(s->flags & JSON_VALIDATE_ONLY))
422                 *va_arg(*ap, json_t**) = root;
423
424             return 0;
425
426         case 'n':
427             /* Never assign, just validate */
428             if(!json_is_null(root)) {
429                 set_error(s, "Expected null, got %s", type_name(root));
430                 return -1;
431             }
432             return 0;
433
434         default:
435             set_error(s, "Unknown format character '%c'", s->token);
436             return -1;
437     }
438 }
439
440 json_t *json_vpack_ex(json_error_t *error, size_t flags,
441                       const char *fmt, va_list ap)
442 {
443     scanner_t s;
444     json_t *value;
445
446     jsonp_error_init(error, "");
447
448     if(!fmt || !*fmt) {
449         jsonp_error_set(error, 1, 1, "Null or empty format string");
450         return NULL;
451     }
452
453     s.error = error;
454     s.flags = flags;
455     s.fmt = fmt;
456     s.line = 1;
457     s.column = 0;
458
459     next_token(&s);
460     value = pack(&s, &ap);
461
462     next_token(&s);
463     if(s.token) {
464         json_decref(value);
465         set_error(&s, "Garbage after format string");
466         return NULL;
467     }
468
469     return value;
470 }
471
472 json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
473 {
474     json_t *value;
475     va_list ap;
476
477     va_start(ap, fmt);
478     value = json_vpack_ex(error, flags, fmt, ap);
479     va_end(ap);
480
481     return value;
482 }
483
484 json_t *json_pack(const char *fmt, ...)
485 {
486     json_t *value;
487     va_list ap;
488
489     va_start(ap, fmt);
490     value = json_vpack_ex(NULL, 0, fmt, ap);
491     va_end(ap);
492
493     return value;
494 }
495
496 int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
497                     const char *fmt, va_list ap)
498 {
499     scanner_t s;
500
501     jsonp_error_init(error, "");
502
503     if(!fmt || !*fmt) {
504         jsonp_error_set(error, 1, 1, "Null or empty format string");
505         return -1;
506     }
507
508     s.error = error;
509     s.flags = flags;
510     s.fmt = fmt;
511     s.line = 1;
512     s.column = 0;
513
514     next_token(&s);
515
516     if(unpack(&s, root, &ap))
517         return -1;
518
519     next_token(&s);
520     if(s.token) {
521         set_error(&s, "Garbage after format string");
522         return -1;
523     }
524
525     return 0;
526 }
527
528 int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
529 {
530     int ret;
531     va_list ap;
532
533     va_start(ap, fmt);
534     ret = json_vunpack_ex(root, error, flags, fmt, ap);
535     va_end(ap);
536
537     return ret;
538 }
539
540 int json_unpack(json_t *root, const char *fmt, ...)
541 {
542     int ret;
543     va_list ap;
544
545     va_start(ap, fmt);
546     ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
547     va_end(ap);
548
549     return ret;
550 }