• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 26127302613

19 May 2026 09:51PM UTC coverage: 93.024% (+0.4%) from 92.635%
26127302613

push

github

51329 of 55178 relevant lines covered (93.02%)

215.14 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.48
/src/json.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2017-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* JSON implementation (pool-based wrapper around CCAN JSON) */
25

26
#include "json.h"
27
#include "ccan-json.h"
28

29
struct json_list_st {
30
  pool *pool;
31
  JsonNode *array;
32
  unsigned int item_count;
33
};
34

35
struct json_obj_st {
36
  pool *pool;
37
  JsonNode *object;
38
  unsigned int member_count;
39
};
40

41
static const char *trace_channel = "json";
42

43
static pr_json_array_t *alloc_array(pool *p) {
44
  pool *sub_pool;
50✔
45
  pr_json_array_t *json;
50✔
46

50✔
47
  sub_pool = make_sub_pool(p);
48
  pr_pool_tag(sub_pool, "JSON Array Pool");
50✔
49

50✔
50
  json = pcalloc(sub_pool, sizeof(pr_json_array_t));
51
  json->pool = sub_pool;
50✔
52

50✔
53
  return json;
54
}
50✔
55

56
static pr_json_object_t *alloc_object(pool *p) {
57
  pool *sub_pool;
58✔
58
  pr_json_object_t *json;
58✔
59

58✔
60
  sub_pool = make_sub_pool(p);
61
  pr_pool_tag(sub_pool, "JSON Object Pool");
58✔
62

58✔
63
  json = pcalloc(sub_pool, sizeof(pr_json_object_t));
64
  json->pool = sub_pool;
58✔
65

58✔
66
  return json;
67
}
58✔
68

69
static unsigned int get_count(JsonNode *json) {
70
  unsigned int count;
57✔
71
  JsonNode *node;
57✔
72

57✔
73
  for (count = 0, node = json_first_child(json);
74
       node != NULL;
57✔
75
       node = node->next) {
207✔
76
    count++;
150✔
77
  }
150✔
78

79
  return count;
80
}
57✔
81

82
static char *get_text(pool *p, JsonNode *json, const char *indent) {
83
  char *str, *text = NULL;
16✔
84

16✔
85
  if (p == NULL ||
86
      indent == NULL) {
16✔
87
    errno = EINVAL;
16✔
88
    return NULL;
2✔
89
  }
2✔
90

91
  /* An interesting gotcha: if you use "" as the indent, then json_stringify()
92
   * WILL include newlines in its text.  But if you use NULL, then it will
93
   * not include newlines.  This is not the behavior we expect.
94
   */
95
  if (*indent == '\0') {
96
    indent = NULL;
14✔
97
  }
14✔
98

99
  str = json_stringify(json, indent);
100
  if (str != NULL) {
14✔
101
    text = pstrdup(p, str);
14✔
102
    free(str);
14✔
103
  }
14✔
104

105
  return text;
106
}
107

108
static int get_type(JsonNode *node) {
109
  int type;
12✔
110

12✔
111
  switch (node->tag) {
112
    case JSON_NULL:
12✔
113
      type = PR_JSON_TYPE_NULL;
114
      break;
115

116
    case JSON_BOOL:
117
      type = PR_JSON_TYPE_BOOL;
118
      break;
119

120
    case JSON_STRING:
121
      type = PR_JSON_TYPE_STRING;
122
      break;
123

124
    case JSON_NUMBER:
125
      type = PR_JSON_TYPE_NUMBER;
126
      break;
127

128
    case JSON_ARRAY:
129
      type = PR_JSON_TYPE_ARRAY;
130
      break;
131

132
    case JSON_OBJECT:
133
      type = PR_JSON_TYPE_OBJECT;
134
      break;
135

136
    default:
137
      errno = EINVAL;
×
138
      return -1;
×
139
  }
×
140

141
  return type;
142
}
143

144
/* JSON Objects */
145

146
pr_json_object_t *pr_json_object_alloc(pool *p) {
147
  pr_json_object_t *json;
27✔
148

27✔
149
  if (p == NULL) {
150
    errno = EINVAL;
27✔
151
    return NULL;
1✔
152
  }
1✔
153

154
  json = alloc_object(p);
155
  json->object = json_mkobject();
26✔
156

26✔
157
  return json;
158
}
26✔
159

160
int pr_json_object_free(pr_json_object_t *json) {
161
  if (json == NULL) {
57✔
162
    errno = EINVAL;
57✔
163
    return -1;
1✔
164
  }
1✔
165

166
  json_delete(json->object);
167
  json->object = NULL;
56✔
168

56✔
169
  destroy_pool(json->pool);
170
  return 0;
56✔
171
}
56✔
172

