Coverage Report

Created: 2026-09-03 07:14

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
10.9k
G_DEFINE_TYPE(FwupdJsonParser, fwupd_json_parser, G_TYPE_OBJECT)
34
10.9k
35
10.9k
#define FWUPD_JSON_PARSER_NEWLINE_MAX 5
36
37
506k
#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.18k
{
54
2.18k
  g_return_if_fail(FWUPD_IS_JSON_PARSER(self));
55
2.18k
  self->max_depth = max_depth;
56
2.18k
}
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.18k
{
73
2.18k
  g_return_if_fail(FWUPD_IS_JSON_PARSER(self));
74
2.18k
  self->max_items = max_items;
75
2.18k
}
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.18k
{
92
2.18k
  g_return_if_fail(FWUPD_IS_JSON_PARSER(self));
93
2.18k
  self->max_quoted = max_quoted;
94
2.18k
}
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.18k
{
126
2.18k
  FwupdJsonParserHelper *helper = g_new0(FwupdJsonParserHelper, 1);
127
2.18k
  helper->max_quoted = self->max_quoted;
128
2.18k
  helper->linecnt = 1;
129
2.18k
  helper->buf = g_byte_array_new();
130
2.18k
  helper->acc = g_string_sized_new(128);
131
2.18k
  helper->buf_offset = G_MAXSIZE;
132
2.18k
  g_byte_array_set_size(helper->buf, 32 * 1024);
133
2.18k
  return helper;
134
2.18k
}
135
136
static void
137
fwupd_json_parser_helper_free(FwupdJsonParserHelper *helper)
138
2.18k
{
139
2.18k
  if (helper->stream != NULL)
140
2.18k
    g_object_unref(helper->stream);
141
2.18k
  g_byte_array_unref(helper->buf);
142
2.18k
  g_string_free(helper->acc, TRUE);
143
2.18k
  g_free(helper);
144
2.18k
}
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
77.0k
{
153
77.0k
  if (helper->is_quoted) {
154
16.8k
    *token = FWUPD_JSON_PARSER_TOKEN_STRING;
155
16.8k
    if (str != NULL)
156
16.8k
      *str = g_ref_string_new_len(helper->acc->str, helper->acc->len);
157
60.2k
  } else {
158
60.2k
    if (helper->acc->len == 0)
159
50.3k
      return;
160
9.89k
    if (g_ascii_strncasecmp(helper->acc->str, "null", helper->acc->len) == 0) {
161
1.71k
      *token = FWUPD_JSON_PARSER_TOKEN_NULL;
162
8.17k
    } else {
163
8.17k
      *token = FWUPD_JSON_PARSER_TOKEN_RAW;
164
8.17k
      if (str != NULL)
165
8.17k
        *str = g_ref_string_new_len(helper->acc->str, helper->acc->len);
166
8.17k
    }
167
9.89k
  }
168
26.7k
  g_string_truncate(helper->acc, 0);
169
26.7k
}
170
171
static gboolean
172
fwupd_json_parser_helper_slurp(FwupdJsonParserHelper *helper, GError **error)
173
4.66k
{
174
4.66k
  gssize rc;
175
176
4.66k
  rc = g_input_stream_read(helper->stream, helper->buf->data, helper->buf->len, NULL, error);
177
4.66k
  if (rc < 0) {
178
0
    fwupd_error_convert(error);
179
0
    return FALSE;
180
0
  }
181
4.66k
  if (rc == 0) {
182
878
    g_set_error_literal(error,
183
878
            FWUPD_ERROR,
184
878
            FWUPD_ERROR_INVALID_DATA,
185
878
            "incomplete data from stream");
186
878
    return FALSE;
187
878
  }
188
3.78k
  g_byte_array_set_size(helper->buf, rc);
189
190
  /* success */
191
3.78k
  helper->buf_offset = 0;
192
3.78k
  return TRUE;
193
4.66k
}
194
195
static gchar
196
fwupd_json_parser_unescape_char(gchar data)
197
927
{
198
927
  if (data == 'n')
199
195
    return '\n';
200
732
  if (data == 't')
201
194
    return '\t';
202
538
  if (data == '\\')
203
251
    return '\\';
204
287
  if (data == '\"')
205
264
    return '\"';
206
23
  return 0;
207
287
}
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
56.8M
{
216
56.8M
  gchar data;
217
218
  /* need more data */
219
56.8M
  if (G_UNLIKELY(helper->buf_offset >= helper->buf->len)) {
220
4.66k
    if (!fwupd_json_parser_helper_slurp(helper, error))
221
878
      return FALSE;
222
4.66k
  }
223
56.8M
  data = helper->buf->data[helper->buf_offset];
224
225
  /* quotes */
226
56.8M
  if (!helper->is_escape && data == '"') {
227
33.8k
    if (helper->is_quoted) {
228
16.8k
      fwupd_json_parser_helper_dump_acc(helper, token, str);
229
16.8k
      helper->is_quoted = FALSE;
230
16.8k
      return TRUE;
231
16.8k
    }
232
16.9k
    helper->is_quoted = TRUE;
233
16.9k
    helper->newlinecnt = 0;
234
16.9k
    helper->whitespacecnt = 0;
235
16.9k
    return TRUE;
236
33.8k
  }
237
56.8M
  if (helper->is_quoted) {
238
    /* escape char */
239
4.49k
    if (!helper->is_escape && data == '\\') {
240
934
      helper->is_escape = TRUE;
241
934
      helper->newlinecnt = 0;
242
934
      return TRUE;
243
934
    }
244
3.55k
    if (G_UNLIKELY(helper->is_escape)) {
245
927
      data = fwupd_json_parser_unescape_char(data);
246
927
      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
904
      helper->is_escape = FALSE;
255
904
    }
256
257
    /* save acc */
258
3.53k
    helper->newlinecnt = 0;
259
3.53k
    helper->whitespacecnt = 0;
260
3.53k
    g_string_append_c(helper->acc, data);
261
3.53k
    if (G_UNLIKELY(helper->max_quoted > 0 && helper->acc->len > helper->max_quoted)) {
262
43
      g_set_error(error,
263
43
            FWUPD_ERROR,
264
43
            FWUPD_ERROR_INVALID_DATA,
265
43
            "token too long, limit was %u",
266
43
            helper->max_quoted);
267
43
      return FALSE;
268
43
    }
269
3.49k
    return TRUE;
270
3.53k
  }
271
272
  /* newline, for error messages */
273
56.7M
  if (data == '\n') {
274
224
    helper->linecnt++;
275
224
    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
223
    fwupd_json_parser_helper_dump_acc(helper, token, str);
284
223
    return TRUE;
285
224
  }
286
56.7M
  helper->newlinecnt = 0;
287
288
  /* split */
289
56.7M
  if (data == ',') {
290
2.14k
    fwupd_json_parser_helper_dump_acc(helper, token, str);
291
2.14k
    return TRUE;
292
2.14k
  }
293
294
  /* control token */
295
56.7M
  if (data == FWUPD_JSON_PARSER_TOKEN_ARRAY_START ||
296
56.7M
      data == FWUPD_JSON_PARSER_TOKEN_ARRAY_END ||
297
56.7M
      data == FWUPD_JSON_PARSER_TOKEN_OBJECT_START ||
298
56.7M
      data == FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM ||
299
56.7M
      data == FWUPD_JSON_PARSER_TOKEN_OBJECT_END) {
300
57.8k
    fwupd_json_parser_helper_dump_acc(helper, token, str);
301
57.8k
    if (*token != FWUPD_JSON_PARSER_TOKEN_INVALID) {
302
7.98k
      *buf_offset_enable = FALSE;
303
7.98k
      return TRUE;
304
7.98k
    }
305
49.8k
    *token = (FwupdJsonParserToken)data;
306
49.8k
    helper->newlinecnt = 0;
307
49.8k
    helper->whitespacecnt = 0;
308
49.8k
    return TRUE;
309
57.8k
  }
310
311
  /* whitespace */
312
56.7M
  if (g_ascii_isspace(data)) {
313
506k
    const guint8 *buf = helper->buf->data;
314
506k
    gsize bufsz = helper->buf->len;
315
506k
    gsize offset = helper->buf_offset + 1;
316
506k
    guint whitespace_max = FWUPD_JSON_PARSER_INDENT_MAX * (helper->depth + 1);
317
318
    /* the most likely next char is another space */
319
632k
    for (; offset < bufsz; offset++) {
320
632k
      if (buf[offset] != ' ')
321
506k
        break;
322
632k
    }
323
506k
    helper->whitespacecnt += (offset - helper->buf_offset) - 1;
324
506k
    helper->buf_offset = offset - 1;
325
506k
    if (helper->whitespacecnt++ >= whitespace_max) {
326
32
      g_set_error(error,
327
32
            FWUPD_ERROR,
328
32
            FWUPD_ERROR_INVALID_DATA,
329
32
            "too much whitespace, limit was %u",
330
32
            whitespace_max);
331
32
      return FALSE;
332
32
    }
333
506k
    return TRUE;
334
506k
  }
335
336
  /* strip control chars */
337
56.2M
  if (g_ascii_iscntrl(data)) {
338
16
    g_set_error(error,
339
16
          FWUPD_ERROR,
340
16
          FWUPD_ERROR_INVALID_DATA,
341
16
          "ASCII control character detected 0x%x",
342
16
          (guint)data);
343
16
    return FALSE;
344
16
  }
345
346
  /* save acc */
347
56.2M
  g_string_append_c(helper->acc, data);
348
56.2M
  helper->whitespacecnt = 0;
349
56.2M
  return TRUE;
350
56.2M
}
351
352
static gboolean
353
fwupd_json_parser_helper_get_next_token(FwupdJsonParserHelper *helper,
354
          FwupdJsonParserToken *token,
355
          GRefString **str,
356
          GError **error)
