Coverage Report

Created: 2026-02-26 06:27

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
  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
}