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

proftpd / proftpd / 14504217694

16 Apr 2025 11:00PM UTC coverage: 92.667% (-0.4%) from 93.03%
14504217694

push

github

web-flow
Bug #3802: While looking into occurrences of `EADDRINUSE` when attempting to create local sockets for active data transfers, I did some refactoring to reduce the verbosity (and confusion) of the messages logged in such cases. (#1926)

16 of 24 new or added lines in 2 files covered. (66.67%)

260 existing lines in 25 files now uncovered.

47172 of 50905 relevant lines covered (92.67%)

235.32 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-2022 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

385
  return 0;
386
}
387

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

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

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

403
  return get_val_from_node(p, node, tag, val);
31✔
404
}
405

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

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

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

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

422
    case JSON_STRING:
13✔
423
      node = json_mkstring(val);
13✔
424
      break;
13✔
425

426
    case JSON_ARRAY: {
9✔
427
      const pr_json_array_t *array;
428

429
      array = val;
9✔
430
      node = array->array;
9✔
431
      break;
9✔
432
    }
433

434
    case JSON_OBJECT: {
2✔
435
      const pr_json_object_t *object;
436

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

443
  return node;
37✔
444
}
445

446
static int set_member(pool *p, pr_json_object_t *json, const char *key,
447
    JsonTag tag, const void *val) {
448
  JsonNode *node = NULL;
25✔
449

450
  node = get_node_from_val(tag, val);
25✔
451
  json_append_member(json->object, key, node);
25✔
452
  json->member_count++;
25✔
453

454
  return 0;
455
}
456

457
int pr_json_object_foreach(pool *p, const pr_json_object_t *json,
5✔
458
    int (*cb)(const char *key, int val_type, const void *val, size_t valsz,
459
    void *cb_data), void *user_data) {
460
  JsonNode *iter;
461

462
  if (p == NULL ||
10✔
463
      json == NULL ||
8✔
464
      cb == NULL) {
465
    errno = EINVAL;
3✔
466
    return -1;
3✔
467
  }
468

469
  for (iter = json_first_child(json->object); iter != NULL; iter = iter->next) {
5✔
470
    int res, val_type, xerrno;
471
    const void *val = NULL;
4✔
472
    size_t valsz = 0;
4✔
473

474
    pr_signals_handle();
4✔
475

476
    val_type = get_type(iter);
4✔
477
    if (val_type < 0) {
4✔
478
      xerrno = errno;
×
479

480
      pr_trace_msg(trace_channel, 9, "unknown value type %d in object",
×
481
        (int) iter->tag);
×
482

483
      errno = xerrno;
×
484
      return -1;
×
485
    }
486

487
    switch (val_type) {
4✔
488
      case PR_JSON_TYPE_BOOL:
3✔
489
        val = &(iter->bool_);
3✔
490
        valsz = sizeof(iter->bool_);
3✔
491
        break;
3✔
492

493
      case PR_JSON_TYPE_NUMBER:
×
494
        val = &(iter->number_);
×
495
        valsz = sizeof(iter->number_);
×
496
        break;
×
497

498
      case PR_JSON_TYPE_NULL:
499
        val = NULL;
500
        valsz = 0;
501
        break;
502

503
      case PR_JSON_TYPE_STRING:
×
504
        val = iter->string_;
×
505
        valsz = strlen(iter->string_);
×
506
        break;
×
507

508
      case PR_JSON_TYPE_ARRAY: {
×
509
        pr_json_array_t *array;
510

511
        (void) get_val_from_node(p, iter, JSON_ARRAY, &array);
×
512
        if (array != NULL) {
×
513
          val = array;
×
514
        }
515

516
        valsz = 0;
×
517
        break;
518
      }
519

520
      case PR_JSON_TYPE_OBJECT: {
1✔
521
        pr_json_object_t *object = NULL;
1✔
522

523
        (void) get_val_from_node(p, iter, JSON_OBJECT, &object);
1✔
524
        if (object != NULL) {
1✔
525
          val = object;
1✔
526
        }
527

528
        valsz = 0;
1✔
529
        break;
530
      }
531
    }
532

533
    res = (cb)(iter->key, val_type, val, valsz, user_data);
4✔
534
    xerrno = errno;
4✔
535

536
    switch (val_type) {
4✔
537
      case PR_JSON_TYPE_ARRAY:
×
538
        pr_json_array_free((pr_json_array_t *) val);
×
539
        break;
×
540

541
      case PR_JSON_TYPE_OBJECT:
1✔
542
        pr_json_object_free((pr_json_object_t *) val);
1✔
543
        break;
1✔
544

545
      default:
546
        break;
547
    }
548

549
    if (res < 0) {
4✔
550
      errno = xerrno;
1✔
551
      return -1;
1✔
552
    }
553
  }
554

555
  return 0;
556
}
557