357
77.6k
{
358
  /* process each byte until we get a token */
359
56.9M
  while (*token == FWUPD_JSON_PARSER_TOKEN_INVALID) {
360
56.8M
    gboolean buf_offset_enable = TRUE;
361
56.8M
    if (!fwupd_json_parser_helper_get_next_token_chunk(helper,
362
56.8M
                   token,
363
56.8M
                   str,
364
56.8M
                   &buf_offset_enable,
365
56.8M
                   error))
366
993
      return FALSE;
367
56.8M
    if (buf_offset_enable)
368
56.8M
      helper->buf_offset++;
369
56.8M
  }
370
371
  /* success */
372
76.6k
  return TRUE;
373
77.6k
}
374
375
static gboolean
376
fwupd_json_parser_helper_check_depth(FwupdJsonParser *self, guint depth, GError **error)
377
19.6k
{
378
19.6k
  if (self->max_depth > 0 && depth > self->max_depth) {
379
23
    g_set_error(error,
380
23
          FWUPD_ERROR,
381
23
          FWUPD_ERROR_INVALID_DATA,
382
23
          "structure too deep, limit was %u",
383
23
          depth);
384
23
    return FALSE;
385
23
  }
386
19.6k
  return TRUE;
387
19.6k
}
388
389
static FwupdJsonObject *
390
fwupd_json_parser_load_object(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error);
391
392
static FwupdJsonArray *
393
fwupd_json_parser_load_array(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error)
394
10.2k
{
395
10.2k
  g_autoptr(FwupdJsonArray) json_arr = fwupd_json_array_new();
396
397
10.2k
  if (G_UNLIKELY(!fwupd_json_parser_helper_check_depth(self, ++helper->depth, error)))
398
19
    return NULL;
399
30.6k
  while (TRUE) {
400
30.6k
    g_autoptr(GRefString) str = NULL;
401
30.6k
    FwupdJsonParserToken token = FWUPD_JSON_PARSER_TOKEN_INVALID;
402
403
30.6k
    if (!fwupd_json_parser_helper_get_next_token(helper, &token, &str, error))
404
136
      return NULL;
405
30.5k
    if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_END)
406
9.70k
      break;
407
20.8k
    if (token == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) {
408
4.83k
      g_autoptr(FwupdJsonObject) json_obj =
409
4.83k
          fwupd_json_parser_load_object(self, helper, error);
410
4.83k
      if (G_UNLIKELY(json_obj == NULL))
411
54
        return NULL;
412
4.77k
      fwupd_json_array_add_object(json_arr, json_obj);
413
16.0k
    } else if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) {
414
7.62k
      g_autoptr(FwupdJsonArray) json_array2 =
415
7.62k
          fwupd_json_parser_load_array(self, helper, error);
416
7.62k
      if (G_UNLIKELY(json_array2 == NULL))
417
342
        return NULL;
418
7.28k
      fwupd_json_array_add_array(json_arr, json_array2);
419
8.40k
    } else if (token == FWUPD_JSON_PARSER_TOKEN_STRING) {
420
2.68k
      fwupd_json_array_add_string_internal(json_arr, str);
421
5.71k
    } else if (token == FWUPD_JSON_PARSER_TOKEN_RAW) {
422
5.69k
      if (G_UNLIKELY(str == NULL)) {
423
0
        g_set_error(error,
424
0
              FWUPD_ERROR,
425
0
              FWUPD_ERROR_INVALID_DATA,
426
0
              "no raw data on line %u",
427
0
              helper->linecnt);
428
0
        return NULL;
429
0
      }
430
5.69k
      fwupd_json_array_add_raw_internal(json_arr, str);
431
5.69k
    } else {
432
17
      g_set_error_literal(error,
433
17
              FWUPD_ERROR,
434
17
              FWUPD_ERROR_INVALID_DATA,
435
17
              "object delimiter not expected in array");
436
17
      return NULL;
437
17
    }