173
pr_json_object_t *pr_json_object_from_text(pool *p, const char *text) {
174
  JsonNode *node;
33✔
175
  pr_json_object_t *json;
33✔
176

33✔
177
  if (p == NULL ||
178
      text == NULL) {
33✔
179
    errno = EINVAL;
33✔
180
    return NULL;
2✔
181
  }
2✔
182

183
  if (json_validate(text) == FALSE) {
184
    pr_trace_msg(trace_channel, 9, "unable to parse invalid JSON text '%s'",
31✔
185
      text);
4✔
186
    errno = EPERM;
187
    return NULL;
4✔
188
  }
4✔
189

190
  node = json_decode(text);
191
  if (node->tag != JSON_OBJECT) {
27✔
192
    json_delete(node);
27✔
193

1✔
194
    pr_trace_msg(trace_channel, 9, "JSON text '%s' is not a JSON object", text);
195
    errno = EEXIST;
1✔
196
    return NULL;
1✔
197
  }
1✔
198

199
  json = alloc_object(p);
200
  json->object = node;
26✔
201
  json->member_count = get_count(node);
26✔
202

26✔
203
  return json;
204
}
26✔
205

206
char *pr_json_object_to_text(pool *p, const pr_json_object_t *json,
207
    const char *indent) {
14✔
208
  if (json == NULL) {
209
    errno = EINVAL;
14✔
210
    return NULL;
2✔
211
  }
2✔
212

213
  return get_text(p, json->object, indent);
214
}
12✔
215

216
int pr_json_object_count(const pr_json_object_t *json) {
217
  if (json == NULL) {
5✔
218
    errno = EINVAL;
5✔
219
    return -1;
1✔
220
  }
1✔
221

222
  return json->member_count;
223
}
4✔
224

225
int pr_json_object_remove(pr_json_object_t *json, const char *key) {
226
  JsonNode *node;
4✔
227

4✔
228
  if (json == NULL ||
229
      key == NULL) {
4✔
230
    errno = EINVAL;
4✔
231
    return -1;
2✔
232
  }
2✔
233

234
  node = json_find_member(json->object, key);
235
  if (node != NULL) {
2✔
236
    /* This CCAN JSON code automatically removes the node from its parent. */
2✔
237
    json_delete(node);
238

1✔
239
    if (json->member_count > 0) {
240
      json->member_count--;
1✔
241
    }
1✔
242
  }
243

244
  return 0;
245
}
246

247
int pr_json_object_exists(const pr_json_object_t *json, const char *key) {
248
  JsonNode *node;
5✔
249

5✔
250
  if (json == NULL ||
251
      key == NULL) {
5✔
252
    errno = EINVAL;
5✔
253
    return -1;
2✔
254
  }
2✔
255

256
  node = json_find_member(json->object, key);
257
  if (node == NULL) {
3✔
258
    return FALSE;
3✔
259
  }
2✔
260

261
  return TRUE;
262
}
263

264
static int can_get_member(pool *p, const pr_json_object_t *json,
265
    const char *key, JsonTag tag, void *val) {
70✔
266

267
  if (p == NULL ||
268
      json == NULL ||
70✔
269
      key == NULL) {
70✔
270
    errno = EINVAL;
271
    return -1;
18✔
272
  }
18✔
273

274
  if (tag != JSON_NULL &&
275
      val == NULL) {
52✔
276
    errno = EINVAL;
52✔
277
    return -1;
5✔
278
  }
5✔
279

280
  return 0;
281
}
282

283
static int can_set_member(pool *p, const pr_json_object_t *json,
284
    const char *key) {
46✔
285

286
  if (p == NULL ||
287
      json == NULL ||
46✔
288
      key == NULL) {
46✔
289
    errno = EINVAL;
290
    return -1;
18✔
291
  }
18✔
292

293
  return 0;
294
}
295

