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

proftpd / proftpd / 14552756858

19 Apr 2025 08:53PM UTC coverage: 93.03% (+0.4%) from 92.667%
14552756858

push

github

51358 of 55206 relevant lines covered (93.03%)

213.67 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-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;
50✔
46
  pr_json_array_t *json;
50✔
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;
58✔
59
  pr_json_object_t *json;
58✔
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) {
57✔
71
  unsigned int count;
57✔
72
  JsonNode *node;
57✔
73

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

80
  return count;
57✔
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 ||
16✔
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;
12✔
111

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

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

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

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

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

133
    case JSON_OBJECT:
134
      type = PR_JSON_TYPE_OBJECT;
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;
27✔
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;
33✔
176
  pr_json_object_t *json;
33✔
177

178
  if (p == NULL ||
33✔
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;
4✔
228

229
  if (json == NULL ||
4✔
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;
5✔
250

251
  if (json == NULL ||
5✔
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;
2✔
260
  }
261

262
  return TRUE;
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 ||
70✔
269
      json == NULL ||
70✔
270
      key == NULL) {
271
    errno = EINVAL;
18✔
272
    return -1;
18✔
273
  }
274

275
  if (tag != JSON_NULL &&
52✔
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,
46✔
285
    const char *key) {
286

287
  if (p == NULL ||
46✔
288
      json == NULL ||
46✔
289
      key == NULL) {
290
    errno = EINVAL;
18✔
291
    return -1;
18✔
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 &&
50✔
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;
12✔
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);
12✔
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;
6✔
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);
6✔
379

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

384
    default:
385
      break;
386
  }
387

388
  return 0;
47✔
389
}
390

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

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

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

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

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

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

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

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

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

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

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

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

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

445
    default:
446
      break;
25✔
447
  }
448

25✔
449
  return node;
450
}
25✔
451

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

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

460
  return 0;
5✔
461
}
462

5✔
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) {
3✔
466
  JsonNode *iter;
3✔
467

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

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

480
    pr_signals_handle();
×
481

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

555
      default:
556
        break;
557
    }
558

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

4✔
565
  return 0;
566
}
567

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

690
/* JSON Arrays */
25✔
691

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

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

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

48✔
703
  return json;
48✔
704
}
705

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

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

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

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

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

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

×
736
    pr_signals_handle();
×
737

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

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

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

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

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

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

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

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

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

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

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

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

×
790
        valsz = 0;
×
791
        break;
×
792
      }
793

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

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

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

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

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

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

16✔
821
  return 0;
2✔
822
}
823

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

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

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

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

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

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

13✔
854
  return json;
13✔
855
}
1✔
856

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

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

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

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

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

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

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

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

1✔
894
  return 0;
895
}
896

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

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

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

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

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

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

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

928
  return 0;
30✔
929
}
930

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

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

24✔
939
  return 0;
7✔
940
}
7✔
941

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

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

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

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

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

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

7✔
968
  return 0;
969
}
970

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1134
  return name;
42✔
1135
}
42✔
1136

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

1142

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

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