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

proftpd / proftpd / 26127302554

19 May 2026 07:34PM UTC coverage: 92.635% (-0.4%) from 93.024%
26127302554

push

github

Castaglia
Make a flaky, racy mod_copy regression test more reliable.

47303 of 51064 relevant lines covered (92.63%)

241.07 hits per line

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

91.4
/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) {
50✔
44
  pool *sub_pool;
45
  pr_json_array_t *json;
46

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

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

53
  return json;
50✔
54
}
55

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

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

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

66
  return json;
58✔
67
}
68

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

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

79
  return count;
80
}
81

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

85
  if (p == NULL ||
32✔
86
      indent == NULL) {
16✔
87
    errno = EINVAL;
2✔
88
    return NULL;
2✔
89
  }
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') {
14✔
96
    indent = NULL;
14✔
97
  }
98

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

105
  return text;
106
}
107

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

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

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

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

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

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

132
    case JSON_OBJECT:
2✔
133
      type = PR_JSON_TYPE_OBJECT;
2✔
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) {
27✔
147
  pr_json_object_t *json;
148

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

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

157
  return json;
26✔
158
}
159

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

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

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

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

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

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

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

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

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

203
  return json;
26✔
204
}
205

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

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

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

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

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

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

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

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

244
  return 0;
245
}
246

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

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

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

261
  return TRUE;
1✔
262
}
263

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

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

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

280
  return 0;
281
}
282

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

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

293
  return 0;
294
}
295

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

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

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

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

313
    case JSON_STRING:
17✔
314
      /* Fortunately, valid JSON does not allow an empty element, or
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_);
17✔
321
      break;
17✔
322

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

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

330
      array = alloc_array(p);
12✔
331

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) {
12✔
340
        char *encoded_str = NULL;
11✔
341

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

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

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

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

358
      object = alloc_object(p);
6✔
359

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) {
6✔
368
        char *encoded_str = NULL;
3✔
369

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

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

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

383
    default:
384
      break;
385
  }
386

387
  return 0;
388
}
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;
393

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

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

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

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

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

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

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

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

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

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

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

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

444
    default:
445
      break;
446
  }
447

448
  return node;
37✔
449
}
450

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

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

459
  return 0;
460
}
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,
464
    void *cb_data), void *user_data) {
465
  JsonNode *iter;
466

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

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

479
    pr_signals_handle();
4✔
480

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

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

488
      errno = xerrno;
×
489
      return -1;
×
490
    }
491

492
    switch (val_type) {
4✔
493
      case PR_JSON_TYPE_BOOL:
3✔
494
        val = &(iter->bool_);
3✔
495
        valsz = sizeof(iter->bool_);
3✔
496
        break;
3✔
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

521
        valsz = 0;
×
522
        break;
523
      }
524

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

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

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

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

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

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

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

554
      default:
555
        break;
556
    }
557

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

564
  return 0;
565
}
566

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

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

576
int pr_json_object_set_bool(pool *p, pr_json_object_t *json, const char *key,
5✔
577
    int val) {
578
  if (can_set_member(p, json, key) < 0) {
5✔
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,
7✔
586
    const char *key) {
587
  if (can_get_member(p, json, key, JSON_NULL, NULL) < 0) {
7✔
588
    return -1;
589
  }
590

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

689
/* JSON Arrays */
690

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

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

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

702
  return json;
25✔
703
}
704

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

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

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

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

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

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

735
    pr_signals_handle();
8✔
736

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

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

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

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

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

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

764
      case PR_JSON_TYPE_STRING:
4✔
765
        val = iter->string_;
4✔
766
        valsz = strlen(iter->string_);
4✔
767
        break;
4✔
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);
×
773
        if (array != NULL) {
×
774
          val = array;
×
775
        }
776

777
        valsz = 0;
×
778
        break;
779
      }
780

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

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

789
        valsz = 0;
1✔
790
        break;
791
      }
792

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

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

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

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

810
      default:
811
        break;
812
    }
813

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

820
  return 0;
821
}
822

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

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

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

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

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

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

853
  return json;
13✔
854
}
855

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

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

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

872
  return json->item_count;
12✔
873
}
874

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

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

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

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

893
  return 0;
894
}
895

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

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

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

909
  return TRUE;
2✔
910
}
911

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

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

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

927
  return 0;
928
}
929

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

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

938
  return 0;
939
}
940

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

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

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

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

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

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

967
  return 0;
968
}
969

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1133
  return name;
11✔
1134
}
1135

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

1141

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

1147
int finish_json(void) {
42✔
1148
  json_set_oom(NULL);
42✔
1149
  return 0;
42✔
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