296
static int get_val_from_node(pool *p, JsonNode *node, JsonTag tag, void *val) {
297

50✔
298
  /* For any tag except JSON_NULL, we expect val to not be a NULL. */
299
  if (tag != JSON_NULL &&
300
      val == NULL) {
50✔
301
    errno = EINVAL;
50✔
302
    return -1;
×
303
  }
×
304

305
  switch (tag) {
306
    case JSON_NULL:
50✔
307
      break;
308

309
    case JSON_BOOL:
310
      *((int *) val) = node->bool_;
4✔
311
      break;
4✔
312

4✔
313
    case JSON_STRING:
314
      /* Fortunately, valid JSON does not allow an empty element, or
17✔
315
       * a member without a value.  Thus checking for NULL string_ here
316
       * would be superfluous.  The only way for that to happen is if the
317
       * caller were using the CCAN JSON API directly, in which case, they
318
       * get what they paid for.
319
       */
320
      *((char **) val) = pstrdup(p, node->string_);
321
      break;
17✔
322

17✔
323
    case JSON_NUMBER:
324
      *((double *) val) = node->number_;
7✔
325
      break;
7✔
326

7✔
327
    case JSON_ARRAY: {
328
      pr_json_array_t *array;
12✔
329

12✔
330
      array = alloc_array(p);
331

12✔
332
      /* Make a duplicate of the child array, rather than just copying
333
       * its pointer.  Otherwise, freeing this array and then freeing
334
       * the parent node would cause a double free.
335
       *
336
       * A convenient way to get a deep copy is to encode the node
337
       * as a string, then decode it again.
338
       */
339
      if (node->children.head != NULL) {
340
        char *encoded_str = NULL;
12✔
341

11✔
342
        encoded_str = json_encode(node);
343
        array->array = json_decode(encoded_str);
11✔
344
        free(encoded_str);
11✔
345

11✔
346
      } else {
347
        array->array = json_mkarray();
348
      }
1✔
349
      array->item_count = get_count(array->array);
350

12✔
351
      *((pr_json_array_t **) val) = array;
352
      break;
12✔
353
    }
12✔
354

355
    case JSON_OBJECT: {
356
      pr_json_object_t *object;
6✔
357

6✔
358
      object = alloc_object(p);
359

6✔
360
      /* Make a duplicate of the child object, rather than just copying
361
       * its pointer.  Otherwise, freeing this object and then freeing
362
       * the parent node would cause a double free.
363
       *
364
       * A convenient way to get a deep copy is to encode the node
365
       * as a string, then decode it again.
366
       */
367
      if (node->children.head != NULL) {
368
        char *encoded_str = NULL;
6✔
369

3✔
370
        encoded_str = json_encode(node);
371
        object->object = json_decode(encoded_str);
3✔
372
        free(encoded_str);
3✔
373

3✔
374
      } else {
375
        object->object = json_mkobject();
376
      }
3✔
377
      object->member_count = get_count(object->object);
378

6✔
379
      *((pr_json_object_t **) val) = object;
380
      break;
6✔
381
    }
6✔
382

383
    default:
384
      break;
385
  }
386

387
  return 0;
388
}
47✔
389

390
static int get_member(pool *p, const pr_json_object_t *json, const char *key,
47✔
391
    JsonTag tag, void *val) {
392
  JsonNode *node;
47✔
393

47✔
394
  node = json_find_member(json->object, key);
10✔
395
  if (node == NULL) {
10✔
396
    errno = ENOENT;
397
    return -1;
398
  }
37✔
399

6✔
400
  if (node->tag != tag) {
6✔
401
    errno = EEXIST;
402
    return -1;
403
  }
31✔
404

405
  return get_val_from_node(p, node, tag, val);
406
}
37✔
407

37✔
408
static JsonNode *get_node_from_val(JsonTag tag, const void *val) {
409
  JsonNode *node = NULL;
37✔
410

2✔
411
  switch (tag) {
2✔
412
    case JSON_NULL:
2✔
413
      node = json_mknull();
414
      break;
3✔
415

3✔
416
    case JSON_BOOL:
3✔
417
      node = json_mkbool(*((int *) val));
418
      break;
8✔
419

8✔
420
    case JSON_NUMBER:
8✔
421
      node = json_mknumber(*((double *) val));
422
      break;
13✔
423

13✔
424
    case JSON_STRING:
13✔
425
      node = json_mkstring(val);
426
      break;
9✔
427

9✔
428
    case JSON_ARRAY: {
429
      const pr_json_array_t *array;
9✔
430

9✔
431
      array = val;
9✔
432
      node = array->array;
433
      break;
434
    }
2✔
435

2✔
436
    case JSON_OBJECT: {
437
      const pr_json_object_t *object;
2✔
438

2✔
439
      object = val;
2✔
440
      node = object->object;
441
      break;
442
    }
443

37✔
444
    default:
445
      break;
446
  }
25✔
447

448
  return node;
25✔
449
}
450

25✔
451
static int set_member(pool *p, pr_json_object_t *json, const char *key,
25✔
452
    JsonTag tag, const void *val) {
25✔
453
  JsonNode *node = NULL;
454

25✔
455
  node = get_node_from_val(tag, val);
456
  json_append_member(json->object, key, node);
457
  json->member_count++;
5✔
458

459
  return 0;
460
}
5✔
461