558
int pr_json_object_get_bool(pool *p, const pr_json_object_t *json,
8✔
559
    const char *key, int *val) {
560
  if (can_get_member(p, json, key, JSON_BOOL, val) < 0) {
8✔
561
    return -1;
562
  }
563

564
  return get_member(p, json, key, JSON_BOOL, val);
4✔
565
}
566

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

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

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

582
  return get_member(p, json, key, JSON_NULL, NULL);
4✔
583
}
584

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

590
  return set_member(p, json, key, JSON_NULL, NULL);
2✔
591
}
592

593
int pr_json_object_get_number(pool *p, const pr_json_object_t *json,
12✔
594
    const char *key, double *val) {
595
  if (can_get_member(p, json, key, JSON_NUMBER, val) < 0) {
12✔
596
    return -1;
597
  }
598

599
  return get_member(p, json, key, JSON_NUMBER, val);
8✔
600
}
601

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

608
  return set_member(p, json, key, JSON_NUMBER, &val);
14✔
609
}
610

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

617
  return get_member(p, json, key, JSON_STRING, val);
13✔
618
}
619

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

626
  if (val == NULL) {
7✔
627
    errno = EINVAL;
1✔
628
    return -1;
1✔
629
  }
630

631
  return set_member(p, json, key, JSON_STRING, val);
12✔
632
}
633

634
int pr_json_object_get_array(pool *p, const pr_json_object_t *json,
18✔
635
    const char *key, pr_json_array_t **val) {
636
  if (can_get_member(p, json, key, JSON_ARRAY, val) < 0) {
18✔
637
    return -1;
638
  }
639

640
  return get_member(p, json, key, JSON_ARRAY, val);
14✔
641
}
642

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

649
  if (val == NULL) {
9✔
650
    errno = EINVAL;
1✔
651
    return -1;
1✔
652
  }
653

654
  return set_member(p, json, key, JSON_ARRAY, val);
16✔
655
}
656

657
int pr_json_object_get_object(pool *p, const pr_json_object_t *json,
8✔
658
    const char *key, pr_json_object_t **val) {
659
  if (can_get_member(p, json, key, JSON_OBJECT, val) < 0) {
8✔
660
    return -1;
661
  }
662

663
  return get_member(p, json, key, JSON_OBJECT, val);
4✔
664
}
665

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

672
  if (val == NULL) {
2✔
673
    errno = EINVAL;
1✔
674
    return -1;
1✔
675
  }
676

677
  return set_member(p, json, key, JSON_OBJECT, val);
2✔
678
}
679

680
/* JSON Arrays */
681

682
pr_json_array_t *pr_json_array_alloc(pool *p) {
26✔
683
  pr_json_array_t *json;
684

685
  if (p == NULL) {
26✔
686
    errno = EINVAL;
1✔
687
    return NULL;
1✔
688
  }
689

690
  json = alloc_array(p);
25✔
691
  json->array = json_mkarray();
25✔
692

693
  return json;
25✔
694
}
695

696
int pr_json_array_free(pr_json_array_t *json) {
49✔
697
  if (json == NULL) {
49✔
698
    errno = EINVAL;
1✔
699
    return -1;
1✔
700
  }
701

702
  json_delete(json->array);
48✔
703
  json->array = NULL;
48✔
704

705
  destroy_pool(json->pool);
48✔
706
  return 0;
48✔
707
}
708