438
20.4k
    if (G_UNLIKELY(self->max_items > 0 &&
439
20.4k
             fwupd_json_array_get_size(json_arr) > self->max_items)) {
440
9
      g_set_error(error,
441
9
            FWUPD_ERROR,
442
9
            FWUPD_ERROR_INVALID_DATA,
443
9
            "too many items in array, limit was %u",
444
9
            self->max_items);
445
9
      return NULL;
446
9
    }
447
20.4k
  }
448
9.70k
  helper->depth--;
449
450
  /* success */
451
9.70k
  return g_steal_pointer(&json_arr);
452
10.2k
}
453
454
static FwupdJsonObject *
455
fwupd_json_parser_load_object(FwupdJsonParser *self, FwupdJsonParserHelper *helper, GError **error)
456
9.41k
{
457
9.41k
  g_autoptr(FwupdJsonObject) json_obj = fwupd_json_object_new();
458
459
9.41k
  if (!fwupd_json_parser_helper_check_depth(self, ++helper->depth, error))
460
4
    return NULL;
461
21.1k
  while (TRUE) {
462
21.1k
    FwupdJsonParserToken token1 = FWUPD_JSON_PARSER_TOKEN_INVALID;
463
21.1k
    FwupdJsonParserToken token2 = FWUPD_JSON_PARSER_TOKEN_INVALID;
464
21.1k
    FwupdJsonParserToken token3 = FWUPD_JSON_PARSER_TOKEN_INVALID;
465
21.1k
    g_autoptr(GRefString) key = NULL;
466
21.1k
    g_autoptr(GRefString) val = NULL;
467
468
    /* "key" : value */
469
21.1k
    if (!fwupd_json_parser_helper_get_next_token(helper, &token1, &key, error))
470
631
      return NULL;
471
20.4k
    if (token1 == FWUPD_JSON_PARSER_TOKEN_OBJECT_END)
472
8.42k
      break;
473
12.0k
    if (G_UNLIKELY(token1 != FWUPD_JSON_PARSER_TOKEN_STRING)) {
474
243
      g_set_error(error,
475
243
            FWUPD_ERROR,
476
243
            FWUPD_ERROR_INVALID_DATA,
477
243
            "object key '%s' must be quoted on line %u",
478
243
            key,
479
243
            helper->linecnt);
480
243
      return NULL;
481
243
    }
482
11.8k
    if (!fwupd_json_parser_helper_get_next_token(helper, &token2, NULL, error))
483
11
      return NULL;
484
11.8k
    if (token2 != FWUPD_JSON_PARSER_TOKEN_OBJECT_DELIM) {
485
6
      g_set_error(error,
486
6
            FWUPD_ERROR,
487
6
            FWUPD_ERROR_INVALID_DATA,
488
6
            "did not find object delimiter on line %u",
489
6
            helper->linecnt);
490
6
      return NULL;
491
6
    }
492
11.7k
    if (!fwupd_json_parser_helper_get_next_token(helper, &token3, &val, error))
493
2
      return NULL;
494
495
11.7k
    if (token3 == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) {
496
3.29k
      g_autoptr(FwupdJsonObject) json_obj2 =
497
3.29k
          fwupd_json_parser_load_object(self, helper, error);
498
3.29k
      if (G_UNLIKELY(json_obj2 == NULL))
499
78
        return NULL;
500
3.22k
      fwupd_json_object_add_object_internal(json_obj, key, json_obj2);
501
8.49k
    } else if (token3 == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) {
502
2.17k
      g_autoptr(FwupdJsonArray) json_array2 =
503
2.17k
          fwupd_json_parser_load_array(self, helper, error);
504
2.17k
      if (G_UNLIKELY(json_array2 == NULL))
505
10
        return NULL;
506
2.16k
      fwupd_json_object_add_array_internal(json_obj, key, json_array2);
507
6.31k
    } else if (token3 == FWUPD_JSON_PARSER_TOKEN_STRING) {
508
2.24k
      fwupd_json_object_add_string_internal(json_obj, key, val, helper->flags);
509
4.06k
    } else if (token3 == FWUPD_JSON_PARSER_TOKEN_NULL) {
510
1.69k
      fwupd_json_object_add_null_internal(json_obj, key, helper->flags);
511
2.36k
    } else {
512
2.36k
      if (G_UNLIKELY(val == NULL)) {
513
3
        g_set_error(error,
514
3
              FWUPD_ERROR,
515
3
              FWUPD_ERROR_INVALID_DATA,
516
3
              "did not find raw value on line %u",
517
3
              helper->linecnt);
518
3
        return NULL;
519
3
      }
520
2.36k
      fwupd_json_object_add_raw_internal(json_obj, key, val, helper->flags);
521
2.36k
    }
522
11.7k
    if (G_UNLIKELY(self->max_items > 0 &&
523
11.7k
             fwupd_json_object_get_size(json_obj) > self->max_items)) {
524
2
      g_set_error(error,
525
2
            FWUPD_ERROR,
526
2
            FWUPD_ERROR_INVALID_DATA,
527
2
            "too many items in object, limit was %u",
528
2
            self->max_items);
529
2
      return NULL;
530
2
    }
531
11.7k
  }
532
8.42k
  helper->depth--;
533
534
  /* success */
535
8.42k
  return g_steal_pointer(&json_obj);
536
9.40k
}
537
538
static FwupdJsonNode *
539
fwupd_json_parser_load_from_stream_internal(FwupdJsonParser *self,
540
              FwupdJsonParserHelper *helper,
541
              GError **error)