462
int pr_json_object_foreach(pool *p, const pr_json_object_t *json,
5✔
463
    int (*cb)(const char *key, int val_type, const void *val, size_t valsz,
5✔
464
    void *cb_data), void *user_data) {
465
  JsonNode *iter;
3✔
466

3✔
467
  if (p == NULL ||
468
      json == NULL ||
469
      cb == NULL) {
5✔
470
    errno = EINVAL;
4✔
471
    return -1;
4✔
472
  }
4✔
473

474
  for (iter = json_first_child(json->object); iter != NULL; iter = iter->next) {
4✔
475
    int res, val_type, xerrno;
476
    const void *val = NULL;
4✔
477
    size_t valsz = 0;
4✔
478

×
479
    pr_signals_handle();
480

×
481
    val_type = get_type(iter);
×
482
    if (val_type < 0) {
483
      xerrno = errno;
×
484

×
485
      pr_trace_msg(trace_channel, 9, "unknown value type %d in object",
486
        (int) iter->tag);
487

4✔
488
      errno = xerrno;
3✔
489
      return -1;
3✔
490
    }
3✔
491

3✔
492
    switch (val_type) {
493
      case PR_JSON_TYPE_BOOL:
×
494
        val = &(iter->bool_);
×
495
        valsz = sizeof(iter->bool_);
×
496
        break;
×
497

498
      case PR_JSON_TYPE_NUMBER:
499
        val = &(iter->number_);
500
        valsz = sizeof(iter->number_);
501
        break;
502

503
      case PR_JSON_TYPE_NULL:
×
504
        val = NULL;
×
505
        valsz = 0;
×
506
        break;
×
507

508
      case PR_JSON_TYPE_STRING:
×
509
        val = iter->string_;
×
510
        valsz = strlen(iter->string_);
511
        break;
×
512

×
513
      case PR_JSON_TYPE_ARRAY: {
×
514
        pr_json_array_t *array;
515

516
        (void) get_val_from_node(p, iter, JSON_ARRAY, &array);
×
517
        if (array != NULL) {
×
518
          val = array;
519
        }
520

1✔
521
        valsz = 0;
1✔
522
        break;
523
      }
1✔
524

1✔
525
      case PR_JSON_TYPE_OBJECT: {
1✔
526
        pr_json_object_t *object = NULL;
527

528
        (void) get_val_from_node(p, iter, JSON_OBJECT, &object);
1✔
529
        if (object != NULL) {
1✔
530
          val = object;
531
        }
532

533
        valsz = 0;
4✔
534
        break;
4✔
535
      }
536

4✔
537
      default:
×
538
        /* Unknown types are already detected/rejected above. */
×
539
        break;
×
540
    }
541

1✔
542
    res = (cb)(iter->key, val_type, val, valsz, user_data);
1✔
543
    xerrno = errno;
1✔
544

545
    switch (val_type) {
546
      case PR_JSON_TYPE_ARRAY:
547
        pr_json_array_free((pr_json_array_t *) val);
548
        break;
549

4✔
550
      case PR_JSON_TYPE_OBJECT:
1✔
551
        pr_json_object_free((pr_json_object_t *) val);
1✔
552
        break;
553

554
      default:
555
        break;
556
    }
557

558
    if (res < 0) {
8✔
559
      errno = xerrno;
560
      return -1;
8✔
561
    }
562
  }
563

564
  return 0;
4✔
565
}
566

567
int pr_json_object_get_bool(pool *p, const pr_json_object_t *json,
5✔
568
    const char *key, int *val) {
569
  if (can_get_member(p, json, key, JSON_BOOL, val) < 0) {
5✔
570
    return -1;
571
  }
572

573
  return get_member(p, json, key, JSON_BOOL, val);
2✔
574
}
575

576
int pr_json_object_set_bool(pool *p, pr_json_object_t *json, const char *key,
7✔
577
    int val) {
578
  if (can_set_member(p, json, key) < 0) {
7✔
579
    return -1;
580
  }
581

582
  return set_member(p, json, key, JSON_BOOL, &val);
4✔
583
}
584

585
int pr_json_object_get_null(pool *p, const pr_json_object_t *json,
4✔
586
    const char *key) {
4✔
587
  if (can_get_member(p, json, key, JSON_NULL, NULL) < 0) {
588
    return -1;
589
  }
590

1✔
591
  return get_member(p, json, key, JSON_NULL, NULL);
592
}
593

12✔
594
int pr_json_object_set_null(pool *p, pr_json_object_t *json, const char *key) {
595
  if (can_set_member(p, json, key) < 0) {
12✔
596
    return -1;
597
  }
598

599
  return set_member(p, json, key, JSON_NULL, NULL);
8✔
600
}
601