709
int pr_json_array_foreach(pool *p, const pr_json_array_t *json,
5✔
710
    int (*cb)(int val_type, const void *val, size_t valsz, void *cb_data),
711
    void *user_data) {
712
  JsonNode *iter;
713

714
  if (p == NULL ||
10✔
715
      json == NULL ||
8✔
716
      cb == NULL) {
717
    errno = EINVAL;
3✔
718
    return -1;
3✔
719
  }
720

721
  for (iter = json_first_child(json->array); iter != NULL; iter = iter->next) {
9✔
722
    int res, val_type, xerrno;
723
    const void *val = NULL;
8✔
724
    size_t valsz = 0;
8✔
725

726
    pr_signals_handle();
8✔
727

728
    val_type = get_type(iter);
8✔
729
    if (val_type < 0) {
8✔
730
      xerrno = errno;
×
731

732
      pr_trace_msg(trace_channel, 9, "unknown value type %d in array",
×
733
        (int) iter->tag);
×
734

735
      errno = xerrno;
×
736
      return -1;
×
737
    }
738

739
    switch (val_type) {
8✔
740
      case PR_JSON_TYPE_BOOL:
2✔
741
        val = &(iter->bool_);
2✔
742
        valsz = sizeof(iter->bool_);
2✔
743
        break;
2✔
744

745
      case PR_JSON_TYPE_NUMBER:
1✔
746
        val = &(iter->number_);
1✔
747
        valsz = sizeof(iter->number_);
1✔
748
        break;
1✔
749

750
      case PR_JSON_TYPE_NULL:
751
        val = NULL;
752
        valsz = 0;
753
        break;
754

755
      case PR_JSON_TYPE_STRING:
4✔
756
        val = iter->string_;
4✔
757
        valsz = strlen(iter->string_);
4✔
758
        break;
4✔
759

760
      case PR_JSON_TYPE_ARRAY: {
×
761
        pr_json_array_t *array;
762

763
        (void) get_val_from_node(p, iter, JSON_ARRAY, &array);
×
764
        if (array != NULL) {
×
765
          val = array;
×
766
        }
767

768
        valsz = 0;
×
769
        break;
770
      }
771

772
      case PR_JSON_TYPE_OBJECT: {
1✔
773
        pr_json_object_t *object = NULL;
1✔
774

775
        (void) get_val_from_node(p, iter, JSON_OBJECT, &object);
1✔
776
        if (object != NULL) {
1✔
777
          val = object;
1✔
778
        }
779

780
        valsz = 0;
1✔
781
        break;
782
      }
783
    }
784

785
    res = (cb)(val_type, val, valsz, user_data);
8✔
786
    xerrno = errno;
8✔
787

788
    switch (val_type) {
8✔
789
      case PR_JSON_TYPE_ARRAY:
×
790
        pr_json_array_free((pr_json_array_t *) val);
×
791
        break;
×
792

793
      case PR_JSON_TYPE_OBJECT:
1✔
794
        pr_json_object_free((pr_json_object_t *) val);
1✔
795
        break;
1✔
796

797
      default:
798
        break;
799
    }
800

801
    if (res < 0) {
8✔
802
      errno = xerrno;
1✔
803
      return -1;
1✔
804
    }
805
  }
806

807
  return 0;
808
}
809

810
pr_json_array_t *pr_json_array_from_text(pool *p, const char *text) {
18✔
811
  JsonNode *node;
812
  pr_json_array_t *json;
813

814
  if (p == NULL ||
36✔
815
      text == NULL) {
18✔
816
    errno = EINVAL;
2✔
817
    return NULL;
2✔
818
  }
819

820
  if (json_validate(text) == FALSE) {
16✔
821
    pr_trace_msg(trace_channel, 9, "unable to parse invalid JSON text '%s'",
2✔
822
      text);
823
    errno = EPERM;
2✔
824
    return NULL;
2✔
825
  }
826

827
  node = json_decode(text);
14✔
828
  if (node->tag != JSON_ARRAY) {
14✔
829
    json_delete(node);
1✔
830

831
    pr_trace_msg(trace_channel, 9, "JSON text '%s' is not a JSON array", text);
1✔
832
    errno = EEXIST;
1✔
833
    return NULL;
1✔
834
  }
835

836
  json = alloc_array(p);
13✔
837
  json->array = node;
13✔
838
  json->item_count = get_count(node);
13✔
839

840
  return json;
13✔
841
}
842

843
char *pr_json_array_to_text(pool *p, const pr_json_array_t *json,
6✔
844
    const char *indent) {
845
  if (json == NULL) {
6✔
846
    errno = EINVAL;
2✔
847
    return NULL;
2✔
848
  }
849

850
  return get_text(p, json->array, indent);
4✔
851
}
852