542
2.18k
{
543
2.18k
  FwupdJsonParserToken token = FWUPD_JSON_PARSER_TOKEN_INVALID;
544
2.18k
  g_autoptr(GRefString) str = NULL;
545
546
2.18k
#ifndef SUPPORTED_BUILD
547
  /* runtime warnings */
548
2.18k
  if (self->max_depth == G_MAXUINT16)
549
0
    g_warning("using the default max depth; use fwupd_json_parser_set_max_depth()");
550
2.18k
  if (self->max_items == G_MAXUINT16)
551
0
    g_warning("using the default max items; use fwupd_json_parser_set_max_items()");
552
2.18k
  if (self->max_quoted == G_MAXUINT16)
553
0
    g_warning("using the default max quoted; use fwupd_json_parser_set_max_quoted()");
554
2.18k
#endif
555
556
2.18k
  if (!fwupd_json_parser_helper_get_next_token(helper, &token, &str, error))
557
213
    return NULL;
558
1.97k
  if (token == FWUPD_JSON_PARSER_TOKEN_OBJECT_START) {
559
1.27k
    g_autoptr(FwupdJsonObject) json_obj = NULL;
560
1.27k
    json_obj = fwupd_json_parser_load_object(self, helper, error);
561
1.27k
    if (json_obj == NULL)
562
858
      return NULL;
563
420
    return fwupd_json_node_new_object(json_obj);
564
1.27k
  }
565
692
  if (token == FWUPD_JSON_PARSER_TOKEN_ARRAY_START) {
566
484
    g_autoptr(FwupdJsonArray) json_arr = NULL;
567
484
    json_arr = fwupd_json_parser_load_array(self, helper, error);
568
484
    if (json_arr == NULL)
569
225
      return NULL;
570
259
    return fwupd_json_node_new_array(json_arr);
571
484
  }
572
208
  if (token == FWUPD_JSON_PARSER_TOKEN_STRING)
573
104
    return fwupd_json_node_new_string_internal(str);
574
104
  if (token == FWUPD_JSON_PARSER_TOKEN_RAW)
575
96
    return fwupd_json_node_new_raw_internal(str);
576
577
  /* failed */
578
8
  g_set_error_literal(error,
579
8
          FWUPD_ERROR,
580
8
          FWUPD_ERROR_INVALID_DATA,
581
8
          "invalid JSON; token was not object, array, string or raw");
582
8
  return NULL;
583
104
}
584
585
/**
586
 * fwupd_json_parser_load_from_bytes: (skip):
587
 * @self: a #FwupdJsonParser
588
 * @blob: a #GBytes
589
 * @flags: a #FwupdJsonLoadFlags
590
 * @error: (nullable): optional return location for an error
591
 *
592
 * Loads JSON from a string.
593
 *
594
 * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error
595
 *
596
 * Since: 2.1.1
597
 **/