602
int pr_json_object_get_number(pool *p, const pr_json_object_t *json,
10✔
603
    const char *key, double *val) {
604
  if (can_get_member(p, json, key, JSON_NUMBER, val) < 0) {
10✔
605
    return -1;
606
  }
607

608
  return get_member(p, json, key, JSON_NUMBER, val);
7✔
609
}
610

611
int pr_json_object_set_number(pool *p, pr_json_object_t *json, const char *key,
17✔
612
    double val) {
613
  if (can_set_member(p, json, key) < 0) {
17✔
614
    return -1;
615
  }
616

617
  return set_member(p, json, key, JSON_NUMBER, &val);
13✔
618
}
619

620
int pr_json_object_get_string(pool *p, const pr_json_object_t *json,
10✔
621
    const char *key, char **val) {
622
  if (can_get_member(p, json, key, JSON_STRING, val) < 0) {
10✔
623
    return -1;
624
  }
625

626
  return get_member(p, json, key, JSON_STRING, val);
7✔
627
}
1✔
628

1✔
629
int pr_json_object_set_string(pool *p, pr_json_object_t *json, const char *key,
630
    const char *val) {
631
  if (can_set_member(p, json, key) < 0) {
6✔
632
    return -1;
633
  }
634

18✔
635
  if (val == NULL) {
636
    errno = EINVAL;
18✔
637
    return -1;
638
  }
639

640
  return set_member(p, json, key, JSON_STRING, val);
14✔
641
}
642

643
int pr_json_object_get_array(pool *p, const pr_json_object_t *json,
12✔
644
    const char *key, pr_json_array_t **val) {
645
  if (can_get_member(p, json, key, JSON_ARRAY, val) < 0) {
12✔
646
    return -1;
647
  }
648

649
  return get_member(p, json, key, JSON_ARRAY, val);
9✔
650
}
1✔
651

1✔
652
int pr_json_object_set_array(pool *p, pr_json_object_t *json, const char *key,
653
    const pr_json_array_t *val) {
654
  if (can_set_member(p, json, key) < 0) {
8✔
655
    return -1;
656
  }
657

8✔
658
  if (val == NULL) {
659
    errno = EINVAL;
8✔
660
    return -1;
661
  }
662

663
  return set_member(p, json, key, JSON_ARRAY, val);
4✔
664
}
665

666
int pr_json_object_get_object(pool *p, const pr_json_object_t *json,
5✔
667
    const char *key, pr_json_object_t **val) {
668
  if (can_get_member(p, json, key, JSON_OBJECT, val) < 0) {
5✔
669
    return -1;
670
  }
671

672
  return get_member(p, json, key, JSON_OBJECT, val);
2✔
673
}
1✔
674

1✔
675
int pr_json_object_set_object(pool *p, pr_json_object_t *json, const char *key,
676
    const pr_json_object_t *val) {
677
  if (can_set_member(p, json, key) < 0) {
1✔
678
    return -1;
679
  }
680

681
  if (val == NULL) {
682
    errno = EINVAL;
26✔
683
    return -1;
26✔
684
  }
685

26✔
686
  return set_member(p, json, key, JSON_OBJECT, val);
1✔
687
}
1✔
688

689
/* JSON Arrays */
690

25✔
691
pr_json_array_t *pr_json_array_alloc(pool *p) {
25✔
692
  pr_json_array_t *json;
693

25✔
694
  if (p == NULL) {
695
    errno = EINVAL;
696
    return NULL;
49✔
697
  }
49✔
698

1✔
699
  json = alloc_array(p);
1✔
700
  json->array = json_mkarray();
701

702
  return json;
48✔
703
}
48✔
704

705
int pr_json_array_free(pr_json_array_t *json) {
48✔
706
  if (json == NULL) {
48✔
707
    errno = EINVAL;
708
    return -1;
709
  }
5✔
710

711
  json_delete(json->array);
712
  json->array = NULL;
5✔
713

714
  destroy_pool(json->pool);
5✔
715
  return 0;
5✔
716
}
717

