Coverage Report

Created: 2025-12-14 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}