/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 | | }; |
31 | | |
32 | 7.45k | G_DEFINE_TYPE(FwupdJsonParser, fwupd_json_parser, G_TYPE_OBJECT) |
33 | 7.45k | |
34 | 7.45k | /** |
35 | 7.45k | * fwupd_json_parser_set_max_depth: |
36 | 7.45k | * @self: a #FwupdJsonParser |
37 | 7.45k | * @max_depth: max nesting depth |
38 | 7.45k | * |
39 | 7.45k | * Sets the maximum nesting depth. By default there is no limit. |
40 | 7.45k | * |
41 | 7.45k | * Since: 2.1.1 |
42 | 7.45k | **/ |
43 | 7.45k | void |
44 | 7.45k | fwupd_json_parser_set_max_depth(FwupdJsonParser *self, guint max_depth) |
45 | 7.45k | { |
46 | 1.86k | g_return_if_fail(FWUPD_IS_JSON_PARSER(self)); |
47 | 1.86k | self->max_depth = max_depth; |
48 | 1.86k | } |
49 | | |
50 | | /** |
51 | | * fwupd_json_parser_set_max_items: |
52 | | * @self: a #FwupdJsonParser |
53 | | * @max_items: max items |
54 | | * |
55 | | * Sets the maximum number of items in an array or object. By default there is no limit. |
56 | | * |
57 | | * Since: 2.1.1 |
58 | | **/ |
59 | | void |
60 | | fwupd_json_parser_set_max_items(FwupdJsonParser *self, guint max_items) |
61 | 1.86k | { |
62 | 1.86k | g_return_if_fail(FWUPD_IS_JSON_PARSER(self)); |
63 | 1.86k | self->max_items = max_items; |
64 | 1.86k | } |
65 | | |
66 | | typedef enum { |
67 | | FWUPD_JSON_PARSER_TOKEN_INVALID = 0, |
68 | | FWUPD_JSON_PARSER_TOKEN_RAW = 'b', |
69 | | FWUPD_JSON_PARSER_TOKEN_STRING = '\"', |
70 | | FWUPD_JSON_PARSER_TOKEN_OBJECT_START = '{', |
71 | | FWUPD_JSON_PARSER_TOKEN_OBJECT_END = '}', |
72 | | FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM = ':', |
73 | | FWUPD_JSON_PARSER_TOKEN_ARRAY_START = '[', |
74 | | FWUPD_JSON_PARSER_TOKEN_ARRAY_END = ']', |
75 | | } FwupdJsonParserToken; |
76 | | |
77 | | typedef struct { |
78 | | FwupdJsonLoadFlags flags; |
79 | | GByteArray *buf; |
80 | | gsize buf_offset; /* into @buf */ |
81 | | GInputStream *stream; |
82 | | GString *acc; |
83 | | gboolean is_quoted; |
84 | | gboolean is_escape; |
85 | | guint linecnt; |
86 | | guint depth; |
87 | | } FwupdJsonParserHelper; |
88 | | |
89 | | static FwupdJsonParserHelper * |
90 | | fwupd_json_parser_helper_new(void) |
91 | 1.86k | { |
92 | 1.86k | FwupdJsonParserHelper *helper = g_new0(FwupdJsonParserHelper, 1); |
93 | 1.86k | helper->linecnt = 1; |
94 | 1.86k | helper->buf = g_byte_array_new(); |
95 | 1.86k | helper->acc = g_string_sized_new(128); |
96 | 1.86k | helper->buf_offset = G_MAXSIZE; |
97 | 1.86k | g_byte_array_set_size(helper->buf, 32 * 1024); |
98 | 1.86k | return helper; |
99 | 1.86k | } |
100 | | |
101 | | static void |
102 | | fwupd_json_parser_helper_free(FwupdJsonParserHelper *helper) |
103 | 1.86k | { |
104 | 1.86k | if (helper->stream != NULL) |
105 | 1.86k | g_object_unref(helper->stream); |
106 | 1.86k | g_byte_array_unref(helper->buf); |
107 | 1.86k | g_string_free(helper->acc, TRUE); |
108 | 1.86k | g_free(helper); |
109 | 1.86k | } |
110 | | |
111 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FwupdJsonParserHelper, fwupd_json_parser_helper_free) |
112 | | |
113 | | static void |
114 | | fwupd_json_parser_helper_dump_acc(FwupdJsonParserHelper *helper, |
115 | | FwupdJsonParserToken *token, |
116 | | GRefString **str) |
117 | 76.6k | { |
118 | 76.6k | if (helper->is_quoted) { |
119 | 18.0k | *token = FWUPD_JSON_PARSER_TOKEN_STRING; |
120 | 18.0k | if (str != NULL) |
121 | 18.0k | *str = g_ref_string_new_len(helper->acc->str, helper->acc->len); |
122 | 58.5k | } else { |
123 | 58.5k | if (helper->acc->len == 0) |
124 | 46.5k | return; |
125 | 12.0k | if (g_ascii_strncasecmp(helper->acc->str, "null", helper->acc->len) == 0) { |
126 | 2.63k | *token = FWUPD_JSON_PARSER_TOKEN_STRING; |
127 | 9.39k | } else { |
128 | 9.39k | *token = FWUPD_JSON_PARSER_TOKEN_RAW; |
129 | 9.39k | if (str != NULL) |
130 | 9.39k | *str = g_ref_string_new_len(helper->acc->str, helper->acc->len); |
131 | 9.39k | } |
132 | 12.0k | } |
133 | 30.0k | g_string_truncate(helper->acc, 0); |
134 | 30.0k | } |
135 | | |
136 | | static gboolean |
137 | | fwupd_json_parser_helper_slurp(FwupdJsonParserHelper *helper, GError **error) |
138 | 4.60k | { |
139 | 4.60k | gssize rc; |
140 | | |
141 | 4.60k | rc = g_input_stream_read(helper->stream, helper->buf->data, helper->buf->len, NULL, error); |
142 | 4.60k | if (rc < 0) { |
143 | 0 | fwupd_error_convert(error); |
144 | 0 | return FALSE; |
145 | 0 | } |
146 | 4.60k | if (rc == 0) { |
147 | 904 | g_set_error_literal(error, |
148 | 904 | FWUPD_ERROR, |
149 | 904 | FWUPD_ERROR_INVALID_DATA, |
150 | 904 | "incomplete data from stream"); |
151 | 904 | return FALSE; |
152 | 904 | } |
153 | 3.69k | g_byte_array_set_size(helper->buf, rc); |
154 | | |
155 | | /* success */ |
156 | 3.69k | helper->buf_offset = 0; |
157 | 3.69k | return TRUE; |
158 | 4.60k | } |
159 | | |
160 | | static gchar |
161 | | fwupd_json_parser_unescape_char(gchar data) |
162 | 1.13k | { |
163 | 1.13k | if (data == 'n') |
164 | 336 | return '\n'; |
165 | 800 | if (data == 't') |
166 | 299 | return '\t'; |
167 | 501 | if (data == '\\') |
168 | 480 | return '\\'; |
169 | 21 | return 0; |
170 | 501 | } |
171 | | |
172 | | static gboolean |
173 | | fwupd_json_parser_helper_get_next_token_chunk(FwupdJsonParserHelper *helper, |
174 | | FwupdJsonParserToken *token, |
175 | | GRefString **str, |
176 | | gboolean *buf_offset_enable, |
177 | | GError **error) |
178 | 64.1M | { |
179 | 64.1M | gchar data; |
180 | | |
181 | | /* need more data */ |
182 | 64.1M | if (G_UNLIKELY(helper->buf_offset >= helper->buf->len)) { |
183 | 4.60k | if (!fwupd_json_parser_helper_slurp(helper, error)) |
184 | 904 | return FALSE; |
185 | 4.60k | } |
186 | 64.1M | data = helper->buf->data[helper->buf_offset]; |
187 | | |
188 | | /* quotes */ |
189 | 64.1M | if (data == '"') { |
190 | 36.2k | if (helper->is_quoted) { |
191 | 18.0k | fwupd_json_parser_helper_dump_acc(helper, token, str); |
192 | 18.0k | helper->is_quoted = FALSE; |
193 | 18.0k | return TRUE; |
194 | 18.0k | } |
195 | 18.1k | helper->is_quoted = TRUE; |
196 | 18.1k | return TRUE; |
197 | 36.2k | } |
198 | 64.0M | if (helper->is_quoted) { |
199 | | /* escape char */ |
200 | 18.3M | if (!helper->is_escape && data == '\\') { |
201 | 1.14k | helper->is_escape = TRUE; |
202 | 1.14k | return TRUE; |
203 | 1.14k | } |
204 | 18.3M | if (G_UNLIKELY(helper->is_escape)) { |
205 | 1.13k | data = fwupd_json_parser_unescape_char(data); |
206 | 1.13k | if (G_UNLIKELY(data == 0)) { |
207 | 21 | g_set_error(error, |
208 | 21 | FWUPD_ERROR, |
209 | 21 | FWUPD_ERROR_INVALID_DATA, |
210 | 21 | "invalid escape char '%c'", |
211 | 21 | data); |
212 | 21 | return FALSE; |
213 | 21 | } |
214 | 1.11k | helper->is_escape = FALSE; |
215 | 1.11k | } |
216 | | |
217 | | /* save acc */ |
218 | 18.3M | g_string_append_c(helper->acc, data); |
219 | 18.3M | return TRUE; |
220 | 18.3M | } |
221 | | |
222 | | /* newline, for error messages */ |
223 | 45.7M | if (data == '\n') { |
224 | 608 | helper->linecnt++; |
225 | 608 | fwupd_json_parser_helper_dump_acc(helper, token, str); |
226 | 608 | return TRUE; |
227 | 608 | } |
228 | | |
229 | | /* split */ |
230 | 45.7M | if (data == ',') { |
231 | 3.51k | fwupd_json_parser_helper_dump_acc(helper, token, str); |
232 | 3.51k | return TRUE; |
233 | 3.51k | } |
234 | | |
235 | | /* control token */ |
236 | 45.7M | if (data == FWUPD_JSON_PARSER_TOKEN_ARRAY_START || |
237 | 45.7M | data == FWUPD_JSON_PARSER_TOKEN_ARRAY_END || |
238 | 45.7M | data == FWUPD_JSON_PARSER_TOKEN_OBJECT_START || |
239 | 45.7M | data == FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM || |
240 | 45.6M | data == FWUPD_JSON_PARSER_TOKEN_OBJECT_END) { |
241 | 54.4k | fwupd_json_parser_helper_dump_acc(helper, token, str); |
242 | 54.4k | if (*token != FWUPD_JSON_PARSER_TOKEN_INVALID) { |
243 | 10.5k | *buf_offset_enable = FALSE; |
244 | 10.5k | return TRUE; |
245 | 10.5k | } |
246 | 43.9k | *token = data; |
247 | 43.9k | return TRUE; |
248 | 54.4k | } |
249 | | |
250 | | /* whitespace */ |
251 | 45.6M | if (g_ascii_isspace(data)) |
252 | 11.2k | return TRUE; |
253 | | |
254 | | /* save acc */ |
255 | 45.6M | g_string_append_c(helper->acc, data); |
256 | 45.6M | return TRUE; |
257 | 45.6M | } |
258 | | |
259 | | static gboolean |
260 | | fwupd_json_parser_helper_get_next_token(FwupdJsonParserHelper *helper, |
261 | | FwupdJsonParserToken *token, |
262 | | GRefString **str, |
263 | | GError **error) |
264 | 74.9k | { |
265 | | /* process each byte until we get a token */ |
266 | 64.1M | while (*token == FWUPD_JSON_PARSER_TOKEN_INVALID) { |
267 | 64.1M | gboolean buf_offset_enable = TRUE; |
268 | 64.1M | if (!fwupd_json_parser_helper_get_next_token_chunk(helper, |
269 | 64.1M | token, |
270 | 64.1M | str, |
271 | 64.1M | &buf_offset_enable, |
272 | 64.1M | error)) |
273 | 925 | return FALSE; |
274 | 64.1M | if (buf_offset_enable) |
275 | 64.0M | helper->buf_offset++; |
276 | 64.1M | } |
277 | | |
278 | | /* success */ |
279 | 74.0k | return TRUE; |
280 | 74.9k | } |
281 | | |
282 | | static gboolean |
283 | | fwupd_json_parser_helper_check_depth(FwupdJsonParser *self, guint depth, GError **error) |
284 | 15.4k | { |
285 | 15.4k | if (self->max_depth > 0 && depth > self->max_depth) { |
286 | 14 | g_set_error(error, |
287 | 14 | FWUPD_ERROR, |
288 | 14 | FWUPD_ERROR_INVALID_DATA, |
289 | 14 | "structure too deep, limit was %u", |
290 | 14 | depth); |
291 | 14 | return FALSE; |
292 | 14 | } |
293 | 15.3k | return TRUE; |
294 | 15.4k | } |
295 | | |
296 | | static FwupdJsonObject * |
297 | | fwupd_json_parser_load_object(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error); |
298 | | |
299 | | static FwupdJsonArray * |
300 | | fwupd_json_parser_load_array(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error) |
301 | 7.59k | { |
302 | 7.59k | g_autoptr(FwupdJsonArray) json_arr = fwupd_json_array_new(); |
303 | | |
304 | 7.59k | if (G_UNLIKELY(!fwupd_json_parser_helper_check_depth(self, ++helper->depth, error))) |
305 | 9 | return NULL; |
306 | 22.3k | while (TRUE) { |
307 | 22.3k | g_autoptr(GRefString) str = NULL; |
308 | 22.3k | FwupdJsonParserToken token = FWUPD_JSON_PARSER_TOKEN_INVALID; |
309 | | |
310 | 22.3k | if (!fwupd_json_parser_helper_get_next_token(helper, &token, &str, error)) |
311 | 95 | return NULL; |
312 | 22.2k | if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_END) |
313 | 7.16k | break; |
314 | 15.0k | if (token == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) { |
315 | 1.52k | g_autoptr(FwupdJsonObject) json_obj = |
316 | 1.52k | fwupd_json_parser_load_object(self, helper, error); |
317 | 1.52k | if (G_UNLIKELY(json_obj == NULL)) |
318 | 113 | return NULL; |
319 | 1.41k | fwupd_json_array_add_object(json_arr, json_obj); |
320 | 13.5k | } else if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) { |
321 | 4.95k | g_autoptr(FwupdJsonArray) json_array2 = |
322 | 4.95k | fwupd_json_parser_load_array(self, helper, error); |
323 | 4.95k | if (G_UNLIKELY(json_array2 == NULL)) |
324 | 196 | return NULL; |
325 | 4.76k | fwupd_json_array_add_array(json_arr, json_array2); |
326 | 8.59k | } else if (token == FWUPD_JSON_PARSER_TOKEN_STRING) { |
327 | 3.07k | fwupd_json_array_add_string_internal(json_arr, str); |
328 | 5.51k | } else if (token == FWUPD_JSON_PARSER_TOKEN_RAW) { |
329 | 5.51k | if (G_UNLIKELY(str == NULL)) { |
330 | 0 | g_set_error(error, |
331 | 0 | FWUPD_ERROR, |
332 | 0 | FWUPD_ERROR_INVALID_DATA, |
333 | 0 | "no raw data on line %u", |
334 | 0 | helper->linecnt); |
335 | 0 | return NULL; |
336 | 0 | } |
337 | 5.51k | fwupd_json_array_add_raw_internal(json_arr, str); |
338 | 5.51k | } else { |
339 | 7 | g_set_error_literal(error, |
340 | 7 | FWUPD_ERROR, |
341 | 7 | FWUPD_ERROR_INVALID_DATA, |
342 | 7 | "object delimiter not expected in array"); |
343 | 7 | return NULL; |
344 | 7 | } |
345 | 14.7k | if (G_UNLIKELY(self->max_items > 0 && |
346 | 14.7k | fwupd_json_array_get_size(json_arr) > self->max_items)) { |
347 | 7 | g_set_error(error, |
348 | 7 | FWUPD_ERROR, |
349 | 7 | FWUPD_ERROR_INVALID_DATA, |
350 | 7 | "too many items in array, limit was %u", |
351 | 7 | self->max_items); |
352 | 7 | return NULL; |
353 | 7 | } |
354 | 14.7k | } |
355 | 7.16k | helper->depth--; |
356 | | |
357 | | /* success */ |
358 | 7.16k | return g_steal_pointer(&json_arr); |
359 | 7.58k | } |
360 | | |
361 | | static FwupdJsonObject * |
362 | | fwupd_json_parser_load_object(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error) |
363 | 7.81k | { |
364 | 7.81k | g_autoptr(FwupdJsonObject) json_obj = fwupd_json_object_new(); |
365 | | |
366 | 7.81k | if (!fwupd_json_parser_helper_check_depth(self, ++helper->depth, error)) |
367 | 5 | return NULL; |
368 | 22.0k | while (TRUE) { |
369 | 22.0k | FwupdJsonParserToken token1 = FWUPD_JSON_PARSER_TOKEN_INVALID; |
370 | 22.0k | FwupdJsonParserToken token2 = FWUPD_JSON_PARSER_TOKEN_INVALID; |
371 | 22.0k | FwupdJsonParserToken token3 = FWUPD_JSON_PARSER_TOKEN_INVALID; |
372 | 22.0k | g_autoptr(GRefString) key = NULL; |
373 | 22.0k | g_autoptr(GRefString) val = NULL; |
374 | | |
375 | | /* "key" : value */ |
376 | 22.0k | if (!fwupd_json_parser_helper_get_next_token(helper, &token1, &key, error)) |
377 | 624 | return NULL; |
378 | 21.3k | if (token1 == FWUPD_JSON_PARSER_TOKEN_OBJECT_END) |
379 | 6.87k | break; |
380 | 14.5k | if (G_UNLIKELY(token1 != FWUPD_JSON_PARSER_TOKEN_STRING)) { |
381 | 143 | g_set_error(error, |
382 | 143 | FWUPD_ERROR, |
383 | 143 | FWUPD_ERROR_INVALID_DATA, |
384 | 143 | "object key '%s' must be quoted on line %u", |
385 | 143 | key, |
386 | 143 | helper->linecnt); |
387 | 143 | return NULL; |
388 | 143 | } |
389 | 14.3k | if (!fwupd_json_parser_helper_get_next_token(helper, &token2, NULL, error)) |
390 | 10 | return NULL; |
391 | 14.3k | if (token2 != FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM) { |
392 | 14 | g_set_error(error, |
393 | 14 | FWUPD_ERROR, |
394 | 14 | FWUPD_ERROR_INVALID_DATA, |
395 | 14 | "did not find object delimiter on line %u", |
396 | 14 | helper->linecnt); |
397 | 14 | return NULL; |
398 | 14 | } |
399 | 14.3k | if (!fwupd_json_parser_helper_get_next_token(helper, &token3, &val, error)) |
400 | 7 | return NULL; |
401 | | |
402 | 14.3k | if (token3 == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) { |
403 | 5.45k | g_autoptr(FwupdJsonObject) json_obj2 = |
404 | 5.45k | fwupd_json_parser_load_object(self, helper, error); |
405 | 5.45k | if (G_UNLIKELY(json_obj2 == NULL)) |
406 | 117 | return NULL; |
407 | 5.33k | fwupd_json_object_add_object_internal(json_obj, key, json_obj2); |
408 | 8.89k | } else if (token3 == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) { |
409 | 2.10k | g_autoptr(FwupdJsonArray) json_array2 = |
410 | 2.10k | fwupd_json_parser_load_array(self, helper, error); |
411 | 2.10k | if (G_UNLIKELY(json_array2 == NULL)) |
412 | 11 | return NULL; |
413 | 2.08k | fwupd_json_object_add_array_internal(json_obj, key, json_array2); |
414 | 6.79k | } else if (token3 == FWUPD_JSON_PARSER_TOKEN_STRING) { |
415 | 3.02k | fwupd_json_object_add_string_internal(json_obj, key, val, helper->flags); |
416 | 3.77k | } else { |
417 | 3.77k | if (G_UNLIKELY(val == NULL)) { |
418 | 6 | g_set_error(error, |
419 | 6 | FWUPD_ERROR, |
420 | 6 | FWUPD_ERROR_INVALID_DATA, |
421 | 6 | "did not find raw value on line %u", |
422 | 6 | helper->linecnt); |
423 | 6 | return NULL; |
424 | 6 | } |
425 | 3.77k | fwupd_json_object_add_raw_internal(json_obj, key, val, helper->flags); |
426 | 3.77k | } |
427 | 14.2k | if (G_UNLIKELY(self->max_items > 0 && |
428 | 14.2k | fwupd_json_object_get_size(json_obj) > self->max_items)) { |
429 | 6 | g_set_error(error, |
430 | 6 | FWUPD_ERROR, |
431 | 6 | FWUPD_ERROR_INVALID_DATA, |
432 | 6 | "too many items in object, limit was %u", |
433 | 6 | self->max_items); |
434 | 6 | return NULL; |
435 | 6 | } |
436 | 14.2k | } |
437 | 6.87k | helper->depth--; |
438 | | |
439 | | /* success */ |
440 | 6.87k | return g_steal_pointer(&json_obj); |
441 | 7.81k | } |
442 | | |
443 | | static FwupdJsonNode * |
444 | | fwupd_json_parser_load_from_stream_internal(FwupdJsonParser *self, |
445 | | FwupdJsonParserHelper *helper, |
446 | | GError **error) |
447 | 1.86k | { |
448 | 1.86k | FwupdJsonParserToken token = FWUPD_JSON_PARSER_TOKEN_INVALID; |
449 | 1.86k | g_autoptr(GRefString) str = NULL; |
450 | | |
451 | 1.86k | if (!fwupd_json_parser_helper_get_next_token(helper, &token, &str, error)) |
452 | 189 | return NULL; |
453 | 1.67k | if (token == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) { |
454 | 836 | g_autoptr(FwupdJsonObject) json_obj = NULL; |
455 | 836 | json_obj = fwupd_json_parser_load_object(self, helper, error); |
456 | 836 | if (json_obj == NULL) |
457 | 713 | return NULL; |
458 | 123 | return fwupd_json_node_new_object(json_obj); |
459 | 836 | } |
460 | 838 | if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) { |
461 | 531 | g_autoptr(FwupdJsonArray) json_arr = NULL; |
462 | 531 | json_arr = fwupd_json_parser_load_array(self, helper, error); |
463 | 531 | if (json_arr == NULL) |
464 | 220 | return NULL; |
465 | 311 | return fwupd_json_node_new_array(json_arr); |
466 | 531 | } |
467 | 307 | if (token == FWUPD_JSON_PARSER_TOKEN_STRING) |
468 | 215 | return fwupd_json_node_new_string_internal(str); |
469 | 92 | if (token == FWUPD_JSON_PARSER_TOKEN_RAW) |
470 | 89 | return fwupd_json_node_new_raw_internal(str); |
471 | | |
472 | | /* failed */ |
473 | 3 | g_set_error_literal(error, |
474 | 3 | FWUPD_ERROR, |
475 | 3 | FWUPD_ERROR_INVALID_DATA, |
476 | 3 | "invalid JSON; token was not object, array, string or raw"); |
477 | 3 | return NULL; |
478 | 92 | } |
479 | | |
480 | | /** |
481 | | * fwupd_json_parser_load_from_bytes: (skip): |
482 | | * @self: a #FwupdJsonParser |
483 | | * @blob: a #GBytes |
484 | | * @flags: a #FwupdJsonLoadFlags |
485 | | * @error: (nullable): optional return location for an error |
486 | | * |
487 | | * Loads JSON from a string. |
488 | | * |
489 | | * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error |
490 | | * |
491 | | * Since: 2.1.1 |
492 | | **/ |
493 | | FwupdJsonNode * |
494 | | fwupd_json_parser_load_from_bytes(FwupdJsonParser *self, |
495 | | GBytes *blob, |
496 | | FwupdJsonLoadFlags flags, |
497 | | GError **error) |
498 | 0 | { |
499 | 0 | g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(); |
500 | |
|
501 | 0 | g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL); |
502 | 0 | g_return_val_if_fail(blob != NULL, NULL); |
503 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
504 | | |
505 | 0 | helper->flags = flags; |
506 | 0 | helper->stream = g_memory_input_stream_new_from_bytes(blob); |
507 | 0 | return fwupd_json_parser_load_from_stream_internal(self, helper, error); |
508 | 0 | } |
509 | | |
510 | | /** |
511 | | * fwupd_json_parser_load_from_data: (skip): |
512 | | * @self: a #FwupdJsonParser |
513 | | * @text: a string |
514 | | * @flags: a #FwupdJsonLoadFlags |
515 | | * @error: (nullable): optional return location for an error |
516 | | * |
517 | | * Loads JSON from a string. |
518 | | * |
519 | | * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error |
520 | | * |
521 | | * Since: 2.1.1 |
522 | | **/ |
523 | | FwupdJsonNode * |
524 | | fwupd_json_parser_load_from_data(FwupdJsonParser *self, |
525 | | const gchar *text, |
526 | | FwupdJsonLoadFlags flags, |
527 | | GError **error) |
528 | 0 | { |
529 | 0 | g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(); |
530 | |
|
531 | 0 | g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL); |
532 | 0 | g_return_val_if_fail(text != NULL, NULL); |
533 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
534 | | |
535 | 0 | helper->flags = flags; |
536 | 0 | helper->stream = g_memory_input_stream_new_from_data(text, strlen(text), NULL); |
537 | 0 | return fwupd_json_parser_load_from_stream_internal(self, helper, error); |
538 | 0 | } |
539 | | |
540 | | /** |
541 | | * fwupd_json_parser_load_from_stream: (skip): |
542 | | * @self: a #FwupdJsonParser |
543 | | * @stream: a #GInputStream |
544 | | * @flags: a #FwupdJsonLoadFlags |
545 | | * @error: (nullable): optional return location for an error |
546 | | * |
547 | | * Loads JSON from a stream. |
548 | | * |
549 | | * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error |
550 | | * |
551 | | * Since: 2.1.1 |
552 | | **/ |
553 | | FwupdJsonNode * |
554 | | fwupd_json_parser_load_from_stream(FwupdJsonParser *self, |
555 | | GInputStream *stream, |
556 | | FwupdJsonLoadFlags flags, |
557 | | GError **error) |
558 | 1.86k | { |
559 | 1.86k | g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(); |
560 | | |
561 | 1.86k | g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL); |
562 | 1.86k | g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); |
563 | 1.86k | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
564 | | |
565 | | /* tokenize in chunks */ |
566 | 1.86k | if (!g_seekable_seek(G_SEEKABLE(stream), 0x0, G_SEEK_SET, NULL, error)) { |
567 | 0 | fwupd_error_convert(error); |
568 | 0 | return NULL; |
569 | 0 | } |
570 | 1.86k | helper->stream = g_object_ref(stream); |
571 | 1.86k | helper->flags = flags; |
572 | 1.86k | return fwupd_json_parser_load_from_stream_internal(self, helper, error); |
573 | 1.86k | } |
574 | | |
575 | | static void |
576 | | fwupd_json_parser_class_init(FwupdJsonParserClass *klass) |
577 | 1 | { |
578 | 1 | } |
579 | | |
580 | | static void |
581 | | fwupd_json_parser_init(FwupdJsonParser *self) |
582 | 1.86k | { |
583 | 1.86k | } |
584 | | |
585 | | /** |
586 | | * fwupd_json_parser_new: |
587 | | * |
588 | | * Returns: (transfer full): a #FwupdJsonParser |
589 | | * |
590 | | * Since: 2.1.1 |
591 | | **/ |
592 | | FwupdJsonParser * |
593 | | fwupd_json_parser_new(void) |
594 | 1.86k | { |
595 | 1.86k | return g_object_new(FWUPD_TYPE_JSON_PARSER, NULL); |
596 | 1.86k | } |