598
FwupdJsonNode *
599
fwupd_json_parser_load_from_bytes(FwupdJsonParser *self,
600
          GBytes *blob,
601
          FwupdJsonLoadFlags flags,
602
          GError **error)
603
0
{
604
0
  g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(self);
605
606
0
  g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL);
607
0
  g_return_val_if_fail(blob != NULL, NULL);
608
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
609
610
0
  helper->flags = flags;
611
0
  helper->stream = g_memory_input_stream_new_from_bytes(blob);
612
0
  return fwupd_json_parser_load_from_stream_internal(self, helper, error);
613
0
}
614
615
/**
616
 * fwupd_json_parser_load_from_data: (skip):
617
 * @self: a #FwupdJsonParser
618
 * @text: a string
619
 * @flags: a #FwupdJsonLoadFlags
620
 * @error: (nullable): optional return location for an error
621
 *
622
 * Loads JSON from a string.
623
 *
624
 * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error
625
 *
626
 * Since: 2.1.1
627
 **/
628
FwupdJsonNode *
629
fwupd_json_parser_load_from_data(FwupdJsonParser *self,
630
         const gchar *text,
631
         FwupdJsonLoadFlags flags,
632
         GError **error)
633
0
{
634
0
  g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(self);
635
636
0
  g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL);