853
int pr_json_array_count(const pr_json_array_t *json) {
13✔
854
  if (json == NULL) {
13✔
855
    errno = EINVAL;
1✔
856
    return -1;
1✔
857
  }
858

859
  return json->item_count;
12✔
860
}
861

862
int pr_json_array_remove(pr_json_array_t *json, unsigned int idx) {
3✔
863
  JsonNode *node;
864

865
  if (json == NULL) {
3✔
866
    errno = EINVAL;
1✔
867
    return -1;
1✔
868
  }
869

870
  node = json_find_element(json->array, idx);
2✔
871
  if (node != NULL) {
2✔
872
    /* This CCAN JSON code automatically removes the node from its parent. */
873
    json_delete(node);
1✔
874

875
    if (json->item_count > 0) {
1✔
876
      json->item_count--;
1✔
877
    }
878
  }
879

880
  return 0;
881
}
882

883
int pr_json_array_exists(const pr_json_array_t *json, unsigned int idx) {
4✔
884
  JsonNode *node;
885

886
  if (json == NULL) {
4✔
887
    errno = EINVAL;
1✔
888
    return -1;
1✔
889
  }
890

891
  node = json_find_element(json->array, idx);
3✔
892
  if (node == NULL) {
3✔
893
    return FALSE;
894
  }
895

896
  return TRUE;
2✔
897
}
898

899
static int can_get_item(pool *p, const pr_json_array_t *json, JsonTag tag,
47✔
900
    void *val) {
901

902
  if (p == NULL ||
94✔
903
      json == NULL) {
47✔
904
    errno = EINVAL;
12✔
905
    return -1;
12✔
906
  }
907

908
  if (tag != JSON_NULL &&
70✔
909
      val == NULL) {
35✔
910
    errno = EINVAL;
5✔
911
    return -1;
5✔
912
  }
913

914
  return 0;
915
}
916

917
static int can_add_item(pool *p, const pr_json_array_t *json) {
918

919
  if (p == NULL ||
54✔
920
      json == NULL) {
27✔
921
    errno = EINVAL;
12✔
922
    return -1;
923
  }
924

925
  return 0;
926
}
927

928
static int get_item(pool *p, const pr_json_array_t *json, unsigned int idx,
30✔
929
    JsonTag tag, void *val) {
930
  JsonNode *node;
931

932
  node = json_find_element(json->array, idx);
30✔
933
  if (node == NULL) {
30✔
934
    errno = ENOENT;
6✔
935
    return -1;
936
  }
937

938
  if (node->tag != tag) {
24✔
939
    errno = EEXIST;
7✔
940
    return -1;
941
  }
942

943
  return get_val_from_node(p, node, tag, val);
17✔
944
}
945

946
static int append_item(pool *p, pr_json_array_t *json, JsonTag tag,
947
    const void *val) {
948
  JsonNode *node = NULL;
12✔
949

950
  node = get_node_from_val(tag, val);
12✔
951
  json_append_element(json->array, node);
12✔
952
  json->item_count++;
12✔
953

954
  return 0;
955
}
956

957
int pr_json_array_append_bool(pool *p, pr_json_array_t *json, int val) {
3✔
958
  if (can_add_item(p, json) < 0) {
3✔
959
    return -1;
960
  }
961

962
  return append_item(p, json, JSON_BOOL, &val);
2✔
963
}
964

965
int pr_json_array_get_bool(pool *p, const pr_json_array_t *json,
7✔
966
    unsigned int idx, int *val) {
967
  if (can_get_item(p, json, JSON_BOOL, val) < 0) {
7✔
968
    return -1;
969
  }
970

971
  return get_item(p, json, idx, JSON_BOOL, val);
4✔
972
}
973

974
int pr_json_array_append_null(pool *p, pr_json_array_t *json) {
3✔
975
  if (can_add_item(p, json) < 0) {
3✔
976
    return -1;
977
  }
978

979
  return append_item(p, json, JSON_NULL, NULL);
2✔
980
}
981

982
int pr_json_array_get_null(pool *p, const pr_json_array_t *json,
6✔
983
    unsigned int idx) {
984
  if (can_get_item(p, json, JSON_NULL, NULL) < 0) {
6✔
985
    return -1;
986
  }
987

988
  return get_item(p, json, idx, JSON_NULL, NULL);
4✔
989
}
990

