/src/fwupd/libfwupd/fwupd-json-parser.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2025 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | | #include "config.h" |
8 | | |
9 | | #include "fwupd-error.h" |
10 | | #include "fwupd-json-array-private.h" |
11 | | #include "fwupd-json-node-private.h" |
12 | | #include "fwupd-json-object-private.h" |
13 | | #include "fwupd-json-parser.h" |
14 | | |
15 | | /** |
16 | | * FwupdJsonParser: |
17 | | * |
18 | | * A streaming tokenizer JSON parser that is resistant to malicious input. |
19 | | * |
20 | | * One item of note is that most of the JSON string methods actually return a #GRefString -- which |
21 | | * can be used to avoid lots of tiny memory allocation when parsing JSON into other objects. |
22 | | * |
23 | | * See also: [struct@FwupdJsonArray] [struct@FwupdJsonObject] [struct@FwupdJsonNode] |
24 | | */ |
25 | | |
26 | | struct _FwupdJsonParser { |
27 | | GObject parent_instance; |
28 | | guint max_depth; |
29 | | guint max_items; |
30 | | guint max_quoted; |
31 | | }; |
32 | | |
33 | 12.1k | G_DEFINE_TYPE(FwupdJsonParser, fwupd_json_parser, G_TYPE_OBJECT) |
34 | 12.1k | |
35 | 12.1k | #define FWUPD_JSON_PARSER_NEWLINE_MAX 5 |
36 | | |
37 | 12.9k | #define FWUPD_JSON_PARSER_INDENT_MAX 8 /* per depth */ |
38 | | |
39 | | /** |
40 | | * fwupd_json_parser_set_max_depth: |
41 | | * @self: a #FwupdJsonParser |
42 | | * @max_depth: max nesting depth |
43 | | * |
44 | | * Sets the maximum nesting depth. |
45 | | * |
46 | | * The default maximum depth is %G_MAXUINT16, but users of #FwupdJsonParser should use this function |
47 | | * to set a better limit. |
48 | | * |
49 | | * Since: 2.1.1 |
50 | | **/ |
51 | | void |
52 | | fwupd_json_parser_set_max_depth(FwupdJsonParser *self, guint max_depth) |
53 | 2.42k | { |
54 | 2.42k | g_return_if_fail(FWUPD_IS_JSON_PARSER(self)); |
55 | 2.42k | self->max_depth = max_depth; |
56 | 2.42k | } |
57 | | |
58 | | /** |
59 | | * fwupd_json_parser_set_max_items: |
60 | | * @self: a #FwupdJsonParser |
61 | | * @max_items: max items |
62 | | * |
63 | | * Sets the maximum number of items in an array or object. |
64 | | * |
65 | | * The default maximum items is %G_MAXUINT16, but users of #FwupdJsonParser should use this function |
66 | | * to set a better limit. |
67 | | * |
68 | | * Since: 2.1.1 |
69 | | **/ |
70 | | void |
71 | | fwupd_json_parser_set_max_items(FwupdJsonParser *self, guint max_items) |
72 | 2.42k | { |
73 | 2.42k | g_return_if_fail(FWUPD_IS_JSON_PARSER(self)); |
74 | 2.42k | self->max_items = max_items; |
75 | 2.42k | } |
76 | | |
77 | | /** |
78 | | * fwupd_json_parser_set_max_quoted: |
79 | | * @self: a #FwupdJsonParser |
80 | | * @max_quoted: maximum size of a quoted string |
81 | | * |
82 | | * Sets the maximum size of a quoted string. |
83 | | * |
84 | | * The default maximum quoted string length is %G_MAXUINT16, but users of #FwupdJsonParser should |
85 | | * use this function to set a better limit. |
86 | | * |
87 | | * Since: 2.1.1 |
88 | | **/ |
89 | | void |
90 | | fwupd_json_parser_set_max_quoted(FwupdJsonParser *self, guint max_quoted) |
91 | 2.42k | { |
92 | 2.42k | g_return_if_fail(FWUPD_IS_JSON_PARSER(self)); |
93 | 2.42k | self->max_quoted = max_quoted; |
94 | 2.42k | } |
95 | | |
96 | | typedef enum { |
97 | | FWUPD_JSON_PARSER_TOKEN_INVALID = 0, |
98 | | FWUPD_JSON_PARSER_TOKEN_NULL = '0', |
99 | | FWUPD_JSON_PARSER_TOKEN_RAW = 'b', |
100 | | FWUPD_JSON_PARSER_TOKEN_STRING = '\"', |
101 | | FWUPD_JSON_PARSER_TOKEN_OBJECT_START = '{', |
102 | | FWUPD_JSON_PARSER_TOKEN_OBJECT_END = '}', |
103 | | FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM = ':', |
104 | | FWUPD_JSON_PARSER_TOKEN_ARRAY_START = '[', |
105 | | FWUPD_JSON_PARSER_TOKEN_ARRAY_END = ']', |
106 | | } FwupdJsonParserToken; |
107 | | |
108 | | typedef struct { |
109 | | FwupdJsonLoadFlags flags; |
110 | | GByteArray *buf; |
111 | | gsize buf_offset; /* into @buf */ |
112 | | GInputStream *stream; |
113 | | GString *acc; |
114 | | guint max_quoted; |
115 | | gboolean is_quoted; |
116 | | gboolean is_escape; |
117 | | guint linecnt; |
118 | | guint newlinecnt; |
119 | | guint whitespacecnt; |
120 | | guint depth; |
121 | | } FwupdJsonParserHelper; |
122 | | |
123 | | static FwupdJsonParserHelper * |
124 | | fwupd_json_parser_helper_new(FwupdJsonParser *self) |
125 | 2.42k | { |
126 | 2.42k | FwupdJsonParserHelper *helper = g_new0(FwupdJsonParserHelper, 1); |
127 | 2.42k | helper->max_quoted = self->max_quoted; |
128 | 2.42k | helper->linecnt = 1; |
129 | 2.42k | helper->buf = g_byte_array_new(); |
130 | 2.42k | helper->acc = g_string_sized_new(128); |
131 | 2.42k | helper->buf_offset = G_MAXSIZE; |
132 | 2.42k | g_byte_array_set_size(helper->buf, 32 * 1024); |
133 | 2.42k | return helper; |
134 | 2.42k | } |
135 | | |
136 | | static void |
137 | | fwupd_json_parser_helper_free(FwupdJsonParserHelper *helper) |
138 | 2.42k | { |
139 | 2.42k | if (helper->stream != NULL) |
140 | 2.42k | g_object_unref(helper->stream); |
141 | 2.42k | g_byte_array_unref(helper->buf); |
142 | 2.42k | g_string_free(helper->acc, TRUE); |
143 | 2.42k | g_free(helper); |
144 | 2.42k | } |
145 | | |
146 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdJsonParserHelper, fwupd_json_parser_helper_free) |
147 | | |
148 | | static void |
149 | | fwupd_json_parser_helper_dump_acc(FwupdJsonParserHelper *helper, |
150 | | FwupdJsonParserToken *token, |
151 | | GRefString **str) |
152 | 79.5k | { |
153 | 79.5k | if (helper->is_quoted) { |
154 | 18.7k | *token = FWUPD_JSON_PARSER_TOKEN_STRING; |
155 | 18.7k | if (str != NULL) |
156 | 18.6k | *str = g_ref_string_new_len(helper->acc->str, helper->acc->len); |
157 | 60.7k | } else { |
158 | 60.7k | if (helper->acc->len == 0) |
159 | 51.3k | return; |
160 | 9.40k | if (g_ascii_strncasecmp(helper->acc->str, "null", helper->acc->len) == 0) { |
161 | 1.49k | *token = FWUPD_JSON_PARSER_TOKEN_NULL; |
162 | 7.91k | } else { |
163 | 7.91k | *token = FWUPD_JSON_PARSER_TOKEN_RAW; |
164 | 7.91k | if (str != NULL) |
165 | 7.91k | *str = g_ref_string_new_len(helper->acc->str, helper->acc->len); |
166 | 7.91k | } |
167 | 9.40k | } |
168 | 28.1k | g_string_truncate(helper->acc, 0); |
169 | 28.1k | } |
170 | | |
171 | | static gboolean |
172 | | fwupd_json_parser_helper_slurp(FwupdJsonParserHelper *helper, GError **error) |
173 | 5.64k | { |
174 | 5.64k | gssize rc; |
175 | | |
176 | 5.64k | rc = g_input_stream_read(helper->stream, helper->buf->data, helper->buf->len, NULL, error); |
177 | 5.64k | if (rc < 0) { |
178 | 0 | fwupd_error_convert(error); |
179 | 0 | return FALSE; |
180 | 0 | } |
181 | 5.64k | if (rc == 0) { |
182 | 885 | g_set_error_literal(error, |
183 | 885 | FWUPD_ERROR, |
184 | 885 | FWUPD_ERROR_INVALID_DATA, |
185 | 885 | "incomplete data from stream"); |
186 | 885 | return FALSE; |
187 | 885 | } |
188 | 4.76k | g_byte_array_set_size(helper->buf, rc); |
189 | | |
190 | | /* success */ |
191 | 4.76k | helper->buf_offset = 0; |
192 | 4.76k | return TRUE; |
193 | 5.64k | } |
194 | | |
195 | | static gchar |
196 | | fwupd_json_parser_unescape_char(gchar data) |
197 | 1.33k | { |
198 | 1.33k | if (data == 'n') |
199 | 221 | return '\n'; |
200 | 1.11k | if (data == 't') |
201 | 324 | return '\t'; |
202 | 788 | if (data == '\\') |
203 | 337 | return '\\'; |
204 | 451 | if (data == '\"') |
205 | 428 | return '\"'; |
206 | 23 | return 0; |
207 | 451 | } |
208 | | |
209 | | static gboolean |
210 | | fwupd_json_parser_helper_get_next_token_chunk(FwupdJsonParserHelper *helper, |
211 | | FwupdJsonParserToken *token, |
212 | | GRefString **str, |
213 | | gboolean *buf_offset_enable, |
214 | | GError **error) |
215 | 82.7M | { |
216 | 82.7M | gchar data; |
217 | | |
218 | | /* need more data */ |
219 | 82.7M | if (G_UNLIKELY(helper->buf_offset >= helper->buf->len)) { |
220 | 5.64k | if (!fwupd_json_parser_helper_slurp(helper, error)) |
221 | 885 | return FALSE; |
222 | 5.64k | } |
223 | 82.7M | data = helper->buf->data[helper->buf_offset]; |
224 | | |
225 | | /* quotes */ |
226 | 82.7M | if (!helper->is_escape && data == '"') { |
227 | 37.5k | if (helper->is_quoted) { |
228 | 18.7k | fwupd_json_parser_helper_dump_acc(helper, token, str); |
229 | 18.7k | helper->is_quoted = FALSE; |
230 | 18.7k | return TRUE; |
231 | 18.7k | } |
232 | 18.8k | helper->is_quoted = TRUE; |
233 | 18.8k | helper->newlinecnt = 0; |
234 | 18.8k | helper->whitespacecnt = 0; |
235 | 18.8k | return TRUE; |
236 | 37.5k | } |
237 | 82.6M | if (helper->is_quoted) { |
238 | | /* escape char */ |
239 | 5.93k | if (!helper->is_escape && data == '\\') { |
240 | 1.34k | helper->is_escape = TRUE; |
241 | 1.34k | helper->newlinecnt = 0; |
242 | 1.34k | return TRUE; |
243 | 1.34k | } |
244 | 4.58k | if (G_UNLIKELY(helper->is_escape)) { |
245 | 1.33k | data = fwupd_json_parser_unescape_char(data); |
246 | 1.33k | if (G_UNLIKELY(data == 0)) { |
247 | 23 | g_set_error(error, |
248 | 23 | FWUPD_ERROR, |
249 | 23 | FWUPD_ERROR_INVALID_DATA, |
250 | 23 | "invalid escape char '%c'", |
251 | 23 | data); |
252 | 23 | return FALSE; |
253 | 23 | } |
254 | 1.31k | helper->is_escape = FALSE; |
255 | 1.31k | } |
256 | | |
257 | | /* save acc */ |
258 | 4.56k | helper->newlinecnt = 0; |
259 | 4.56k | helper->whitespacecnt = 0; |
260 | 4.56k | g_string_append_c(helper->acc, data); |
261 | 4.56k | if (G_UNLIKELY(helper->max_quoted > 0 && helper->acc->len > helper->max_quoted)) { |
262 | 53 | g_set_error(error, |
263 | 53 | FWUPD_ERROR, |
264 | 53 | FWUPD_ERROR_INVALID_DATA, |
265 | 53 | "token too long, limit was %u", |
266 | 53 | helper->max_quoted); |
267 | 53 | return FALSE; |
268 | 53 | } |
269 | 4.51k | return TRUE; |
270 | 4.56k | } |
271 | | |
272 | | /* newline, for error messages */ |
273 | 82.6M | if (data == '\n') { |
274 | 279 | helper->linecnt++; |
275 | 279 | if (helper->newlinecnt++ > FWUPD_JSON_PARSER_NEWLINE_MAX) { |
276 | 1 | g_set_error(error, |
277 | 1 | FWUPD_ERROR, |
278 | 1 | FWUPD_ERROR_INVALID_DATA, |
279 | 1 | "too many newlines, limit was %u", |
280 | 1 | (guint)FWUPD_JSON_PARSER_NEWLINE_MAX); |
281 | 1 | return FALSE; |
282 | 1 | } |
283 | 278 | fwupd_json_parser_helper_dump_acc(helper, token, str); |
284 | 278 | return TRUE; |
285 | 279 | } |
286 | 82.6M | helper->newlinecnt = 0; |
287 | | |
288 | | /* split */ |
289 | 82.6M | if (data == ',') { |
290 | 2.33k | fwupd_json_parser_helper_dump_acc(helper, token, str); |
291 | 2.33k | return TRUE; |
292 | 2.33k | } |
293 | | |
294 | | /* control token */ |
295 | 82.6M | if (data == FWUPD_JSON_PARSER_TOKEN_ARRAY_START || |
296 | 82.6M | data == FWUPD_JSON_PARSER_TOKEN_ARRAY_END || |
297 | 82.6M | data == FWUPD_JSON_PARSER_TOKEN_OBJECT_START || |
298 | 82.6M | data == FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM || |
299 | 82.6M | data == FWUPD_JSON_PARSER_TOKEN_OBJECT_END) { |
300 | 58.1k | fwupd_json_parser_helper_dump_acc(helper, token, str); |
301 | 58.1k | if (*token != FWUPD_JSON_PARSER_TOKEN_INVALID) { |
302 | 7.36k | *buf_offset_enable = FALSE; |
303 | 7.36k | return TRUE; |
304 | 7.36k | } |
305 | 50.8k | *token = data; |
306 | 50.8k | helper->newlinecnt = 0; |
307 | 50.8k | helper->whitespacecnt = 0; |
308 | 50.8k | return TRUE; |
309 | 58.1k | } |
310 | | |
311 | | /* whitespace */ |
312 | 82.6M | if (g_ascii_isspace(data)) { |
313 | 12.9k | guint whitespace_max = FWUPD_JSON_PARSER_INDENT_MAX * (helper->depth + 1); |
314 | 12.9k | if (helper->whitespacecnt++ >= whitespace_max) { |
315 | 1 | g_set_error(error, |
316 | 1 | FWUPD_ERROR, |
317 | 1 | FWUPD_ERROR_INVALID_DATA, |
318 | 1 | "too much whitespace, limit was %u", |
319 | 1 | whitespace_max); |
320 | 1 | return FALSE; |
321 | 1 | } |
322 | 12.9k | return TRUE; |
323 | 12.9k | } |
324 | | |
325 | | /* strip control chars */ |
326 | 82.6M | if (g_ascii_iscntrl(data)) { |
327 | 15 | g_set_error(error, |
328 | 15 | FWUPD_ERROR, |
329 | 15 | FWUPD_ERROR_INVALID_DATA, |
330 | 15 | "ASCII control character detected 0x%x", |
331 | 15 | (guint)data); |
332 | 15 | return FALSE; |
333 | 15 | } |
334 | | |
335 | | /* save acc */ |
336 | 82.6M | g_string_append_c(helper->acc, data); |
337 | 82.6M | helper->whitespacecnt = 0; |
338 | 82.6M | return TRUE; |
339 | 82.6M | } |
340 | | |
341 | | static gboolean |
342 | | fwupd_json_parser_helper_get_next_token(FwupdJsonParserHelper *helper, |
343 | | FwupdJsonParserToken *token, |
344 | | GRefString **str, |
345 | | GError **error) |
346 | 79.9k | { |
347 | | /* process each byte until we get a token */ |
348 | 82.8M | while (*token == FWUPD_JSON_PARSER_TOKEN_INVALID) { |
349 | 82.7M | gboolean buf_offset_enable = TRUE; |
350 | 82.7M | if (!fwupd_json_parser_helper_get_next_token_chunk(helper, |
351 | 82.7M | token, |
352 | 82.7M | str, |
353 | 82.7M | &buf_offset_enable, |
354 | 82.7M | error)) |
355 | 978 | return FALSE; |
356 | 82.7M | if (buf_offset_enable) |
357 | 82.7M | helper->buf_offset++; |
358 | 82.7M | } |
359 | | |
360 | | /* success */ |
361 | 78.9k | return TRUE; |
362 | 79.9k | } |
363 | | |
364 | | static gboolean |
365 | | fwupd_json_parser_helper_check_depth(FwupdJsonParser *self, guint depth, GError **error) |
366 | 20.4k | { |
367 | 20.4k | if (self->max_depth > 0 && depth > self->max_depth) { |
368 | 25 | g_set_error(error, |
369 | 25 | FWUPD_ERROR, |
370 | 25 | FWUPD_ERROR_INVALID_DATA, |
371 | 25 | "structure too deep, limit was %u", |
372 | 25 | depth); |
373 | 25 | return FALSE; |
374 | 25 | } |
375 | 20.3k | return TRUE; |
376 | 20.4k | } |
377 | | |
378 | | static FwupdJsonObject * |
379 | | fwupd_json_parser_load_object(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error); |
380 | | |
381 | | static FwupdJsonArray * |
382 | | fwupd_json_parser_load_array(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error) |
383 | 11.7k | { |
384 | 11.7k | g_autoptr(FwupdJsonArray) json_arr = fwupd_json_array_new(); |
385 | | |
386 | 11.7k | if (G_UNLIKELY(!fwupd_json_parser_helper_check_depth(self, ++helper->depth, error))) |
387 | 15 | return NULL; |
388 | 33.3k | while (TRUE) { |
389 | 33.3k | g_autoptr(GRefString) str = NULL; |
390 | 33.3k | FwupdJsonParserToken token = FWUPD_JSON_PARSER_TOKEN_INVALID; |
391 | | |
392 | 33.3k | if (!fwupd_json_parser_helper_get_next_token(helper, &token, &str, error)) |
393 | 134 | return NULL; |
394 | 33.2k | if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_END) |
395 | 11.1k | break; |
396 | 22.1k | if (token == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) { |
397 | 4.22k | g_autoptr(FwupdJsonObject) json_obj = |
398 | 4.22k | fwupd_json_parser_load_object(self, helper, error); |
399 | 4.22k | if (G_UNLIKELY(json_obj == NULL)) |
400 | 91 | return NULL; |
401 | 4.13k | fwupd_json_array_add_object(json_arr, json_obj); |
402 | 17.9k | } else if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) { |
403 | 8.49k | g_autoptr(FwupdJsonArray) json_array2 = |
404 | 8.49k | fwupd_json_parser_load_array(self, helper, error); |
405 | 8.49k | if (G_UNLIKELY(json_array2 == NULL)) |
406 | 317 | return NULL; |
407 | 8.18k | fwupd_json_array_add_array(json_arr, json_array2); |
408 | 9.41k | } else if (token == FWUPD_JSON_PARSER_TOKEN_STRING) { |
409 | 4.67k | fwupd_json_array_add_string_internal(json_arr, str); |
410 | 4.73k | } else if (token == FWUPD_JSON_PARSER_TOKEN_RAW) { |
411 | 4.71k | if (G_UNLIKELY(str == NULL)) { |
412 | 0 | g_set_error(error, |
413 | 0 | FWUPD_ERROR, |
414 | 0 | FWUPD_ERROR_INVALID_DATA, |
415 | 0 | "no raw data on line %u", |
416 | 0 | helper->linecnt); |
417 | 0 | return NULL; |
418 | 0 | } |
419 | 4.71k | fwupd_json_array_add_raw_internal(json_arr, str); |
420 | 4.71k | } else { |
421 | 17 | g_set_error_literal(error, |
422 | 17 | FWUPD_ERROR, |
423 | 17 | FWUPD_ERROR_INVALID_DATA, |
424 | 17 | "object delimiter not expected in array"); |
425 | 17 | return NULL; |
426 | 17 | } |
427 | 21.7k | if (G_UNLIKELY(self->max_items > 0 && |
428 | 21.7k | fwupd_json_array_get_size(json_arr) > self->max_items)) { |
429 | 9 | g_set_error(error, |
430 | 9 | FWUPD_ERROR, |
431 | 9 | FWUPD_ERROR_INVALID_DATA, |
432 | 9 | "too many items in array, limit was %u", |
433 | 9 | self->max_items); |
434 | 9 | return NULL; |
435 | 9 | } |
436 | 21.7k | } |
437 | 11.1k | helper->depth--; |
438 | | |
439 | | /* success */ |
440 | 11.1k | return g_steal_pointer(&json_arr); |
441 | 11.6k | } |
442 | | |
443 | | static FwupdJsonObject * |
444 | | fwupd_json_parser_load_object(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error) |
445 | 8.70k | { |
446 | 8.70k | g_autoptr(FwupdJsonObject) json_obj = fwupd_json_object_new(); |
447 | | |
448 | 8.70k | if (!fwupd_json_parser_helper_check_depth(self, ++helper->depth, error)) |
449 | 10 | return NULL; |
450 | 20.4k | while (TRUE) { |
451 | 20.4k | FwupdJsonParserToken token1 = FWUPD_JSON_PARSER_TOKEN_INVALID; |
452 | 20.4k | FwupdJsonParserToken token2 = FWUPD_JSON_PARSER_TOKEN_INVALID; |
453 | 20.4k | FwupdJsonParserToken token3 = FWUPD_JSON_PARSER_TOKEN_INVALID; |
454 | 20.4k | g_autoptr(GRefString) key = NULL; |
455 | 20.4k | g_autoptr(GRefString) val = NULL; |
456 | | |
457 | | /* "key" : value */ |
458 | 20.4k | if (!fwupd_json_parser_helper_get_next_token(helper, &token1, &key, error)) |
459 | 645 | return NULL; |
460 | 19.7k | if (token1 == FWUPD_JSON_PARSER_TOKEN_OBJECT_END) |
461 | 7.19k | break; |
462 | 12.5k | if (G_UNLIKELY(token1 != FWUPD_JSON_PARSER_TOKEN_STRING)) { |
463 | 721 | g_set_error(error, |
464 | 721 | FWUPD_ERROR, |
465 | 721 | FWUPD_ERROR_INVALID_DATA, |
466 | 721 | "object key '%s' must be quoted on line %u", |
467 | 721 | key, |
468 | 721 | helper->linecnt); |
469 | 721 | return NULL; |
470 | 721 | } |
471 | 11.8k | if (!fwupd_json_parser_helper_get_next_token(helper, &token2, NULL, error)) |
472 | 12 | return NULL; |
473 | 11.8k | if (token2 != FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM) { |
474 | 7 | g_set_error(error, |
475 | 7 | FWUPD_ERROR, |
476 | 7 | FWUPD_ERROR_INVALID_DATA, |
477 | 7 | "did not find object delimiter on line %u", |
478 | 7 | helper->linecnt); |
479 | 7 | return NULL; |
480 | 7 | } |
481 | 11.8k | if (!fwupd_json_parser_helper_get_next_token(helper, &token3, &val, error)) |
482 | 7 | return NULL; |
483 | | |
484 | 11.8k | if (token3 == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) { |
485 | 2.94k | g_autoptr(FwupdJsonObject) json_obj2 = |
486 | 2.94k | fwupd_json_parser_load_object(self, helper, error); |
487 | 2.94k | if (G_UNLIKELY(json_obj2 == NULL)) |
488 | 80 | return NULL; |
489 | 2.86k | fwupd_json_object_add_object_internal(json_obj, key, json_obj2); |
490 | 8.87k | } else if (token3 == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) { |
491 | 2.72k | g_autoptr(FwupdJsonArray) json_array2 = |
492 | 2.72k | fwupd_json_parser_load_array(self, helper, error); |
493 | 2.72k | if (G_UNLIKELY(json_array2 == NULL)) |
494 | 13 | return NULL; |
495 | 2.71k | fwupd_json_object_add_array_internal(json_obj, key, json_array2); |
496 | 6.15k | } else if (token3 == FWUPD_JSON_PARSER_TOKEN_STRING) { |
497 | 2.05k | fwupd_json_object_add_string_internal(json_obj, key, val, helper->flags); |
498 | 4.09k | } else if (token3 == FWUPD_JSON_PARSER_TOKEN_NULL) { |
499 | 1.48k | fwupd_json_object_add_null_internal(json_obj, key, helper->flags); |
500 | 2.61k | } else { |
501 | 2.61k | if (G_UNLIKELY(val == NULL)) { |
502 | 3 | g_set_error(error, |
503 | 3 | FWUPD_ERROR, |
504 | 3 | FWUPD_ERROR_INVALID_DATA, |
505 | 3 | "did not find raw value on line %u", |
506 | 3 | helper->linecnt); |
507 | 3 | return NULL; |
508 | 3 | } |
509 | 2.61k | fwupd_json_object_add_raw_internal(json_obj, key, val, helper->flags); |
510 | 2.61k | } |
511 | 11.7k | if (G_UNLIKELY(self->max_items > 0 && |
512 | 11.7k | fwupd_json_object_get_size(json_obj) > self->max_items)) { |
513 | 8 | g_set_error(error, |
514 | 8 | FWUPD_ERROR, |
515 | 8 | FWUPD_ERROR_INVALID_DATA, |
516 | 8 | "too many items in object, limit was %u", |
517 | 8 | self->max_items); |
518 | 8 | return NULL; |
519 | 8 | } |
520 | 11.7k | } |
521 | 7.19k | helper->depth--; |
522 | | |
523 | | /* success */ |
524 | 7.19k | return g_steal_pointer(&json_obj); |
525 | 8.69k | } |
526 | | |
527 | | static FwupdJsonNode * |
528 | | fwupd_json_parser_load_from_stream_internal(FwupdJsonParser *self, |
529 | | FwupdJsonParserHelper *helper, |
530 | | GError **error) |
531 | 2.42k | { |
532 | 2.42k | FwupdJsonParserToken token = FWUPD_JSON_PARSER_TOKEN_INVALID; |
533 | 2.42k | g_autoptr(GRefString) str = NULL; |
534 | | |
535 | 2.42k | #ifndef SUPPORTED_BUILD |
536 | | /* runtime warnings */ |
537 | 2.42k | if (self->max_depth == G_MAXUINT16) |
538 | 0 | g_warning("using the default max depth; use fwupd_json_parser_set_max_depth()"); |
539 | 2.42k | if (self->max_items == G_MAXUINT16) |
540 | 0 | g_warning("using the default max items; use fwupd_json_parser_set_max_items()"); |
541 | 2.42k | if (self->max_quoted == G_MAXUINT16) |
542 | 0 | g_warning("using the default max quoted; use fwupd_json_parser_set_max_quoted()"); |
543 | 2.42k | #endif |
544 | | |
545 | 2.42k | if (!fwupd_json_parser_helper_get_next_token(helper, &token, &str, error)) |
546 | 180 | return NULL; |
547 | 2.24k | if (token == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) { |
548 | 1.53k | g_autoptr(FwupdJsonObject) json_obj = NULL; |
549 | 1.53k | json_obj = fwupd_json_parser_load_object(self, helper, error); |
550 | 1.53k | if (json_obj == NULL) |
551 | 1.33k | return NULL; |
552 | 203 | return fwupd_json_node_new_object(json_obj); |
553 | 1.53k | } |
554 | 707 | if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) { |
555 | 490 | g_autoptr(FwupdJsonArray) json_arr = NULL; |
556 | 490 | json_arr = fwupd_json_parser_load_array(self, helper, error); |
557 | 490 | if (json_arr == NULL) |
558 | 253 | return NULL; |
559 | 237 | return fwupd_json_node_new_array(json_arr); |
560 | 490 | } |
561 | 217 | if (token == FWUPD_JSON_PARSER_TOKEN_STRING) |
562 | 119 | return fwupd_json_node_new_string_internal(str); |
563 | 98 | if (token == FWUPD_JSON_PARSER_TOKEN_RAW) |
564 | 90 | return fwupd_json_node_new_raw_internal(str); |
565 | | |
566 | | /* failed */ |
567 | 8 | g_set_error_literal(error, |
568 | 8 | FWUPD_ERROR, |
569 | 8 | FWUPD_ERROR_INVALID_DATA, |
570 | 8 | "invalid JSON; token was not object, array, string or raw"); |
571 | 8 | return NULL; |
572 | 98 | } |
573 | | |
574 | | /** |
575 | | * fwupd_json_parser_load_from_bytes: (skip): |
576 | | * @self: a #FwupdJsonParser |
577 | | * @blob: a #GBytes |
578 | | * @flags: a #FwupdJsonLoadFlags |
579 | | * @error: (nullable): optional return location for an error |
580 | | * |
581 | | * Loads JSON from a string. |
582 | | * |
583 | | * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error |
584 | | * |
585 | | * Since: 2.1.1 |
586 | | **/ |
587 | | FwupdJsonNode * |
588 | | fwupd_json_parser_load_from_bytes(FwupdJsonParser *self, |
589 | | GBytes *blob, |
590 | | FwupdJsonLoadFlags flags, |
591 | | GError **error) |
592 | 0 | { |
593 | 0 | g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(self); |
594 | |
|
595 | 0 | g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL); |
596 | 0 | g_return_val_if_fail(blob != NULL, NULL); |
597 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
598 | | |
599 | 0 | helper->flags = flags; |
600 | 0 | helper->stream = g_memory_input_stream_new_from_bytes(blob); |
601 | 0 | return fwupd_json_parser_load_from_stream_internal(self, helper, error); |
602 | 0 | } |
603 | | |
604 | | /** |
605 | | * fwupd_json_parser_load_from_data: (skip): |
606 | | * @self: a #FwupdJsonParser |
607 | | * @text: a string |
608 | | * @flags: a #FwupdJsonLoadFlags |
609 | | * @error: (nullable): optional return location for an error |
610 | | * |
611 | | * Loads JSON from a string. |
612 | | * |
613 | | * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error |
614 | | * |
615 | | * Since: 2.1.1 |
616 | | **/ |
617 | | FwupdJsonNode * |
618 | | fwupd_json_parser_load_from_data(FwupdJsonParser *self, |
619 | | const gchar *text, |
620 | | FwupdJsonLoadFlags flags, |
621 | | GError **error) |
622 | 0 | { |
623 | 0 | g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(self); |
624 | |
|
625 | 0 | g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL); |
626 | 0 | g_return_val_if_fail(text != NULL, NULL); |
627 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
628 | | |
629 | 0 | helper->flags = flags; |
630 | 0 | helper->stream = g_memory_input_stream_new_from_data(text, strlen(text), NULL); |
631 | 0 | return fwupd_json_parser_load_from_stream_internal(self, helper, error); |
632 | 0 | } |
633 | | |
634 | | /** |
635 | | * fwupd_json_parser_load_from_stream: (skip): |
636 | | * @self: a #FwupdJsonParser |
637 | | * @stream: a #GInputStream |
638 | | * @flags: a #FwupdJsonLoadFlags |
639 | | * @error: (nullable): optional return location for an error |
640 | | * |
641 | | * Loads JSON from a stream. |
642 | | * |
643 | | * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error |
644 | | * |
645 | | * Since: 2.1.1 |
646 | | **/ |
647 | | FwupdJsonNode * |
648 | | fwupd_json_parser_load_from_stream(FwupdJsonParser *self, |
649 | | GInputStream *stream, |
650 | | FwupdJsonLoadFlags flags, |
651 | | GError **error) |
652 | 2.42k | { |
653 | 2.42k | g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(self); |
654 | | |
655 | 2.42k | g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL); |
656 | 2.42k | g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); |
657 | 2.42k | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
658 | | |
659 | | /* tokenize in chunks */ |
660 | 2.42k | if (!g_seekable_seek(G_SEEKABLE(stream), 0x0, G_SEEK_SET, NULL, error)) { |
661 | 0 | fwupd_error_convert(error); |
662 | 0 | return NULL; |
663 | 0 | } |
664 | 2.42k | helper->stream = g_object_ref(stream); |
665 | 2.42k | helper->flags = flags; |
666 | 2.42k | return fwupd_json_parser_load_from_stream_internal(self, helper, error); |
667 | 2.42k | } |
668 | | |
669 | | static void |
670 | | fwupd_json_parser_class_init(FwupdJsonParserClass *klass) |
671 | 1 | { |
672 | 1 | } |
673 | | |
674 | | static void |
675 | | fwupd_json_parser_init(FwupdJsonParser *self) |
676 | 2.42k | { |
677 | 2.42k | self->max_depth = G_MAXUINT16; |
678 | 2.42k | self->max_items = G_MAXUINT16; |
679 | 2.42k | self->max_quoted = G_MAXUINT16; |
680 | 2.42k | } |
681 | | |
682 | | /** |
683 | | * fwupd_json_parser_new: |
684 | | * |
685 | | * Returns: (transfer full): a #FwupdJsonParser |
686 | | * |
687 | | * Since: 2.1.1 |
688 | | **/ |
689 | | FwupdJsonParser * |
690 | | fwupd_json_parser_new(void) |
691 | 2.42k | { |
692 | 2.42k | return g_object_new(FWUPD_TYPE_JSON_PARSER, NULL); |
693 | 2.42k | } |