637
0
  g_return_val_if_fail(text != NULL, NULL);
638
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
639
640
0
  helper->flags = flags;
641
0
  helper->stream = g_memory_input_stream_new_from_data(text, strlen(text), NULL);
642
0
  return fwupd_json_parser_load_from_stream_internal(self, helper, error);
643
0
}
644
645
/**
646
 * fwupd_json_parser_load_from_stream: (skip):
647
 * @self: a #FwupdJsonParser
648
 * @stream: a #GInputStream
649
 * @flags: a #FwupdJsonLoadFlags
650
 * @error: (nullable): optional return location for an error
651
 *
652
 * Loads JSON from a stream.
653
 *
654
 * Returns: (transfer full): a #FwupdJsonNode, or %NULL for error
655
 *
656
 * Since: 2.1.1
657
 **/
658
FwupdJsonNode *
659
fwupd_json_parser_load_from_stream(FwupdJsonParser *self,
660
           GInputStream *stream,
661
           FwupdJsonLoadFlags flags,
662
           GError **error)
663
2.18k
{
664
2.18k
  g_autoptr(FwupdJsonParserHelper) helper = fwupd_json_parser_helper_new(self);
665
666
2.18k
  g_return_val_if_fail(FWUPD_IS_JSON_PARSER(self), NULL);
667
2.18k
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
668
2.18k
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
669
670
  /* seek to start if possible */
671
2.18k
  if (G_IS_SEEKABLE(stream) && g_seekable_can_seek(G_SEEKABLE(stream))) {
672
2.18k
    if (!g_seekable_seek(G_SEEKABLE(stream), 0x0, G_SEEK_SET, NULL, error)) {
673
0
      fwupd_error_convert(error);
674
0
      return NULL;
675
0
    }
676
2.18k
  }
677
2.18k
  helper->stream = g_object_ref(stream);
678
2.18k
  helper->flags = flags;
679
2.18k
  return fwupd_json_parser_load_from_stream_internal(self, helper, error);
680
2.18k
}
681
682
static void
683
fwupd_json_parser_class_init(FwupdJsonParserClass *klass)
684
1
{
685
1
}
686
687
static void
688
fwupd_json_parser_init(FwupdJsonParser *self)
689
2.18k
{
690
2.18k
  self->max_depth = G_MAXUINT16;
691
2.18k
  self->max_items = G_MAXUINT16;
692
2.18k
  self->max_quoted = G_MAXUINT16;
693
2.18k
}
694
695
/**
696
 * fwupd_json_parser_new:
697
 *
698
 * Returns: (transfer full): a #FwupdJsonParser
699
 *
700
 * Since: 2.1.1
701
 **/
702
FwupdJsonParser *
703
fwupd_json_parser_new(void)
704
2.18k
{
705
2.18k
  return g_object_new(FWUPD_TYPE_JSON_PARSER, NULL);
706
2.18k
}