Add validation features to json_unpack()
[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 <stdarg.h>
10 #include <string.h>
11 #include <assert.h>
12
13 #include <jansson.h>
14 #include "jansson_private.h"
15
16 typedef struct {
17     const char *fmt;
18     char token;
19     json_error_t *error;
20     int line;
21     int column;
22 } scanner_t;
23
24 static void next_token(scanner_t *s)
25 {
26     const char *t = s->fmt;
27     s->column++;
28
29     /* skip space and ignored chars */
30     while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
31         if(*t == '\n') {
32             s->line++;
33             s->column = 1;
34         }
35         else
36             s->column++;
37
38         t++;
39     }
40
41     s->token = *t;
42
43     t++;
44     s->fmt = t;
45 }
46
47 static void set_error(scanner_t *s, const char *fmt, ...)
48 {
49     va_list ap;
50     va_start(ap, fmt);
51     jsonp_error_vset(s->error, s->line, s->column, fmt, ap);
52     va_end(ap);
53 }
54
55 static json_t *pack(scanner_t *s, va_list *ap);
56
57 static json_t *pack_object(scanner_t *s, va_list *ap)
58 {
59     json_t *object = json_object();
60     next_token(s);
61
62     while(s->token != '}') {
63         const char *key;
64         json_t *value;
65
66         if(!s->token) {
67             set_error(s, "Unexpected end of format string");
68             goto error;
69         }
70
71         if(s->token != 's') {
72             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
73             goto error;
74         }
75
76         key = va_arg(*ap, const char *);
77         if(!key) {
78             set_error(s, "NULL object key");
79             goto error;
80         }
81
82         next_token(s);
83
84         value = pack(s, ap);
85         if(!value)
86             goto error;
87
88         if(json_object_set_new(object, key, value)) {
89             set_error(s, "Unable to add key \"%s\"", key);
90             goto error;
91         }
92
93         next_token(s);
94     }
95
96     return object;
97
98 error:
99     json_decref(object);
100     return NULL;
101 }
102
103 static json_t *pack_array(scanner_t *s, va_list *ap)
104 {
105     json_t *array = json_array();
106     next_token(s);
107
108     while(s->token != ']') {
109         json_t *value;
110
111         if(!s->token) {
112             set_error(s, "Unexpected end of format string");
113             goto error;
114         }
115
116         value = pack(s, ap);
117         if(!value)
118             goto error;
119
120         if(json_array_append_new(array, value)) {
121             set_error(s, "Unable to append to array");
122             goto error;
123         }
124
125         next_token(s);
126     }
127     return array;
128
129 error:
130     json_decref(array);
131     return NULL;
132 }
133
134 static json_t *pack(scanner_t *s, va_list *ap)
135 {
136     switch(s->token) {
137         case '{':
138             return pack_object(s, ap);
139
140         case '[':
141             return pack_array(s, ap);
142
143         case 's': /* string */
144         {
145             const char *str = va_arg(*ap, const char *);
146             if(!str)
147             {
148                 set_error(s, "NULL string");
149                 return NULL;
150             }
151             return json_string(str);
152         }
153
154         case 'n': /* null */
155             return json_null();
156
157         case 'b': /* boolean */
158             return va_arg(*ap, int) ? json_true() : json_false();
159
160         case 'i': /* integer */
161             return json_integer(va_arg(*ap, int));
162
163         case 'f': /* double-precision float */
164             return json_real(va_arg(*ap, double));
165
166         case 'O': /* a json_t object; increments refcount */
167             return json_incref(va_arg(*ap, json_t *));
168
169         case 'o': /* a json_t object; doesn't increment refcount */
170             return va_arg(*ap, json_t *);
171
172         default: /* Whoops! */
173             set_error(s, "Unrecognized format character '%c'", s->token);
174             return NULL;
175     }
176 }
177
178 static int unpack(scanner_t *s, json_t *root, va_list *ap);
179
180 static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
181 {
182     int ret = -1;
183     int wildcard = 0;
184     hashtable_t key_set;
185
186     if(hashtable_init(&key_set, jsonp_hash_key, jsonp_key_equal, NULL, NULL)) {
187         set_error(s, "Out of memory");
188         return -1;
189     }
190
191     if(!json_is_object(root)) {
192         set_error(s, "Expected object, got %i", json_typeof(root));
193         goto error;
194     }
195     next_token(s);
196
197     while(s->token != '}') {
198         const char *key;
199         json_t *value;
200
201         if(wildcard) {
202             set_error(s, "Expected '}', got '%c'", s->token);
203             goto error;
204         }
205
206         if(!s->token) {
207             set_error(s, "Unexpected end of format string");
208             goto error;
209         }
210
211         if(s->token == '*') {
212             wildcard = 1;
213             next_token(s);
214             continue;
215         }
216
217         if(s->token != 's') {
218             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
219             goto error;
220         }
221
222         key = va_arg(*ap, const char *);
223         if(!key) {
224             set_error(s, "NULL object key");
225             goto error;
226         }
227
228         next_token(s);
229
230         value = json_object_get(root, key);
231         if(unpack(s, value, ap))
232             goto error;
233
234         hashtable_set(&key_set, (void *)key, NULL);
235         next_token(s);
236     }
237
238     if(!wildcard && key_set.size != json_object_size(root)) {
239         long diff = (long)json_object_size(root) - (long)key_set.size;
240         set_error(s, "%li object items left unpacked", diff);
241         goto error;
242     }
243     ret = 0;
244
245 error:
246     hashtable_close(&key_set);
247     return ret;
248 }
249
250 static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
251 {
252     size_t i = 0;
253     int wildcard = 0;
254
255     if(!json_is_array(root)) {
256         set_error(s, "Expected array, got %d", json_typeof(root));
257         return -1;
258     }
259     next_token(s);
260
261     while(s->token != ']') {
262         json_t *value;
263
264         if(wildcard) {
265             set_error(s, "Expected ']', got '%c'", s->token);
266             return -1;
267         }
268
269         if(!s->token) {
270             set_error(s, "Unexpected end of format string");
271             return -1;
272         }
273
274         if(s->token == '*') {
275             wildcard = 1;
276             next_token(s);
277             continue;
278         }
279
280         value = json_array_get(root, i);
281         if(!value) {
282             set_error(s, "Array index %lu out of range", (unsigned long)i);
283             return -1;
284         }
285
286         if(unpack(s, value, ap))
287             return -1;
288
289         next_token(s);
290         i++;
291     }
292
293     if(!wildcard && i != json_array_size(root)) {
294         long diff = (long)json_array_size(root) - (long)i;
295         set_error(s, "%li array items left upacked", diff);
296         return -1;
297     }
298
299     return 0;
300 }
301
302 static int unpack(scanner_t *s, json_t *root, va_list *ap)
303 {
304     switch(s->token)
305     {
306         case '{':
307             return unpack_object(s, root, ap);
308
309         case '[':
310             return unpack_array(s, root, ap);
311
312         case 's':
313         {
314             const char **str;
315
316             if(!json_is_string(root))
317             {
318                 set_error(s, "Type mismatch! Object (%i) wasn't a string.",
319                           json_typeof(root));
320                 return -1;
321             }
322
323             str = va_arg(*ap, const char **);
324             if(!str) {
325                 set_error(s, "Passed a NULL string pointer!");
326                 return -1;
327             }
328
329             *str = json_string_value(root);
330             return 0;
331         }
332
333         case 'i':
334             if(!json_is_integer(root))
335             {
336                 set_error(s, "Type mismatch! Object (%i) wasn't an integer.",
337                       json_typeof(root));
338                 return -1;
339             }
340             *va_arg(*ap, int*) = json_integer_value(root);
341             return 0;
342
343         case 'b':
344             if(!json_is_boolean(root))
345             {
346                 set_error(s, "Type mismatch! Object (%i) wasn't a boolean.",
347                       json_typeof(root));
348                 return -1;
349             }
350             *va_arg(*ap, int*) = json_is_true(root);
351             return 0;
352
353         case 'f':
354             if(!json_is_number(root))
355             {
356                 set_error(s, "Type mismatch! Object (%i) wasn't a real.",
357                       json_typeof(root));
358                 return -1;
359             }
360             *va_arg(*ap, double*) = json_number_value(root);
361             return 0;
362
363         case 'O':
364             json_incref(root);
365             /* Fall through */
366
367         case 'o':
368             *va_arg(*ap, json_t**) = root;
369             return 0;
370
371         case 'n':
372             /* Don't assign, just validate */
373             if(!json_is_null(root))
374             {
375                 set_error(s, "Type mismatch! Object (%i) wasn't null.",
376                       json_typeof(root));
377                 return -1;
378             }
379             return 0;
380
381         default:
382             set_error(s, "Unknown format character '%c'", s->token);
383             return -1;
384     }
385 }
386
387 json_t *json_pack(json_error_t *error, const char *fmt, ...)
388 {
389     scanner_t s;
390     json_t *value;
391     va_list ap;
392
393     jsonp_error_init(error, "");
394
395     if(!fmt || !*fmt) {
396         jsonp_error_set(error, 1, 1, "Null or empty format string!");
397         return NULL;
398     }
399
400     s.error = error;
401     s.fmt = fmt;
402     s.line = 1;
403     s.column = 0;
404
405     next_token(&s);
406
407     va_start(ap, fmt);
408     value = pack(&s, &ap);
409     va_end(ap);
410
411     next_token(&s);
412     if(s.token) {
413         set_error(&s, "Garbage after format string");
414         return NULL;
415     }
416
417     return value;
418 }
419
420 int json_unpack(json_t *root, json_error_t *error, const char *fmt, ...)
421 {
422     scanner_t s;
423     va_list ap;
424     int result;
425
426     jsonp_error_init(error, "");
427
428     if(!fmt || !*fmt) {
429         jsonp_error_set(error, 1, 1, "Null or empty format string!");
430         return -1;
431     }
432
433     s.error = error;
434     s.fmt = fmt;
435     s.line = 1;
436     s.column = 0;
437
438     next_token(&s);
439
440     va_start(ap, fmt);
441     result = unpack(&s, root, &ap);
442     va_end(ap);
443
444     if(result)
445         return -1;
446
447     next_token(&s);
448     if(s.token) {
449         set_error(&s, "Garbage after format string");
450         return -1;
451     }
452
453     return 0;
454 }