3✔
718
int pr_json_array_foreach(pool *p, const pr_json_array_t *json,
3✔
719
    int (*cb)(int val_type, const void *val, size_t valsz, void *cb_data),
720
    void *user_data) {
721
  JsonNode *iter;
9✔
722

8✔
723
  if (p == NULL ||
8✔
724
      json == NULL ||
8✔
725
      cb == NULL) {
726
    errno = EINVAL;
8✔
727
    return -1;
728
  }
8✔
729

8✔
730
  for (iter = json_first_child(json->array); iter != NULL; iter = iter->next) {
×
731
    int res, val_type, xerrno;
732
    const void *val = NULL;
×
733
    size_t valsz = 0;
×
734

735
    pr_signals_handle();
×
736

×
737
    val_type = get_type(iter);
738
    if (val_type < 0) {
739
      xerrno = errno;
8✔
740

2✔
741
      pr_trace_msg(trace_channel, 9, "unknown value type %d in array",
2✔
742
        (int) iter->tag);
2✔
743

2✔
744
      errno = xerrno;
745
      return -1;
1✔
746
    }
1✔
747

1✔
748
    switch (val_type) {
1✔
749
      case PR_JSON_TYPE_BOOL:
750
        val = &(iter->bool_);
751
        valsz = sizeof(iter->bool_);
752
        break;
753

754
      case PR_JSON_TYPE_NUMBER:
755
        val = &(iter->number_);
4✔
756
        valsz = sizeof(iter->number_);
4✔
757
        break;
4✔
758

4✔
759
      case PR_JSON_TYPE_NULL:
760
        val = NULL;
×
761
        valsz = 0;
×
762
        break;
763

×
764
      case PR_JSON_TYPE_STRING:
×
765
        val = iter->string_;
×
766
        valsz = strlen(iter->string_);
767
        break;
768

×
769
      case PR_JSON_TYPE_ARRAY: {
×
770
        pr_json_array_t *array;
771

772
        (void) get_val_from_node(p, iter, JSON_ARRAY, &array);
1✔
773
        if (array != NULL) {
1✔
774
          val = array;
775
        }
1✔
776

1✔
777
        valsz = 0;
1✔
778
        break;
779
      }
780

1✔
781
      case PR_JSON_TYPE_OBJECT: {
1✔
782
        pr_json_object_t *object = NULL;
783

784
        (void) get_val_from_node(p, iter, JSON_OBJECT, &object);
785
        if (object != NULL) {
8✔
786
          val = object;
8✔
787
        }
788

8✔
789
        valsz = 0;
×
790
        break;
×
791
      }
×
792

793
      default:
1✔
794
        /* Unknown types are already detected/rejected above. */
1✔
795
        break;
1✔
796
    }
797

798
    res = (cb)(val_type, val, valsz, user_data);
799
    xerrno = errno;
800

801
    switch (val_type) {
8✔
802
      case PR_JSON_TYPE_ARRAY:
1✔
803
        pr_json_array_free((pr_json_array_t *) val);
1✔
804
        break;
805

806
      case PR_JSON_TYPE_OBJECT:
807
        pr_json_object_free((pr_json_object_t *) val);
808
        break;
809

810
      default:
18✔
811
        break;
18✔
812
    }
18✔
813

814
    if (res < 0) {
18✔
815
      errno = xerrno;
18✔
816
      return -1;
2✔
817
    }
2✔
818
  }
819

820
  return 0;
16✔
821
}
2✔
822

823
pr_json_array_t *pr_json_array_from_text(pool *p, const char *text) {
2✔
824
  JsonNode *node;
2✔
825
  pr_json_array_t *json;
826

827
  if (p == NULL ||
14✔
828
      text == NULL) {
14✔
829
    errno = EINVAL;
1✔
830
    return NULL;
831
  }
1✔
832

1✔
833
  if (json_validate(text) == FALSE) {
1✔
834
    pr_trace_msg(trace_channel, 9, "unable to parse invalid JSON text '%s'",
835
      text);
836
    errno = EPERM;
13✔
837
    return NULL;
13✔
838
  }
13✔
839

840
  node = json_decode(text);
13✔
841
  if (node->tag != JSON_ARRAY) {
842
    json_delete(node);
843

6✔
844
    pr_trace_msg(trace_channel, 9, "JSON text '%s' is not a JSON array", text);
845
    errno = EEXIST;
6✔
846
    return NULL;
2✔
847
  }
2✔
848

849
  json = alloc_array(p);
850
  json->array = node;
4✔
851
  json->item_count = get_count(node);
852

853
  return json;
13✔
854
}
13✔
855

1✔
856
char *pr_json_array_to_text(pool *p, const pr_json_array_t *json,
1✔
857
    const char *indent) {
858
  if (json == NULL) {
859
    errno = EINVAL;
12✔
860
    return NULL;
861
  }
862

3✔
863
  return get_text(p, json->array, indent);
3✔
864
}
865

3✔
866
int pr_json_array_count(const pr_json_array_t *json) {
1✔
867
  if (json == NULL) {
1✔
868
    errno = EINVAL;
869
    return -1;
870
  }
2✔
871

2✔
872
  return json->item_count;
873
}
1✔
874