991
int pr_json_array_append_number(pool *p, pr_json_array_t *json, double val) {
3✔
992
  if (can_add_item(p, json) < 0) {
3✔
993
    return -1;
994
  }
995

996
  return append_item(p, json, JSON_NUMBER, &val);
2✔
997
}
998

999
int pr_json_array_get_number(pool *p, const pr_json_array_t *json,
7✔
1000
    unsigned int idx, double *val) {
1001
  if (can_get_item(p, json, JSON_NUMBER, val) < 0) {
7✔
1002
    return -1;
1003
  }
1004

1005
  return get_item(p, json, idx, JSON_NUMBER, val);
4✔
1006
}
1007

1008
int pr_json_array_append_string(pool *p, pr_json_array_t *json,
10✔
1009
    const char *val) {
1010
  if (can_add_item(p, json) < 0) {
10✔
1011
    return -1;
1012
  }
1013

1014
  if (val == NULL) {
8✔
1015
    errno = EINVAL;
1✔
1016
    return -1;
1✔
1017
  }
1018

1019
  return append_item(p, json, JSON_STRING, val);
14✔
1020
}
1021

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

1028
  return get_item(p, json, idx, JSON_STRING, val);
10✔
1029
}
1030

1031
int pr_json_array_append_array(pool *p, pr_json_array_t *json,
4✔
1032
    const pr_json_array_t *val) {
1033
  if (can_add_item(p, json) < 0) {
4✔
1034
    return -1;
1035
  }
1036

1037
  if (val == NULL) {
2✔
1038
    errno = EINVAL;
1✔
1039
    return -1;
1✔
1040
  }
1041

1042
  return append_item(p, json, JSON_ARRAY, val);
2✔
1043
}
1044

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

1051
  return get_item(p, json, idx, JSON_ARRAY, val);
4✔
1052
}
1053

1054
int pr_json_array_append_object(pool *p, pr_json_array_t *json,
4✔
1055
    const pr_json_object_t *val) {
1056
  if (can_add_item(p, json) < 0) {
4✔
1057
    return -1;
1058
  }
1059

1060
  if (val == NULL) {
2✔
1061
    errno = EINVAL;
1✔
1062
    return -1;
1✔
1063
  }
1064

1065
  return append_item(p, json, JSON_OBJECT, val);
2✔
1066
}
1067

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

1074
  return get_item(p, json, idx, JSON_OBJECT, val);
4✔
1075
}
1076

1077
int pr_json_text_validate(pool *p, const char *text) {
4✔
1078
  if (p == NULL ||
8✔
1079
      text == NULL) {
4✔
1080
    errno = EINVAL;
2✔
1081
    return -1;
2✔
1082
  }
1083

1084
  return json_validate(text);
2✔
1085
}
1086

1087
const char *pr_json_type_name(unsigned int json_type) {
11✔
1088
  const char *name;
1089

1090
  switch (json_type) {
11✔
1091
    case PR_JSON_TYPE_BOOL:
1092
      name = "boolean";
1093
      break;
1094

1095
    case PR_JSON_TYPE_NUMBER:
2✔
1096
      name = "number";
2✔
1097
      break;
2✔
1098

1099
    case PR_JSON_TYPE_NULL:
1✔
1100
      name = "null";
1✔
1101
      break;
1✔
1102

1103
    case PR_JSON_TYPE_STRING:
3✔
1104
      name = "string";
3✔
1105
      break;
3✔
1106

1107
    case PR_JSON_TYPE_ARRAY:
1✔
1108
      name = "array";
1✔
1109
      break;
1✔
1110

1111
    case PR_JSON_TYPE_OBJECT:
1✔
1112
      name = "object";
1✔
1113
      break;
1✔
1114

1115
    default:
1✔
1116
      errno = EINVAL;
1✔
1117
      name = NULL;
1✔
1118
  }
1119

1120
  return name;
11✔
1121
}
1122

1123
static void json_oom(void) {
×
1124
  pr_log_pri(PR_LOG_ALERT, "%s", "Out of memory!");
×
1125
  exit(1);
×
1126
}
1127

1128

1129
int init_json(void) {
42✔
1130
  json_set_oom(json_oom);
42✔
1131
  return 0;
42✔
1132
}
1133

1134
int finish_json(void) {
42✔
1135
  json_set_oom(NULL);
42✔
1136
  return 0;
42✔
1137
}
1138

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