/src/libplist/src/jplist.c
Line | Count | Source |
1 | | /* |
2 | | * jplist.c |
3 | | * JSON plist implementation |
4 | | * |
5 | | * Copyright (c) 2019-2021 Nikias Bassen All Rights Reserved. |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * This library is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | | * Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with this library; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | */ |
21 | | |
22 | | #ifdef HAVE_CONFIG_H |
23 | | #include <config.h> |
24 | | #endif |
25 | | |
26 | | #include <string.h> |
27 | | #include <stdlib.h> |
28 | | #include <stdio.h> |
29 | | #include <time.h> |
30 | | |
31 | | #include <inttypes.h> |
32 | | #include <ctype.h> |
33 | | #include <math.h> |
34 | | #include <limits.h> |
35 | | |
36 | | #include <node.h> |
37 | | |
38 | | #include "plist.h" |
39 | | #include "strbuf.h" |
40 | | #include "jsmn.h" |
41 | | #include "hashtable.h" |
42 | | #include "base64.h" |
43 | | #include "time64.h" |
44 | | #include "common.h" |
45 | | |
46 | | #ifdef DEBUG |
47 | | static int plist_json_debug = 0; |
48 | 0 | #define PLIST_JSON_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonparser] ERROR: " __VA_ARGS__); } |
49 | 0 | #define PLIST_JSON_WRITE_ERR(...) if (plist_json_debug) { fprintf(stderr, "libplist[jsonwriter] ERROR: " __VA_ARGS__); } |
50 | | #else |
51 | | #define PLIST_JSON_ERR(...) |
52 | | #define PLIST_JSON_WRITE_ERR(...) |
53 | | #endif |
54 | | |
55 | | void plist_json_init(void) |
56 | 2 | { |
57 | | /* init JSON stuff */ |
58 | 2 | #ifdef DEBUG |
59 | 2 | char *env_debug = getenv("PLIST_JSON_DEBUG"); |
60 | 2 | if (env_debug && !strcmp(env_debug, "1")) { |
61 | 0 | plist_json_debug = 1; |
62 | 0 | } |
63 | 2 | #endif |
64 | 2 | } |
65 | | |
66 | | void plist_json_deinit(void) |
67 | 0 | { |
68 | | /* deinit JSON stuff */ |
69 | 0 | } |
70 | | |
71 | | void plist_json_set_debug(int debug) |
72 | 0 | { |
73 | 0 | #ifdef DEBUG |
74 | 0 | plist_json_debug = debug; |
75 | 0 | #endif |
76 | 0 | } |
77 | | |
78 | | #ifndef HAVE_STRNDUP |
79 | | #ifndef _MSC_VER |
80 | | #pragma GCC diagnostic push |
81 | | #pragma GCC diagnostic ignored "-Wshadow" |
82 | | #endif |
83 | | static char* strndup(const char* str, size_t len) |
84 | | { |
85 | | char *newstr = (char *)malloc(len+1); |
86 | | if (newstr) { |
87 | | strncpy(newstr, str, len); |
88 | | newstr[len]= '\0'; |
89 | | } |
90 | | return newstr; |
91 | | } |
92 | | #ifndef _MSC_VER |
93 | | #pragma GCC diagnostic pop |
94 | | #endif |
95 | | #endif |
96 | | |
97 | | static plist_err_t node_to_json(node_t node, bytearray_t **outbuf, uint32_t depth, int prettify, int coerce) |
98 | 0 | { |
99 | 0 | plist_data_t node_data = NULL; |
100 | |
|
101 | 0 | char *val = NULL; |
102 | 0 | size_t val_len = 0; |
103 | |
|
104 | 0 | uint32_t i = 0; |
105 | |
|
106 | 0 | if (!node) |
107 | 0 | return PLIST_ERR_INVALID_ARG; |
108 | | |
109 | 0 | node_data = plist_get_data(node); |
110 | |
|
111 | 0 | switch (node_data->type) |
112 | 0 | { |
113 | 0 | case PLIST_BOOLEAN: |
114 | 0 | { |
115 | 0 | if (node_data->boolval) { |
116 | 0 | str_buf_append(*outbuf, "true", 4); |
117 | 0 | } else { |
118 | 0 | str_buf_append(*outbuf, "false", 5); |
119 | 0 | } |
120 | 0 | } |
121 | 0 | break; |
122 | | |
123 | 0 | case PLIST_NULL: |
124 | 0 | str_buf_append(*outbuf, "null", 4); |
125 | 0 | break; |
126 | | |
127 | 0 | case PLIST_INT: |
128 | 0 | val = (char*)malloc(64); |
129 | 0 | if (node_data->length == 16) { |
130 | 0 | val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); |
131 | 0 | } else { |
132 | 0 | val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); |
133 | 0 | } |
134 | 0 | str_buf_append(*outbuf, val, val_len); |
135 | 0 | free(val); |
136 | 0 | break; |
137 | | |
138 | 0 | case PLIST_REAL: |
139 | 0 | val = (char*)malloc(64); |
140 | 0 | val_len = dtostr(val, 64, node_data->realval); |
141 | 0 | str_buf_append(*outbuf, val, val_len); |
142 | 0 | free(val); |
143 | 0 | break; |
144 | | |
145 | 0 | case PLIST_STRING: |
146 | 0 | case PLIST_KEY: { |
147 | 0 | const char *charmap[32] = { |
148 | 0 | "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", |
149 | 0 | "\\b", "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", |
150 | 0 | "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", |
151 | 0 | "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f", |
152 | 0 | }; |
153 | 0 | size_t j = 0; |
154 | 0 | size_t len = 0; |
155 | 0 | off_t start = 0; |
156 | 0 | off_t cur = 0; |
157 | |
|
158 | 0 | str_buf_append(*outbuf, "\"", 1); |
159 | |
|
160 | 0 | len = node_data->length; |
161 | 0 | for (j = 0; j < len; j++) { |
162 | 0 | unsigned char ch = (unsigned char)node_data->strval[j]; |
163 | 0 | if (ch < 0x20) { |
164 | 0 | str_buf_append(*outbuf, node_data->strval + start, cur - start); |
165 | 0 | str_buf_append(*outbuf, charmap[ch], (charmap[ch][1] == 'u') ? 6 : 2); |
166 | 0 | start = cur+1; |
167 | 0 | } else if (ch == '"') { |
168 | 0 | str_buf_append(*outbuf, node_data->strval + start, cur - start); |
169 | 0 | str_buf_append(*outbuf, "\\\"", 2); |
170 | 0 | start = cur+1; |
171 | 0 | } |
172 | 0 | cur++; |
173 | 0 | } |
174 | 0 | str_buf_append(*outbuf, node_data->strval + start, cur - start); |
175 | |
|
176 | 0 | str_buf_append(*outbuf, "\"", 1); |
177 | 0 | } break; |
178 | | |
179 | 0 | case PLIST_ARRAY: { |
180 | 0 | str_buf_append(*outbuf, "[", 1); |
181 | 0 | node_t ch; |
182 | 0 | uint32_t cnt = 0; |
183 | 0 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { |
184 | 0 | if (cnt > 0) { |
185 | 0 | str_buf_append(*outbuf, ",", 1); |
186 | 0 | } |
187 | 0 | if (prettify) { |
188 | 0 | str_buf_append(*outbuf, "\n", 1); |
189 | 0 | for (i = 0; i <= depth; i++) { |
190 | 0 | str_buf_append(*outbuf, " ", 2); |
191 | 0 | } |
192 | 0 | } |
193 | 0 | plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify, coerce); |
194 | 0 | if (res < 0) { |
195 | 0 | return res; |
196 | 0 | } |
197 | 0 | cnt++; |
198 | 0 | } |
199 | 0 | if (cnt > 0 && prettify) { |
200 | 0 | str_buf_append(*outbuf, "\n", 1); |
201 | 0 | for (i = 0; i < depth; i++) { |
202 | 0 | str_buf_append(*outbuf, " ", 2); |
203 | 0 | } |
204 | 0 | } |
205 | 0 | str_buf_append(*outbuf, "]", 1); |
206 | 0 | } break; |
207 | 0 | case PLIST_DICT: { |
208 | 0 | str_buf_append(*outbuf, "{", 1); |
209 | 0 | node_t ch; |
210 | 0 | uint32_t cnt = 0; |
211 | 0 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { |
212 | 0 | if (cnt > 0 && cnt % 2 == 0) { |
213 | 0 | str_buf_append(*outbuf, ",", 1); |
214 | 0 | } |
215 | 0 | if (cnt % 2 == 0 && prettify) { |
216 | 0 | str_buf_append(*outbuf, "\n", 1); |
217 | 0 | for (i = 0; i <= depth; i++) { |
218 | 0 | str_buf_append(*outbuf, " ", 2); |
219 | 0 | } |
220 | 0 | } |
221 | 0 | plist_err_t res = node_to_json(ch, outbuf, depth+1, prettify, coerce); |
222 | 0 | if (res < 0) { |
223 | 0 | return res; |
224 | 0 | } |
225 | 0 | if (cnt % 2 == 0) { |
226 | 0 | str_buf_append(*outbuf, ":", 1); |
227 | 0 | if (prettify) { |
228 | 0 | str_buf_append(*outbuf, " ", 1); |
229 | 0 | } |
230 | 0 | } |
231 | 0 | cnt++; |
232 | 0 | } |
233 | 0 | if (cnt > 0 && prettify) { |
234 | 0 | str_buf_append(*outbuf, "\n", 1); |
235 | 0 | for (i = 0; i < depth; i++) { |
236 | 0 | str_buf_append(*outbuf, " ", 2); |
237 | 0 | } |
238 | 0 | } |
239 | 0 | str_buf_append(*outbuf, "}", 1); |
240 | 0 | } break; |
241 | 0 | case PLIST_DATA: |
242 | 0 | if (coerce) { |
243 | 0 | size_t b64_len = ((node_data->length + 2) / 3) * 4; |
244 | 0 | char *b64_buf = (char*)malloc(b64_len + 1); |
245 | 0 | if (!b64_buf) { |
246 | 0 | return PLIST_ERR_NO_MEM; |
247 | 0 | } |
248 | 0 | size_t actual_len = base64encode(b64_buf, node_data->buff, node_data->length); |
249 | 0 | str_buf_append(*outbuf, "\"", 1); |
250 | 0 | str_buf_append(*outbuf, b64_buf, actual_len); |
251 | 0 | str_buf_append(*outbuf, "\"", 1); |
252 | 0 | free(b64_buf); |
253 | 0 | } else { |
254 | 0 | PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n"); |
255 | 0 | return PLIST_ERR_FORMAT; |
256 | 0 | } |
257 | 0 | break; |
258 | 0 | case PLIST_DATE: |
259 | 0 | if (coerce) { |
260 | 0 | Time64_T timev; |
261 | 0 | if (plist_real_to_time64(node_data->realval, &timev) < 0) { |
262 | 0 | PLIST_JSON_WRITE_ERR("Encountered invalid date value %f\n", node_data->realval); |
263 | 0 | return PLIST_ERR_INVALID_ARG; |
264 | 0 | } |
265 | 0 | struct TM _btime; |
266 | 0 | struct TM *btime = gmtime64_r(&timev, &_btime); |
267 | 0 | char datebuf[32]; |
268 | 0 | size_t datelen = 0; |
269 | 0 | if (btime) { |
270 | 0 | struct tm _tmcopy; |
271 | 0 | copy_TM64_to_tm(btime, &_tmcopy); |
272 | 0 | datelen = strftime(datebuf, sizeof(datebuf), "%Y-%m-%dT%H:%M:%SZ", &_tmcopy); |
273 | 0 | } |
274 | 0 | if (datelen <= 0) { |
275 | 0 | datelen = snprintf(datebuf, sizeof(datebuf), "1970-01-01T00:00:00Z"); |
276 | 0 | } |
277 | 0 | str_buf_append(*outbuf, "\"", 1); |
278 | 0 | str_buf_append(*outbuf, datebuf, datelen); |
279 | 0 | str_buf_append(*outbuf, "\"", 1); |
280 | 0 | } else { |
281 | 0 | PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n"); |
282 | 0 | return PLIST_ERR_FORMAT; |
283 | 0 | } |
284 | 0 | break; |
285 | 0 | case PLIST_UID: |
286 | 0 | if (coerce) { |
287 | 0 | val = (char*)malloc(64); |
288 | 0 | if (node_data->length == 16) { |
289 | 0 | val_len = snprintf(val, 64, "%" PRIu64, node_data->intval); |
290 | 0 | } else { |
291 | 0 | val_len = snprintf(val, 64, "%" PRIi64, node_data->intval); |
292 | 0 | } |
293 | 0 | str_buf_append(*outbuf, val, val_len); |
294 | 0 | free(val); |
295 | 0 | } else { |
296 | 0 | PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n"); |
297 | 0 | return PLIST_ERR_FORMAT; |
298 | 0 | } |
299 | 0 | break; |
300 | 0 | default: |
301 | 0 | return PLIST_ERR_UNKNOWN; |
302 | 0 | } |
303 | | |
304 | 0 | return PLIST_ERR_SUCCESS; |
305 | 0 | } |
306 | | |
307 | | static plist_err_t _node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce, hashtable_t *visited) |
308 | 0 | { |
309 | 0 | plist_data_t data; |
310 | 0 | if (!node) { |
311 | 0 | return PLIST_ERR_INVALID_ARG; |
312 | 0 | } |
313 | | |
314 | 0 | if (depth > PLIST_MAX_NESTING_DEPTH) { |
315 | 0 | PLIST_JSON_WRITE_ERR("maximum nesting depth (%u) exceeded\n", (unsigned)PLIST_MAX_NESTING_DEPTH); |
316 | 0 | return PLIST_ERR_MAX_NESTING; |
317 | 0 | } |
318 | | |
319 | 0 | if (hash_table_lookup(visited, node)) { |
320 | 0 | PLIST_JSON_WRITE_ERR("circular reference detected\n"); |
321 | 0 | return PLIST_ERR_CIRCULAR_REF; |
322 | 0 | } |
323 | | |
324 | | // mark as visited |
325 | 0 | hash_table_insert(visited, node, (void*)1); |
326 | |
|
327 | 0 | data = plist_get_data(node); |
328 | 0 | if (node->children) { |
329 | 0 | node_t ch; |
330 | 0 | unsigned int n_children = node_n_children(node); |
331 | 0 | for (ch = node_first_child(node); ch; ch = node_next_sibling(ch)) { |
332 | 0 | plist_err_t res = _node_estimate_size(ch, size, depth + 1, prettify, coerce, visited); |
333 | 0 | if (res < 0) { |
334 | 0 | return res; |
335 | 0 | } |
336 | 0 | } |
337 | 0 | switch (data->type) { |
338 | 0 | case PLIST_DICT: |
339 | 0 | *size += 2; // '{' and '}' |
340 | 0 | *size += n_children-1; // number of ':' and ',' |
341 | 0 | if (prettify) { |
342 | 0 | *size += n_children; // number of '\n' and extra space |
343 | 0 | *size += (uint64_t)n_children * (depth+1); // indent for every 2nd child |
344 | 0 | *size += 1; // additional '\n' |
345 | 0 | } |
346 | 0 | break; |
347 | 0 | case PLIST_ARRAY: |
348 | 0 | *size += 2; // '[' and ']' |
349 | 0 | *size += n_children-1; // number of ',' |
350 | 0 | if (prettify) { |
351 | 0 | *size += n_children; // number of '\n' |
352 | 0 | *size += (uint64_t)n_children * ((depth+1)<<1); // indent for every child |
353 | 0 | *size += 1; // additional '\n' |
354 | 0 | } |
355 | 0 | break; |
356 | 0 | default: |
357 | 0 | break; |
358 | 0 | } |
359 | 0 | if (prettify) |
360 | 0 | *size += (depth << 1); // indent for {} and [] |
361 | 0 | } else { |
362 | 0 | switch (data->type) { |
363 | 0 | case PLIST_STRING: |
364 | 0 | case PLIST_KEY: |
365 | 0 | *size += data->length; |
366 | 0 | *size += 2; |
367 | 0 | break; |
368 | 0 | case PLIST_INT: |
369 | 0 | if (data->length == 16) { |
370 | 0 | *size += num_digits_u(data->intval); |
371 | 0 | } else { |
372 | 0 | *size += num_digits_i((int64_t)data->intval); |
373 | 0 | } |
374 | 0 | break; |
375 | 0 | case PLIST_REAL: |
376 | 0 | *size += dtostr(NULL, 0, data->realval); |
377 | 0 | break; |
378 | 0 | case PLIST_BOOLEAN: |
379 | 0 | *size += ((data->boolval) ? 4 : 5); |
380 | 0 | break; |
381 | 0 | case PLIST_NULL: |
382 | 0 | *size += 4; |
383 | 0 | break; |
384 | 0 | case PLIST_DICT: |
385 | 0 | case PLIST_ARRAY: |
386 | 0 | *size += 2; |
387 | 0 | break; |
388 | 0 | case PLIST_DATA: |
389 | 0 | if (coerce) { |
390 | | // base64 encoded string: 2 quotes + ((len+2)/3)*4 base64 chars |
391 | 0 | *size += 2 + ((data->length + 2) / 3) * 4; |
392 | 0 | } else { |
393 | 0 | PLIST_JSON_WRITE_ERR("PLIST_DATA type is not valid for JSON format\n"); |
394 | 0 | return PLIST_ERR_FORMAT; |
395 | 0 | } |
396 | 0 | break; |
397 | 0 | case PLIST_DATE: |
398 | 0 | if (coerce) { |
399 | | // ISO 8601 string: "YYYY-MM-DDTHH:MM:SSZ" = 22 chars max |
400 | 0 | *size += 24; |
401 | 0 | } else { |
402 | 0 | PLIST_JSON_WRITE_ERR("PLIST_DATE type is not valid for JSON format\n"); |
403 | 0 | return PLIST_ERR_FORMAT; |
404 | 0 | } |
405 | 0 | break; |
406 | 0 | case PLIST_UID: |
407 | 0 | if (coerce) { |
408 | | // integer representation |
409 | 0 | if (data->length == 16) { |
410 | 0 | *size += num_digits_u(data->intval); |
411 | 0 | } else { |
412 | 0 | *size += num_digits_i((int64_t)data->intval); |
413 | 0 | } |
414 | 0 | } else { |
415 | 0 | PLIST_JSON_WRITE_ERR("PLIST_UID type is not valid for JSON format\n"); |
416 | 0 | return PLIST_ERR_FORMAT; |
417 | 0 | } |
418 | 0 | break; |
419 | 0 | default: |
420 | 0 | PLIST_JSON_WRITE_ERR("invalid node type encountered\n"); |
421 | 0 | return PLIST_ERR_UNKNOWN; |
422 | 0 | } |
423 | 0 | } |
424 | 0 | return PLIST_ERR_SUCCESS; |
425 | 0 | } |
426 | | |
427 | | static plist_err_t node_estimate_size(node_t node, uint64_t *size, uint32_t depth, int prettify, int coerce) |
428 | 0 | { |
429 | 0 | hashtable_t *visited = hash_table_new(plist_node_ptr_hash, plist_node_ptr_compare, NULL); |
430 | 0 | if (!visited) return PLIST_ERR_NO_MEM; |
431 | 0 | plist_err_t err = _node_estimate_size(node, size, depth, prettify, coerce, visited); |
432 | 0 | hash_table_destroy(visited); |
433 | 0 | return err; |
434 | 0 | } |
435 | | |
436 | | plist_err_t plist_to_json(plist_t plist, char **plist_json, uint32_t* length, int prettify) |
437 | 0 | { |
438 | 0 | plist_write_options_t opts = prettify ? PLIST_OPT_NONE : PLIST_OPT_COMPACT; |
439 | 0 | return plist_to_json_with_options(plist, plist_json, length, opts); |
440 | 0 | } |
441 | | |
442 | | plist_err_t plist_to_json_with_options(plist_t plist, char **plist_json, uint32_t* length, plist_write_options_t options) |
443 | 0 | { |
444 | 0 | uint64_t size = 0; |
445 | 0 | plist_err_t res; |
446 | |
|
447 | 0 | if (!plist || !plist_json || !length) { |
448 | 0 | return PLIST_ERR_INVALID_ARG; |
449 | 0 | } |
450 | | |
451 | 0 | if (!PLIST_IS_DICT(plist) && !PLIST_IS_ARRAY(plist)) { |
452 | 0 | PLIST_JSON_WRITE_ERR("plist data is not valid for JSON format\n"); |
453 | 0 | return PLIST_ERR_FORMAT; |
454 | 0 | } |
455 | | |
456 | 0 | int prettify = !(options & PLIST_OPT_COMPACT); |
457 | 0 | int coerce = options & PLIST_OPT_COERCE; |
458 | |
|
459 | 0 | res = node_estimate_size((node_t)plist, &size, 0, prettify, coerce); |
460 | 0 | if (res < 0) { |
461 | 0 | return res; |
462 | 0 | } |
463 | | |
464 | 0 | strbuf_t *outbuf = str_buf_new(size); |
465 | 0 | if (!outbuf) { |
466 | 0 | PLIST_JSON_WRITE_ERR("Could not allocate output buffer\n"); |
467 | 0 | return PLIST_ERR_NO_MEM; |
468 | 0 | } |
469 | | |
470 | 0 | res = node_to_json((node_t)plist, &outbuf, 0, prettify, coerce); |
471 | 0 | if (res < 0) { |
472 | 0 | str_buf_free(outbuf); |
473 | 0 | *plist_json = NULL; |
474 | 0 | *length = 0; |
475 | 0 | return res; |
476 | 0 | } |
477 | 0 | if (prettify) { |
478 | 0 | str_buf_append(outbuf, "\n", 1); |
479 | 0 | } |
480 | |
|
481 | 0 | str_buf_append(outbuf, "\0", 1); |
482 | |
|
483 | 0 | *plist_json = (char*)outbuf->data; |
484 | 0 | *length = outbuf->len - 1; |
485 | |
|
486 | 0 | outbuf->data = NULL; |
487 | 0 | str_buf_free(outbuf); |
488 | |
|
489 | 0 | return PLIST_ERR_SUCCESS; |
490 | 0 | } |
491 | | |
492 | | typedef struct { |
493 | | jsmntok_t* tokens; |
494 | | int count; |
495 | | plist_err_t err; |
496 | | } jsmntok_info_t; |
497 | | |
498 | | static int64_t parse_decimal(const char* str, const char* str_end, char** endp) |
499 | 0 | { |
500 | 0 | const uint64_t po10i_limit = INT64_MAX / 10; |
501 | 0 | uint64_t MAX = INT64_MAX; |
502 | 0 | uint64_t x = 0; |
503 | 0 | int is_neg = 0; |
504 | 0 | *endp = (char*)str; |
505 | |
|
506 | 0 | if (str[0] == '-') { |
507 | 0 | is_neg = 1; |
508 | 0 | (*endp)++; |
509 | 0 | } else if (str[0] == '+') { |
510 | 0 | (*endp)++; |
511 | 0 | } |
512 | 0 | if (is_neg) { |
513 | 0 | MAX++; |
514 | 0 | } |
515 | 0 | while (*endp < str_end && isdigit(**endp)) { |
516 | 0 | if (x > po10i_limit) { |
517 | 0 | x = MAX; |
518 | 0 | break; |
519 | 0 | } |
520 | 0 | x = x * 10; |
521 | 0 | unsigned int add = (**endp - '0'); |
522 | 0 | if (x + add > MAX) { |
523 | 0 | x = MAX; |
524 | 0 | break; |
525 | 0 | } |
526 | 0 | x += add; |
527 | 0 | (*endp)++; |
528 | 0 | } |
529 | | |
530 | | // swallow the rest of the digits in case we dropped out early |
531 | 0 | while (*endp < str_end && isdigit(**endp)) (*endp)++; |
532 | |
|
533 | 0 | int64_t result = x; |
534 | 0 | if (is_neg) { |
535 | 0 | if (x == MAX) { |
536 | 0 | result = INT64_MIN; |
537 | 0 | } else { |
538 | 0 | result = -(int64_t)x; |
539 | 0 | } |
540 | 0 | } |
541 | 0 | return result; |
542 | 0 | } |
543 | | |
544 | | static plist_t parse_primitive(const char* js, jsmntok_info_t* ti, int* index) |
545 | 0 | { |
546 | 0 | if (ti->tokens[*index].type != JSMN_PRIMITIVE) { |
547 | 0 | PLIST_JSON_ERR("%s: token type != JSMN_PRIMITIVE\n", __func__); |
548 | 0 | return NULL; |
549 | 0 | } |
550 | 0 | plist_t val = NULL; |
551 | 0 | const char* str_val = js + ti->tokens[*index].start; |
552 | 0 | const char* str_end = js + ti->tokens[*index].end; |
553 | 0 | size_t str_len = ti->tokens[*index].end - ti->tokens[*index].start; |
554 | 0 | if (!strncmp("false", str_val, str_len)) { |
555 | 0 | val = plist_new_bool(0); |
556 | 0 | } else if (!strncmp("true", str_val, str_len)) { |
557 | 0 | val = plist_new_bool(1); |
558 | 0 | } else if (!strncmp("null", str_val, str_len)) { |
559 | 0 | plist_data_t data = plist_new_plist_data(); |
560 | 0 | if (!data) { |
561 | 0 | PLIST_JSON_ERR("%s: failed to allocate plist data\n", __func__); |
562 | 0 | return NULL; |
563 | 0 | } |
564 | 0 | data->type = PLIST_NULL; |
565 | 0 | val = plist_new_node(data); |
566 | 0 | } else if (isdigit(str_val[0]) || (str_val[0] == '-' && str_val+1 < str_end && isdigit(str_val[1]))) { |
567 | 0 | char* endp = (char*)str_val; |
568 | 0 | int is_neg = (str_val[0] == '-'); |
569 | 0 | int64_t intpart = parse_decimal(str_val, str_end, &endp); |
570 | 0 | if (endp >= str_end) { |
571 | | /* integer */ |
572 | 0 | if (is_neg || intpart <= INT64_MAX) { |
573 | 0 | val = plist_new_int(intpart); |
574 | 0 | } else { |
575 | 0 | val = plist_new_uint((uint64_t)intpart); |
576 | 0 | } |
577 | 0 | } else if ((*endp == '.' && endp+1 < str_end && isdigit(*(endp+1))) || ((*endp == 'e' || *endp == 'E') && endp+1 < str_end && (isdigit(*(endp+1)) || (((*(endp+1) == '-') || (*(endp+1) == '+')) && endp+2 < str_end && isdigit(*(endp+2)))))) { |
578 | | /* floating point */ |
579 | 0 | double dval = (double)intpart; |
580 | 0 | char* fendp = endp; |
581 | 0 | int err = 0; |
582 | 0 | do { |
583 | 0 | if (*endp == '.') { |
584 | 0 | fendp++; |
585 | 0 | double frac = 0; |
586 | 0 | double p = 0.1; |
587 | 0 | while (fendp < str_end && isdigit(*fendp)) { |
588 | 0 | frac = frac + (*fendp - '0') * p; |
589 | 0 | p *= 0.1; |
590 | 0 | fendp++; |
591 | 0 | } |
592 | 0 | if (is_neg) { |
593 | 0 | dval -= frac; |
594 | 0 | } else { |
595 | 0 | dval += frac; |
596 | 0 | } |
597 | 0 | } |
598 | 0 | if (fendp >= str_end) { |
599 | 0 | break; |
600 | 0 | } |
601 | 0 | if (fendp+1 < str_end && (*fendp == 'e' || *fendp == 'E') && (isdigit(*(fendp+1)) || (((*(fendp+1) == '-') || (*(fendp+1) == '+')) && fendp+2 < str_end && isdigit(*(fendp+2))))) { |
602 | 0 | int64_t exp = parse_decimal(fendp+1, str_end, &fendp); |
603 | 0 | dval = dval * pow(10, (double)exp); |
604 | 0 | } else { |
605 | 0 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing floating point value\n", __func__, (int)(fendp - js)); |
606 | 0 | err++; |
607 | 0 | } |
608 | 0 | } while (0); |
609 | 0 | if (!err) { |
610 | 0 | if (isinf(dval) || isnan(dval)) { |
611 | 0 | PLIST_JSON_ERR("%s: unrepresentable floating point value at offset %d when parsing numerical value\n", __func__, (int)(str_val - js)); |
612 | 0 | } else { |
613 | 0 | val = plist_new_real(dval); |
614 | 0 | } |
615 | 0 | } |
616 | 0 | } else { |
617 | 0 | PLIST_JSON_ERR("%s: invalid character at offset %d when parsing numerical value\n", __func__, (int)(endp - js)); |
618 | 0 | } |
619 | 0 | } else { |
620 | 0 | PLIST_JSON_ERR("%s: invalid primitive value '%.*s' encountered\n", __func__, (int)str_len, str_val); |
621 | 0 | } |
622 | 0 | if (!val) { |
623 | 0 | PLIST_JSON_ERR("%s: failed to create node\n", __func__); |
624 | 0 | return NULL; |
625 | 0 | } |
626 | 0 | (*index)++; |
627 | 0 | return val; |
628 | 0 | } |
629 | | |
630 | | static char* unescape_string(const char* str_val, size_t str_len, size_t *new_len) |
631 | 0 | { |
632 | 0 | char* strval = strndup(str_val, str_len); |
633 | 0 | if (!strval) return NULL; |
634 | 0 | size_t i = 0; |
635 | 0 | while (i < str_len) { |
636 | 0 | if (strval[i] == '\\' && i < str_len-1) { |
637 | 0 | switch (strval[i+1]) { |
638 | 0 | case '\"': case '/' : case '\\' : case 'b' : |
639 | 0 | case 'f' : case 'r' : case 'n' : case 't' : |
640 | 0 | memmove(strval+i, strval+i+1, str_len - (i+1)); |
641 | 0 | str_len--; |
642 | 0 | switch (strval[i]) { |
643 | 0 | case 'b': |
644 | 0 | strval[i] = '\b'; |
645 | 0 | break; |
646 | 0 | case 'f': |
647 | 0 | strval[i] = '\f'; |
648 | 0 | break; |
649 | 0 | case 'r': |
650 | 0 | strval[i] = '\r'; |
651 | 0 | break; |
652 | 0 | case 'n': |
653 | 0 | strval[i] = '\n'; |
654 | 0 | break; |
655 | 0 | case 't': |
656 | 0 | strval[i] = '\t'; |
657 | 0 | break; |
658 | 0 | default: |
659 | 0 | break; |
660 | 0 | } |
661 | 0 | break; |
662 | 0 | case 'u': { |
663 | 0 | unsigned int val = 0; |
664 | 0 | if (str_len-(i+2) < 4) { |
665 | 0 | PLIST_JSON_ERR("%s: invalid escape sequence '%s' (too short)\n", __func__, strval+i); |
666 | 0 | free(strval); |
667 | 0 | return NULL; |
668 | 0 | } |
669 | 0 | if (!(isxdigit(strval[i+2]) && isxdigit(strval[i+3]) && isxdigit(strval[i+4]) && isxdigit(strval[i+5])) || sscanf(strval+i+2, "%04x", &val) != 1) { |
670 | 0 | PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 6, strval+i); |
671 | 0 | free(strval); |
672 | 0 | return NULL; |
673 | 0 | } |
674 | 0 | int bytelen = 0; |
675 | 0 | if (val >= 0x800) { |
676 | | /* three bytes */ |
677 | 0 | strval[i] = (char)(0xE0 + ((val >> 12) & 0xF)); |
678 | 0 | strval[i+1] = (char)(0x80 + ((val >> 6) & 0x3F)); |
679 | 0 | strval[i+2] = (char)(0x80 + (val & 0x3F)); |
680 | 0 | bytelen = 3; |
681 | 0 | } else if (val >= 0x80) { |
682 | | /* two bytes */ |
683 | 0 | strval[i] = (char)(0xC0 + ((val >> 6) & 0x1F)); |
684 | 0 | strval[i+1] = (char)(0x80 + (val & 0x3F)); |
685 | 0 | bytelen = 2; |
686 | 0 | } else { |
687 | | /* one byte */ |
688 | 0 | strval[i] = (char)(val & 0x7F); |
689 | 0 | bytelen = 1; |
690 | 0 | } |
691 | 0 | memmove(strval+i+bytelen, strval+i+6, str_len - (i+5)); |
692 | 0 | str_len -= (6-bytelen); |
693 | 0 | } break; |
694 | 0 | default: |
695 | 0 | PLIST_JSON_ERR("%s: invalid escape sequence '%.*s'\n", __func__, 2, strval+i); |
696 | 0 | free(strval); |
697 | 0 | return NULL; |
698 | 0 | } |
699 | 0 | } |
700 | 0 | i++; |
701 | 0 | } |
702 | 0 | strval[str_len] = '\0'; |
703 | 0 | if (new_len) { |
704 | 0 | *new_len = str_len; |
705 | 0 | } |
706 | 0 | return strval; |
707 | 0 | } |
708 | | |
709 | | static plist_t parse_string(const char* js, jsmntok_info_t* ti, int* index) |
710 | 0 | { |
711 | 0 | if (ti->tokens[*index].type != JSMN_STRING) { |
712 | 0 | PLIST_JSON_ERR("%s: token type != JSMN_STRING\n", __func__); |
713 | 0 | return NULL; |
714 | 0 | } |
715 | | |
716 | 0 | size_t str_len = 0; ; |
717 | 0 | char* strval = unescape_string(js + ti->tokens[*index].start, ti->tokens[*index].end - ti->tokens[*index].start, &str_len); |
718 | 0 | if (!strval) { |
719 | 0 | return NULL; |
720 | 0 | } |
721 | 0 | plist_t node; |
722 | |
|
723 | 0 | plist_data_t data = plist_new_plist_data(); |
724 | 0 | if (!data) { |
725 | 0 | free(strval); |
726 | 0 | PLIST_JSON_ERR("%s: failed to allocate plist data\n", __func__); |
727 | 0 | return NULL; |
728 | 0 | } |
729 | 0 | data->type = PLIST_STRING; |
730 | 0 | data->strval = strval; |
731 | 0 | data->length = str_len; |
732 | 0 | node = plist_new_node(data); |
733 | 0 | if (!node) { |
734 | 0 | plist_free_data(data); |
735 | 0 | PLIST_JSON_ERR("%s: failed to create node\n", __func__); |
736 | 0 | return NULL; |
737 | 0 | } |
738 | | |
739 | 0 | (*index)++; |
740 | 0 | return node; |
741 | 0 | } |
742 | | |
743 | | static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth); |
744 | | |
745 | | static plist_t parse_array(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth) |
746 | 0 | { |
747 | 0 | if (ti->tokens[*index].type != JSMN_ARRAY) { |
748 | 0 | PLIST_JSON_ERR("%s: token type != JSMN_ARRAY\n", __func__); |
749 | 0 | ti->err = PLIST_ERR_PARSE; |
750 | 0 | return NULL; |
751 | 0 | } |
752 | 0 | if (depth > PLIST_MAX_NESTING_DEPTH) { |
753 | 0 | PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH); |
754 | 0 | ti->err = PLIST_ERR_MAX_NESTING; |
755 | 0 | return NULL; |
756 | 0 | } |
757 | 0 | plist_t arr = plist_new_array(); |
758 | 0 | if (!arr) { |
759 | 0 | PLIST_JSON_ERR("%s: failed to create array node\n", __func__); |
760 | 0 | ti->err = PLIST_ERR_NO_MEM; |
761 | 0 | return NULL; |
762 | 0 | } |
763 | 0 | size_t num_tokens = ti->tokens[*index].size; |
764 | 0 | size_t num; |
765 | 0 | int j = (*index)+1; |
766 | 0 | for (num = 0; num < num_tokens; num++) { |
767 | 0 | if (j >= ti->count) { |
768 | 0 | PLIST_JSON_ERR("%s: token index out of valid range\n", __func__); |
769 | 0 | plist_free(arr); |
770 | 0 | ti->err = PLIST_ERR_PARSE; |
771 | 0 | return NULL; |
772 | 0 | } |
773 | 0 | plist_t val = NULL; |
774 | 0 | switch (ti->tokens[j].type) { |
775 | 0 | case JSMN_OBJECT: |
776 | 0 | val = parse_object(js, ti, &j, depth+1); |
777 | 0 | break; |
778 | 0 | case JSMN_ARRAY: |
779 | 0 | val = parse_array(js, ti, &j, depth+1); |
780 | 0 | break; |
781 | 0 | case JSMN_STRING: |
782 | 0 | val = parse_string(js, ti, &j); |
783 | 0 | break; |
784 | 0 | case JSMN_PRIMITIVE: |
785 | 0 | val = parse_primitive(js, ti, &j); |
786 | 0 | break; |
787 | 0 | default: |
788 | 0 | break; |
789 | 0 | } |
790 | 0 | if (val) { |
791 | 0 | plist_array_append_item(arr, val); |
792 | | // if append failed, val still has no parent, free it and abort |
793 | 0 | if (((node_t)val)->parent == NULL) { |
794 | 0 | plist_free(val); |
795 | 0 | plist_free(arr); |
796 | 0 | ti->err = PLIST_ERR_NO_MEM; |
797 | 0 | return NULL; |
798 | 0 | } |
799 | 0 | } else { |
800 | 0 | plist_free(arr); |
801 | 0 | ti->err = PLIST_ERR_PARSE; |
802 | 0 | return NULL; |
803 | 0 | } |
804 | 0 | } |
805 | 0 | *(index) = j; |
806 | 0 | return arr; |
807 | 0 | } |
808 | | |
809 | | static plist_t parse_object(const char* js, jsmntok_info_t* ti, int* index, uint32_t depth) |
810 | 0 | { |
811 | 0 | if (ti->tokens[*index].type != JSMN_OBJECT) { |
812 | 0 | PLIST_JSON_ERR("%s: token type != JSMN_OBJECT\n", __func__); |
813 | 0 | ti->err = PLIST_ERR_PARSE; |
814 | 0 | return NULL; |
815 | 0 | } |
816 | 0 | if (depth > PLIST_MAX_NESTING_DEPTH) { |
817 | 0 | PLIST_JSON_ERR("%s: maximum nesting depth (%u) exceeded\n", __func__, (unsigned)PLIST_MAX_NESTING_DEPTH); |
818 | 0 | ti->err = PLIST_ERR_MAX_NESTING; |
819 | 0 | return NULL; |
820 | 0 | } |
821 | 0 | size_t num_tokens = ti->tokens[*index].size; |
822 | 0 | size_t num; |
823 | 0 | int j = (*index)+1; |
824 | 0 | if (num_tokens % 2 != 0) { |
825 | 0 | PLIST_JSON_ERR("%s: number of children must be even\n", __func__); |
826 | 0 | ti->err = PLIST_ERR_PARSE; |
827 | 0 | return NULL; |
828 | 0 | } |
829 | 0 | plist_t obj = plist_new_dict(); |
830 | 0 | if (!obj) { |
831 | 0 | PLIST_JSON_ERR("%s: failed to create dict node\n", __func__); |
832 | 0 | ti->err = PLIST_ERR_NO_MEM; |
833 | 0 | return NULL; |
834 | 0 | } |
835 | 0 | for (num = 0; num < num_tokens; num++) { |
836 | 0 | if (j+1 >= ti->count) { |
837 | 0 | PLIST_JSON_ERR("%s: token index out of valid range\n", __func__); |
838 | 0 | plist_free(obj); |
839 | 0 | ti->err = PLIST_ERR_PARSE; |
840 | 0 | return NULL; |
841 | 0 | } |
842 | 0 | if (ti->tokens[j].type == JSMN_STRING) { |
843 | 0 | char* key = unescape_string(js + ti->tokens[j].start, ti->tokens[j].end - ti->tokens[j].start, NULL); |
844 | 0 | if (!key) { |
845 | 0 | plist_free(obj); |
846 | 0 | ti->err = PLIST_ERR_PARSE; |
847 | 0 | return NULL; |
848 | 0 | } |
849 | 0 | plist_t val = NULL; |
850 | 0 | j++; |
851 | 0 | num++; |
852 | 0 | switch (ti->tokens[j].type) { |
853 | 0 | case JSMN_OBJECT: |
854 | 0 | val = parse_object(js, ti, &j, depth+1); |
855 | 0 | break; |
856 | 0 | case JSMN_ARRAY: |
857 | 0 | val = parse_array(js, ti, &j, depth+1); |
858 | 0 | break; |
859 | 0 | case JSMN_STRING: |
860 | 0 | val = parse_string(js, ti, &j); |
861 | 0 | break; |
862 | 0 | case JSMN_PRIMITIVE: |
863 | 0 | val = parse_primitive(js, ti, &j); |
864 | 0 | break; |
865 | 0 | default: |
866 | 0 | break; |
867 | 0 | } |
868 | 0 | if (val) { |
869 | 0 | plist_dict_set_item(obj, key, val); |
870 | | // if set failed, val still has no parent, free it and abort |
871 | 0 | if (((node_t)val)->parent == NULL) { |
872 | 0 | plist_free(val); |
873 | 0 | free(key); |
874 | 0 | plist_free(obj); |
875 | 0 | ti->err = PLIST_ERR_NO_MEM; |
876 | 0 | return NULL; |
877 | 0 | } |
878 | 0 | } else { |
879 | 0 | free(key); |
880 | 0 | plist_free(obj); |
881 | 0 | ti->err = PLIST_ERR_PARSE; |
882 | 0 | return NULL; |
883 | 0 | } |
884 | 0 | free(key); |
885 | 0 | } else { |
886 | 0 | PLIST_JSON_ERR("%s: keys must be of type STRING\n", __func__); |
887 | 0 | plist_free(obj); |
888 | 0 | ti->err = PLIST_ERR_PARSE; |
889 | 0 | return NULL; |
890 | 0 | } |
891 | 0 | } |
892 | 0 | (*index) = j; |
893 | 0 | return obj; |
894 | 0 | } |
895 | | |
896 | | plist_err_t plist_from_json(const char *json, uint32_t length, plist_t * plist) |
897 | 0 | { |
898 | 0 | if (!plist) { |
899 | 0 | return PLIST_ERR_INVALID_ARG; |
900 | 0 | } |
901 | 0 | *plist = NULL; |
902 | 0 | if (!json || (length == 0)) { |
903 | 0 | return PLIST_ERR_INVALID_ARG; |
904 | 0 | } |
905 | | |
906 | 0 | jsmn_parser parser; |
907 | 0 | jsmn_init(&parser); |
908 | 0 | unsigned int maxtoks = 256; |
909 | 0 | unsigned int curtoks = 0; |
910 | 0 | int r = 0; |
911 | 0 | jsmntok_t *tokens = NULL; |
912 | |
|
913 | 0 | do { |
914 | 0 | jsmntok_t* newtokens = (jsmntok_t*)realloc(tokens, sizeof(jsmntok_t)*maxtoks); |
915 | 0 | if (!newtokens) { |
916 | 0 | free(tokens); |
917 | 0 | PLIST_JSON_ERR("%s: Out of memory\n", __func__); |
918 | 0 | return PLIST_ERR_NO_MEM; |
919 | 0 | } |
920 | 0 | memset((unsigned char*)newtokens + sizeof(jsmntok_t)*curtoks, '\0', sizeof(jsmntok_t)*(maxtoks-curtoks)); |
921 | 0 | tokens = newtokens; |
922 | 0 | curtoks = maxtoks; |
923 | |
|
924 | 0 | r = jsmn_parse(&parser, json, length, tokens, maxtoks); |
925 | 0 | if (r == JSMN_ERROR_NOMEM) { |
926 | 0 | if (maxtoks > (unsigned int)INT_MAX - 16) { |
927 | 0 | free(tokens); |
928 | 0 | return PLIST_ERR_NO_MEM; |
929 | 0 | } |
930 | 0 | maxtoks+=16; |
931 | 0 | continue; |
932 | 0 | } else if (r < 0) { |
933 | 0 | break; |
934 | 0 | } |
935 | 0 | } while (r == JSMN_ERROR_NOMEM); |
936 | | |
937 | 0 | switch(r) { |
938 | 0 | case JSMN_ERROR_NOMEM: |
939 | 0 | PLIST_JSON_ERR("%s: Out of memory...\n", __func__); |
940 | 0 | free(tokens); |
941 | 0 | return PLIST_ERR_NO_MEM; |
942 | 0 | case JSMN_ERROR_INVAL: |
943 | 0 | PLIST_JSON_ERR("%s: Invalid character inside JSON string\n", __func__); |
944 | 0 | free(tokens); |
945 | 0 | return PLIST_ERR_PARSE; |
946 | 0 | case JSMN_ERROR_PART: |
947 | 0 | PLIST_JSON_ERR("%s: Incomplete JSON, more bytes expected\n", __func__); |
948 | 0 | free(tokens); |
949 | 0 | return PLIST_ERR_PARSE; |
950 | 0 | case JSMN_ERROR_LIMIT: |
951 | 0 | PLIST_JSON_ERR("%s: Input data too large\n", __func__); |
952 | 0 | free(tokens); |
953 | 0 | return PLIST_ERR_PARSE; |
954 | 0 | default: |
955 | 0 | break; |
956 | 0 | } |
957 | | |
958 | 0 | int startindex = 0; |
959 | 0 | jsmntok_info_t ti = { tokens, parser.toknext, PLIST_ERR_SUCCESS }; |
960 | 0 | switch (tokens[startindex].type) { |
961 | 0 | case JSMN_PRIMITIVE: |
962 | 0 | *plist = parse_primitive(json, &ti, &startindex); |
963 | 0 | break; |
964 | 0 | case JSMN_STRING: |
965 | 0 | *plist = parse_string(json, &ti, &startindex); |
966 | 0 | break; |
967 | 0 | case JSMN_ARRAY: |
968 | 0 | *plist = parse_array(json, &ti, &startindex, 0); |
969 | 0 | break; |
970 | 0 | case JSMN_OBJECT: |
971 | 0 | *plist = parse_object(json, &ti, &startindex, 0); |
972 | 0 | break; |
973 | 0 | default: |
974 | 0 | break; |
975 | 0 | } |
976 | 0 | free(tokens); |
977 | 0 | if (!*plist) { |
978 | 0 | return (ti.err != PLIST_ERR_SUCCESS) ? ti.err : PLIST_ERR_PARSE; |
979 | 0 | } |
980 | 0 | return PLIST_ERR_SUCCESS; |
981 | 0 | } |