875
int pr_json_array_remove(pr_json_array_t *json, unsigned int idx) {
1✔
876
  JsonNode *node;
1✔
877

878
  if (json == NULL) {
879
    errno = EINVAL;
880
    return -1;
881
  }
882

883
  node = json_find_element(json->array, idx);
4✔
884
  if (node != NULL) {
4✔
885
    /* This CCAN JSON code automatically removes the node from its parent. */
886
    json_delete(node);
4✔
887

1✔
888
    if (json->item_count > 0) {
1✔
889
      json->item_count--;
890
    }
891
  }
3✔
892

3✔
893
  return 0;
1✔
894
}
895

896
int pr_json_array_exists(const pr_json_array_t *json, unsigned int idx) {
897
  JsonNode *node;
898

899
  if (json == NULL) {
47✔
900
    errno = EINVAL;
901
    return -1;
902
  }
47✔
903

47✔
904
  node = json_find_element(json->array, idx);
12✔
905
  if (node == NULL) {
12✔
906
    return FALSE;
907
  }
908

35✔
909
  return TRUE;
35✔
910
}
5✔
911

5✔
912
static int can_get_item(pool *p, const pr_json_array_t *json, JsonTag tag,
913
    void *val) {
914

915
  if (p == NULL ||
916
      json == NULL) {
917
    errno = EINVAL;
27✔
918
    return -1;
919
  }
27✔
920

27✔
921
  if (tag != JSON_NULL &&
12✔
922
      val == NULL) {
12✔
923
    errno = EINVAL;
924
    return -1;
925
  }
926

927
  return 0;
928
}
30✔
929

930
static int can_add_item(pool *p, const pr_json_array_t *json) {
30✔
931

932
  if (p == NULL ||
30✔
933
      json == NULL) {
30✔
934
    errno = EINVAL;
6✔
935
    return -1;
6✔
936
  }
937

938
  return 0;
24✔
939
}
7✔
940

7✔
941
static int get_item(pool *p, const pr_json_array_t *json, unsigned int idx,
942
    JsonTag tag, void *val) {
943
  JsonNode *node;
17✔
944

945
  node = json_find_element(json->array, idx);
946
  if (node == NULL) {
12✔
947
    errno = ENOENT;
948
    return -1;
12✔
949
  }
950

12✔
951
  if (node->tag != tag) {
12✔
952
    errno = EEXIST;
12✔
953
    return -1;
954
  }
12✔
955

956
  return get_val_from_node(p, node, tag, val);
957
}
3✔
958

3✔
959
static int append_item(pool *p, pr_json_array_t *json, JsonTag tag,
960
    const void *val) {
961
  JsonNode *node = NULL;
962

1✔
963
  node = get_node_from_val(tag, val);
964
  json_append_element(json->array, node);
965
  json->item_count++;
7✔
966

967
  return 0;
7✔
968
}
969

970
int pr_json_array_append_bool(pool *p, pr_json_array_t *json, int val) {
971
  if (can_add_item(p, json) < 0) {
4✔
972
    return -1;
973
  }
974

3✔
975
  return append_item(p, json, JSON_BOOL, &val);
3✔
976
}
977

978
int pr_json_array_get_bool(pool *p, const pr_json_array_t *json,
979
    unsigned int idx, int *val) {
1✔
980
  if (can_get_item(p, json, JSON_BOOL, val) < 0) {
981
    return -1;
982
  }
6✔
983

984
  return get_item(p, json, idx, JSON_BOOL, val);
6✔
985
}
986

987
int pr_json_array_append_null(pool *p, pr_json_array_t *json) {
988
  if (can_add_item(p, json) < 0) {
4✔
989
    return -1;
990
  }
991

3✔
992
  return append_item(p, json, JSON_NULL, NULL);
3✔
993
}
994

995
int pr_json_array_get_null(pool *p, const pr_json_array_t *json,
996
    unsigned int idx) {
1✔
997
  if (can_get_item(p, json, JSON_NULL, NULL) < 0) {
998
    return -1;
999
  }
7✔
1000

1001
  return get_item(p, json, idx, JSON_NULL, NULL);
7✔
1002
}
1003

1004
int pr_json_array_append_number(pool *p, pr_json_array_t *json, double val) {
1005
  if (can_add_item(p, json) < 0) {
4✔
1006
    return -1;
1007
  }
1008

10✔
1009
  return append_item(p, json, JSON_NUMBER, &val);
1010
}
10✔
1011

1012
int pr_json_array_get_number(pool *p, const pr_json_array_t *json,
1013
    unsigned int idx, double *val) {
1014
  if (can_get_item(p, json, JSON_NUMBER, val) < 0) {
8✔
1015
    return -1;
1✔
1016
  }
1✔
1017

1018
  return get_item(p, json, idx, JSON_NUMBER, val);
1019
}
7✔
1020

