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

proftpd / proftpd / 21220316014

21 Jan 2026 06:02PM UTC coverage: 92.638% (-0.4%) from 93.026%
21220316014

push

github

web-flow
Issue #2020: Guard against inadvertently accessing bytes before the allocated memory range when processing FTP control connection characters. 

In certain conditions, we might use a -1 index, thus leading to a single byte read of out-of-bounds memory.

3 of 3 new or added lines in 1 file covered. (100.0%)

898 existing lines in 28 files now uncovered.

47238 of 50992 relevant lines covered (92.64%)

237.04 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-2025 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, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18
 *
19
 * As a special exemption, The ProFTPD Project team and other respective
20
 * copyright holders give permission to link this program with OpenSSL, and
21
 * distribute the resulting executable, without including the source code for
22
 * OpenSSL in the source distribution.
23
 */
24

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

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

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

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

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

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

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

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

54
  return json;
50✔
55
}
56

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

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

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

67
  return json;
58✔
68
}
69

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

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

80
  return count;
81
}
82

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

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

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

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

106
  return text;
107
}
108

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

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

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

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

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

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

133
    case JSON_OBJECT:
2✔
134
      type = PR_JSON_TYPE_OBJECT;
2✔
135
      break;
136

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

142
  return type;
143
}
144

145
/* JSON Objects */
146

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

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

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

158
  return json;
26✔
159
}
160

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

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

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

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

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

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

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

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

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

204
  return json;
26✔
205
}
206

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

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

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

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

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

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

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

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

245
  return 0;
246
}
247

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

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

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

262
  return TRUE;
1✔
263
}
264

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

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

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

281
  return 0;
282
}
283

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

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

294
  return 0;
295
}
296

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

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

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

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

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

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

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

331
      array = alloc_array(p);
12✔
332

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

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

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

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

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

359
      object = alloc_object(p);
6✔
360

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

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

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

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

384
    default:
385
      break;
386
  }
387

388
  return 0;
389
}
390

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

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

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

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

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

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

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

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

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

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

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

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

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

445
    default:
446
      break;
447
  }
448

449
  return node;
37✔
450
}
451

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

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

460
  return 0;
461
}
462

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

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

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

480
    pr_signals_handle();
4✔
481

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

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

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

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

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

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

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

UNCOV
514
      case PR_JSON_TYPE_ARRAY: {
×
515
        pr_json_array_t *array;
516

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

UNCOV
522
        valsz = 0;
×
523
        break;
524
      }
525

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

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

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

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

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

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

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

555
      default:
556
        break;
557
    }
558

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

565
  return 0;
566
}
567

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

690
/* JSON Arrays */
691

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

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

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

703
  return json;
25✔
704
}
705

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

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

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

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

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

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

736
    pr_signals_handle();
8✔
737

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

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

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

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

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

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

765
      case PR_JSON_TYPE_STRING:
4✔
766
        val = iter->string_;
4✔
767
        valsz = strlen(iter->string_);
4✔
768
        break;
4✔
769

UNCOV
770
      case PR_JSON_TYPE_ARRAY: {
×
771
        pr_json_array_t *array;
772

UNCOV
773
        (void) get_val_from_node(p, iter, JSON_ARRAY, &array);
×
UNCOV
774
        if (array != NULL) {
×
UNCOV
775
          val = array;
×
776
        }
777

UNCOV
778
        valsz = 0;
×
779
        break;
780
      }
781

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

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

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

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

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

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

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

811
      default:
812
        break;
813
    }
814

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

821
  return 0;
822
}
823

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

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

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

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

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

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

854
  return json;
13✔
855
}
856

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

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

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

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

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

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

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

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

894
  return 0;
895
}
896

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

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

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

910
  return TRUE;
2✔
911
}
912

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

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

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

928
  return 0;
929
}
930

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

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

939
  return 0;
940
}
941

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

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

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

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

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

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

968
  return 0;
969
}
970

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1134
  return name;
11✔
1135
}
1136

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

1142

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

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

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