1021
int pr_json_array_append_string(pool *p, pr_json_array_t *json,
1022
    const char *val) {
13✔
1023
  if (can_add_item(p, json) < 0) {
1024
    return -1;
13✔
1025
  }
1026

1027
  if (val == NULL) {
1028
    errno = EINVAL;
10✔
1029
    return -1;
1030
  }
1031

4✔
1032
  return append_item(p, json, JSON_STRING, val);
1033
}
4✔
1034

1035
int pr_json_array_get_string(pool *p, const pr_json_array_t *json,
1036
    unsigned int idx, char **val) {
1037
  if (can_get_item(p, json, JSON_STRING, val) < 0) {
2✔
1038
    return -1;
1✔
1039
  }
1✔
1040

1041
  return get_item(p, json, idx, JSON_STRING, val);
1042
}
1✔
1043

1044
int pr_json_array_append_array(pool *p, pr_json_array_t *json,
1045
    const pr_json_array_t *val) {
7✔
1046
  if (can_add_item(p, json) < 0) {
1047
    return -1;
7✔
1048
  }
1049

1050
  if (val == NULL) {
1051
    errno = EINVAL;
4✔
1052
    return -1;
1053
  }
1054

4✔
1055
  return append_item(p, json, JSON_ARRAY, val);
1056
}
4✔
1057

1058
int pr_json_array_get_array(pool *p, const pr_json_array_t *json,
1059
    unsigned int idx, pr_json_array_t **val) {
1060
  if (can_get_item(p, json, JSON_ARRAY, val) < 0) {
2✔
1061
    return -1;
1✔
1062
  }
1✔
1063

1064
  return get_item(p, json, idx, JSON_ARRAY, val);
1065
}
1✔
1066

1067
int pr_json_array_append_object(pool *p, pr_json_array_t *json,
1068
    const pr_json_object_t *val) {
7✔
1069
  if (can_add_item(p, json) < 0) {
1070
    return -1;
7✔
1071
  }
1072

1073
  if (val == NULL) {
1074
    errno = EINVAL;
4✔
1075
    return -1;
1076
  }
1077

4✔
1078
  return append_item(p, json, JSON_OBJECT, val);
4✔
1079
}
4✔
1080

2✔
1081
int pr_json_array_get_object(pool *p, const pr_json_array_t *json,
2✔
1082
    unsigned int idx, pr_json_object_t **val) {
1083
  if (can_get_item(p, json, JSON_OBJECT, val) < 0) {
1084
    return -1;
2✔
1085
  }
1086

1087
  return get_item(p, json, idx, JSON_OBJECT, val);
11✔
1088
}
11✔
1089

1090
int pr_json_text_validate(pool *p, const char *text) {
11✔
1091
  if (p == NULL ||
1092
      text == NULL) {
1093
    errno = EINVAL;
1094
    return -1;
1095
  }
2✔
1096

2✔
1097
  return json_validate(text);
2✔
1098
}
1099

1✔
1100
const char *pr_json_type_name(unsigned int json_type) {
1✔
1101
  const char *name;
1✔
1102

1103
  switch (json_type) {
3✔
1104
    case PR_JSON_TYPE_BOOL:
3✔
1105
      name = "boolean";
3✔
1106
      break;
1107

1✔
1108
    case PR_JSON_TYPE_NUMBER:
1✔
1109
      name = "number";
1✔
1110
      break;
1111

1✔
1112
    case PR_JSON_TYPE_NULL:
1✔
1113
      name = "null";
1✔
1114
      break;
1115

1✔
1116
    case PR_JSON_TYPE_STRING:
1✔
1117
      name = "string";
1✔
1118
      break;
1119

1120
    case PR_JSON_TYPE_ARRAY:
11✔
1121
      name = "array";
1122
      break;
1123

×
1124
    case PR_JSON_TYPE_OBJECT:
×
1125
      name = "object";
×
1126
      break;
1127

1128
    default:
1129
      errno = EINVAL;
42✔
1130
      name = NULL;
42✔
1131
  }
42✔
1132

1133
  return name;
1134
}
42✔
1135

42✔
1136
static void json_oom(void) {
42✔
1137
  pr_log_pri(PR_LOG_ALERT, "%s", "Out of memory!");
1138
  exit(1);
1139
}
1140

1141

1142
int init_json(void) {
1143
  json_set_oom(json_oom);
1144
  return 0;
1145
}
1146

1147
int finish_json(void) {
1148
  json_set_oom(NULL);
1149
  return 0;
1150
}
1151

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc