Coverage Report

Created: 2026-06-30 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/json/json.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or (at
5
 *   your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: 1c0db6c5857b81925cd5cc19da95f7f652bb4d4b $
19
 * @file json.c
20
 * @brief Common functions for working with json-c
21
 *
22
 * @author Arran Cudbard-Bell
23
 * @author Matthew Newton
24
 *
25
 * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
26
 * @copyright 2015,2020 Network RADIUS SAS (legal@networkradius.com)
27
 * @copyright 2015 The FreeRADIUS Server Project
28
 */
29
#include <freeradius-devel/util/debug.h>
30
#include <freeradius-devel/util/base16.h>
31
#include <freeradius-devel/util/base64.h>
32
#include <freeradius-devel/util/types.h>
33
#include <freeradius-devel/util/value.h>
34
#include "base.h"
35
#include "lib/util/strerror.h"
36
37
fr_table_num_sorted_t const fr_json_format_table[] = {
38
  { L("array"),   JSON_MODE_ARRAY   },
39
  { L("array_of_names"),  JSON_MODE_ARRAY_OF_NAMES  },
40
  { L("array_of_values"), JSON_MODE_ARRAY_OF_VALUES },
41
  { L("object"),    JSON_MODE_OBJECT    },
42
  { L("object_simple"), JSON_MODE_OBJECT_SIMPLE },
43
};
44
size_t fr_json_format_table_len = NUM_ELEMENTS(fr_json_format_table);
45
46
fr_table_num_sorted_t const fr_json_binary_format_table[] = {
47
  { L("base16"),  JSON_BINARY_FORMAT_BASE16 },
48
  { L("base64"),  JSON_BINARY_FORMAT_BASE64 },
49
  { L("raw"), JSON_BINARY_FORMAT_RAW },
50
};
51
size_t fr_json_binary_format_table_len = NUM_ELEMENTS(fr_json_binary_format_table);
52
53
static fr_json_format_t const default_json_format = {
54
  .attr = { .prefix = NULL },
55
  .value = { .value_is_always_array = true },
56
  .output_mode = JSON_MODE_OBJECT
57
};
58
59
static conf_parser_t const json_format_attr_config[] = {
60
  { FR_CONF_OFFSET("prefix", fr_json_format_attr_t, prefix) },
61
  CONF_PARSER_TERMINATOR
62
};
63
64
static conf_parser_t const json_format_value_config[] = {
65
  { FR_CONF_OFFSET("single_value_as_array", fr_json_format_value_t, value_is_always_array), .dflt = "no" },
66
  { FR_CONF_OFFSET("enum_as_integer", fr_json_format_value_t, enum_as_int), .dflt = "no" },
67
  { FR_CONF_OFFSET("always_string", fr_json_format_value_t, always_string), .dflt = "no" },
68
  { FR_CONF_OFFSET("binary_format", fr_json_format_value_t, binary_format), .dflt = "raw",
69
    .func = cf_table_parse_int,
70
    .uctx = &(cf_table_parse_ctx_t){ .table = fr_json_binary_format_table, .len = &fr_json_binary_format_table_len } },
71
  CONF_PARSER_TERMINATOR
72
};
73
74
conf_parser_t const fr_json_format_config[] = {
75
  { FR_CONF_OFFSET("output_mode", fr_json_format_t, output_mode_str), .dflt = "object" },
76
  { FR_CONF_OFFSET_SUBSECTION("attribute", 0, fr_json_format_t, attr, json_format_attr_config) },
77
  { FR_CONF_OFFSET_SUBSECTION("value", 0, fr_json_format_t, value, json_format_value_config) },
78
79
  CONF_PARSER_TERMINATOR
80
};
81
82
static inline CC_HINT(always_inline)
83
void json_object_put_assert(json_object *obj)
84
0
{
85
0
  int ret;
86
87
0
  ret = json_object_put(obj);
88
0
  if (ret == 1) return;
89
90
0
  fr_assert_fail("json_object_put did not free object (returned %d), likely leaking memory", ret);
91
0
}
92
93
/** Convert json object to fr_value_box_t
94
 *
95
 * @param[in] ctx to allocate any value buffers in (should usually be the same as out).
96
 * @param[in] out Where to write value.  Must be initialised.
97
 * @param[in] object  to convert.
98
 * @param[in] enumv Any string values are assumed to be in PRESENTATION format, meaning
99
 *      that if an enumv is specified, they'll be checked against the list
100
 *      of aliases for that enumeration, and possibly converted into one of
101
 *      the enumeration values (which may not be a string).
102
 * @param[in] tainted Whether the data source is untrusted.
103
 * @return
104
 *  - 0 on success.
105
 *  - -1 on failure.
106
 */
107
int fr_json_object_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, json_object *object,
108
        fr_dict_attr_t const *enumv, bool tainted)
109
0
{
110
0
  switch (json_object_get_type(object)) {
111
0
  case json_type_string:
112
0
  {
113
0
    char const      *value;
114
0
    size_t        len;
115
0
    fr_dict_enum_value_t const  *found;
116
117
0
    value = json_object_get_string(object);
118
0
    len = json_object_get_string_len(object);
119
120
0
    if (!enumv) goto no_enumv;
121
122
0
    if (fr_dict_valid_name(value, len) < 0) goto no_enumv;
123
124
    /*
125
     *  If an alias exists, use that value instead
126
     */
127
0
    found = fr_dict_enum_by_name(enumv, value, len);
128
0
    if (found) {
129
0
      if (unlikely(fr_value_box_copy(ctx, out, found->value)) < 0) return -1;
130
0
      return 0;
131
0
    }
132
133
0
  no_enumv:
134
    /*
135
     *  Just copy the string to the box.
136
     */
137
0
    if (fr_value_box_bstrndup(out, out, NULL, value, len, tainted) < 0) return -1;
138
0
  }
139
0
    break;
140
141
0
  case json_type_double:
142
0
    fr_value_box(out, json_object_get_double(object), tainted);
143
0
    break;
144
145
0
  case json_type_int:
146
0
  {
147
0
#ifdef HAVE_JSON_OBJECT_GET_INT64
148
0
    int64_t num;
149
#else
150
    int32_t num;
151
#endif
152
153
#ifndef HAVE_JSON_OBJECT_GET_INT64
154
    num = json_object_get_int(object);
155
#else
156
0
    num = json_object_get_int64(object);
157
0
    if (num < INT32_MIN) {     /* 64bit signed*/
158
0
      fr_value_box(out, (int64_t)num, tainted);
159
0
    } else if (num > UINT32_MAX) {   /* 64bit unsigned */
160
0
      fr_value_box(out, (uint64_t)num, tainted);
161
0
    } else
162
0
#endif
163
0
    if (num < INT16_MIN) {     /* 32bit signed */
164
0
      fr_value_box(out, (int32_t)num, tainted);
165
0
    } else if (num < INT8_MIN) {   /* 16bit signed */
166
0
      fr_value_box(out, (int16_t)num, tainted);
167
0
    } else if (num < 0) {     /* 8bit signed */
168
0
      fr_value_box(out, (int8_t)num, tainted);
169
0
    } else if (num > UINT16_MAX) {   /* 32bit unsigned */
170
0
      fr_value_box(out, (uint32_t)num, tainted);
171
0
    } else if (num > UINT8_MAX) {   /* 16bit unsigned */
172
0
      fr_value_box(out, (uint16_t)num, tainted);
173
0
    } else {       /* 8bit unsigned */
174
0
      fr_value_box(out, (uint8_t)num, tainted);
175
0
    }
176
0
  }
177
0
    break;
178
179
0
  case json_type_boolean:
180
    /* Must be cast to bool for correct generic case selection */
181
0
    fr_value_box(out, ((bool)(json_object_get_boolean(object) > 0)), tainted);
182
0
    break;
183
184
0
  case json_type_null:
185
0
  case json_type_array:
186
0
  case json_type_object:
187
0
  {
188
0
    char const *value = json_object_to_json_string(object);
189
190
0
    if (fr_value_box_bstrndup(out, out, NULL, value, strlen(value), tainted) < 0) return -1;
191
0
  }
192
0
    break;
193
0
  }
194
195
0
  out->tainted = tainted;
196
197
0
  return 0;
198
0
}
199
200
/** Convert boxed value_box to a JSON object
201
 *
202
 * @param[in] data  to convert.
203
 */
204
json_object *json_object_from_value_box(fr_value_box_t const *data)
205
0
{
206
  /*
207
   *  We're converting to PRESENTATION format
208
   *  so any attributes with enumeration values
209
   *  should be converted to string types.
210
   */
211
0
  if (data->enumv) {
212
0
    fr_dict_enum_value_t const *enumv;
213
214
0
    enumv = fr_dict_enum_by_value(data->enumv, data);
215
0
    if (enumv) return json_object_new_string(enumv->name);
216
0
  }
217
218
0
  switch (data->type) {
219
0
  default:
220
0
  do_string:
221
0
  {
222
0
    char    buffer[64];
223
0
    fr_sbuff_t  sbuff = FR_SBUFF_OUT(buffer, sizeof(buffer));
224
225
0
    if (fr_value_box_print(&sbuff, data, NULL) <= 0) return NULL;
226
227
0
    return json_object_new_string_len(buffer, fr_sbuff_used(&sbuff));
228
0
  }
229
230
0
  case FR_TYPE_STRING:
231
0
    return json_object_new_string_len(data->vb_strvalue, data->vb_length);
232
233
0
  case FR_TYPE_OCTETS:
234
0
    return json_object_new_string_len((char const *)data->vb_octets, data->vb_length);
235
236
0
  case FR_TYPE_BOOL:
237
0
    return json_object_new_boolean(data->vb_bool);
238
239
0
  case FR_TYPE_UINT8:
240
0
    return json_object_new_int(data->vb_uint8);
241
242
0
  case FR_TYPE_UINT16:
243
0
    return json_object_new_int(data->vb_uint16);
244
245
0
#ifdef HAVE_JSON_OBJECT_GET_INT64
246
0
  case FR_TYPE_UINT32:
247
0
    return json_object_new_int64((int64_t)data->vb_uint32);  /* uint32_t (max) > int32_t (max) */
248
249
0
  case FR_TYPE_UINT64:
250
0
    if (data->vb_uint64 > INT64_MAX) goto do_string;
251
0
    return json_object_new_int64(data->vb_uint64);
252
#else
253
  case FR_TYPE_UINT32:
254
    if (data->vb_uint32 > INT32_MAX) goto do_string;
255
    return json_object_new_int(data->vb_uint32);
256
#endif
257
258
0
  case FR_TYPE_INT8:
259
0
    return json_object_new_int(data->vb_int8);
260
261
0
  case FR_TYPE_INT16:
262
0
    return json_object_new_int(data->vb_int16);
263
264
0
  case FR_TYPE_INT32:
265
0
    return json_object_new_int(data->vb_int32);
266
267
0
#ifdef HAVE_JSON_OBJECT_GET_INT64
268
0
  case FR_TYPE_INT64:
269
0
    return json_object_new_int64(data->vb_int64);
270
271
0
  case FR_TYPE_SIZE:
272
0
    return json_object_new_int64(data->vb_size);
273
0
#endif
274
275
0
  case FR_TYPE_STRUCTURAL:
276
0
    fr_strerror_const("Can't convert structural type to JSON");
277
0
    return NULL;
278
0
  }
279
0
}
280
281
/** Print a value box as its equivalent JSON format without going via a struct json_object (in most cases)
282
 *
283
 * @param[out] out    buffer to write to.
284
 * @param[in] vb    to print.
285
 * @param[in] include_quotes  whether we should wrap string values,
286
 *        or non-native types like IPv4 addresses in quotes.
287
 * @return
288
 *  - <0 on error.
289
 *  - >= number of bytes written.
290
 */
291
fr_slen_t fr_json_str_from_value(fr_sbuff_t *out, fr_value_box_t const *vb, bool include_quotes)
292
0
{
293
0
  fr_sbuff_t our_out = FR_SBUFF(out);
294
295
0
  switch (vb->type) {
296
0
  case FR_TYPE_NULL:
297
0
    FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "null");
298
0
    break;
299
300
0
  case FR_TYPE_BOOL:
301
0
    FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, vb->vb_bool ? "true" : "false");
302
0
    break;
303
304
  /*
305
   *  This is identical to JSON-C's escaping function
306
   *  but we avoid creating JSON objects just to be able
307
   *  to escape strings.
308
   */
309
0
  case FR_TYPE_STRING:
310
0
  case FR_TYPE_OCTETS:
311
0
  {
312
0
    char const *last_app, *p, *end;
313
314
0
    if (include_quotes) FR_SBUFF_IN_CHAR_RETURN(&our_out, '"');
315
316
0
    last_app = p = vb->vb_strvalue;
317
0
    end = p + vb->vb_length;
318
319
0
    while (p < end) {
320
0
      if ((*p < ' ') || (*p == '"') || (*p == '\\') || (*p == '/')) {
321
0
        if (p > last_app) FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, last_app, p - last_app);
322
323
0
        switch (*p) {
324
0
        case '\b':
325
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\b");
326
0
          break;
327
328
0
        case '\n':
329
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\n");
330
0
          break;
331
332
0
        case '\r':
333
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\r");
334
0
          break;
335
336
0
        case '\t':
337
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\t");
338
0
          break;
339
340
0
        case '\f':
341
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\f");
342
0
          break;
343
344
0
        case '"':
345
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\\"");
346
0
          break;
347
348
0
        case '\\':
349
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\\\");
350
0
          break;
351
352
0
        case '/':
353
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\/");
354
0
          break;
355
356
0
        default:
357
0
          FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "\\u00");
358
0
          fr_base16_encode(&our_out, &FR_DBUFF_TMP((uint8_t const *)p, 1));
359
0
        }
360
361
0
        last_app = p + 1;
362
0
      }
363
0
      p++;
364
0
    }
365
0
    if (end > last_app) FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, last_app, end - last_app);
366
0
    if (include_quotes) FR_SBUFF_IN_CHAR_RETURN(&our_out, '"');
367
0
  }
368
0
    break;
369
370
0
  case FR_TYPE_UINT8:
371
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", vb->vb_uint8);
372
0
    break;
373
374
0
  case FR_TYPE_UINT16:
375
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", vb->vb_uint16);
376
0
    break;
377
378
0
  case FR_TYPE_UINT32:
379
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%u", vb->vb_uint32);
380
0
    break;
381
382
0
  case FR_TYPE_UINT64:
383
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%" PRIu64, vb->vb_uint64);
384
0
    break;
385
386
0
  case FR_TYPE_INT8:
387
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%i", vb->vb_int8);
388
0
    break;
389
390
0
  case FR_TYPE_INT16:
391
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%i", vb->vb_int16);
392
0
    break;
393
394
0
  case FR_TYPE_INT32:
395
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%i", vb->vb_int32);
396
0
    break;
397
398
0
  case FR_TYPE_INT64:
399
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%" PRIi64, vb->vb_int64);
400
0
    break;
401
402
0
  case FR_TYPE_SIZE:
403
0
    FR_SBUFF_IN_SPRINTF_RETURN(&our_out, "%zu", vb->vb_size);
404
0
    break;
405
406
  /*
407
   *  It's too complex to replicate the float/double printing
408
   *  here, so pass it off to JSON-C's printing functions.
409
   */
410
0
  case FR_TYPE_FLOAT32:
411
0
  {
412
0
    struct json_object *obj;
413
0
    fr_slen_t slen;
414
415
0
    obj = json_object_new_double((double)vb->vb_float32);
416
0
    if (unlikely(obj == NULL)) return -1;
417
0
    slen = fr_sbuff_in_strcpy(&our_out, json_object_to_json_string(obj));
418
0
    json_object_put_assert(obj);
419
420
0
    if (slen < 0) return slen;
421
0
    break;
422
0
  }
423
424
0
  case FR_TYPE_FLOAT64:
425
0
  {
426
0
    struct json_object *obj;
427
0
    fr_slen_t slen;
428
429
0
    obj = json_object_new_double((double)vb->vb_float64);
430
0
    if (unlikely(obj == NULL)) return -1;
431
0
    slen = fr_sbuff_in_strcpy(&our_out, json_object_to_json_string(obj));
432
0
    json_object_put_assert(obj);
433
434
0
    if (slen < 0) return slen;
435
0
    break;
436
0
  }
437
438
0
  case FR_TYPE_IPV4_ADDR:
439
0
  case FR_TYPE_IPV4_PREFIX:
440
0
  case FR_TYPE_IPV6_ADDR:
441
0
  case FR_TYPE_IPV6_PREFIX:
442
0
  case FR_TYPE_COMBO_IP_ADDR:
443
0
  case FR_TYPE_COMBO_IP_PREFIX:
444
0
  case FR_TYPE_IFID:
445
0
  case FR_TYPE_ETHERNET:
446
0
  case FR_TYPE_DATE:
447
0
  case FR_TYPE_TIME_DELTA:
448
0
  case FR_TYPE_ATTR:
449
0
  {
450
0
    fr_slen_t slen;
451
452
0
    if (include_quotes) FR_SBUFF_IN_CHAR_RETURN(&our_out, '"');
453
0
    slen = fr_value_box_print(&our_out, vb, NULL);
454
0
    if (include_quotes) FR_SBUFF_IN_CHAR_RETURN(&our_out, '"');
455
    /* Intentionally after writing second dquote, so the buffer is always has a properly terminated string */
456
0
    if (slen < 0) return slen;
457
0
  }
458
0
    break;
459
460
0
  case FR_TYPE_STRUCTURAL:
461
0
    fr_strerror_const("Structural boxes not yet supported");
462
0
    return -1;
463
464
0
  case FR_TYPE_INTERNAL:
465
0
    fr_strerror_printf("Box type %s cannot be converted to string", fr_type_to_str(vb->type));
466
0
    return -1;
467
0
  }
468
469
0
  return fr_sbuff_set(out, &our_out);
470
0
}
471
472
/** Print JSON-C version
473
 *
474
 */
475
void fr_json_version_print(void)
476
0
{
477
0
#ifdef HAVE_JSON_C_VERSION
478
0
  INFO("libfreeradius-json: json-c version: %s", json_c_version());
479
#else
480
  INFO("libfreeradius-json: json-c version: Unknown (less than 0.10) - Please upgrade");
481
#endif
482
0
}
483
484
485
/** Convert a value box into a JSON object
486
 *
487
 * If format.value.always_string is set then a numeric value
488
 * will be returned as a JSON string object.
489
 *
490
 * @param[in] ctx Talloc context.
491
 * @param[out] out  returned json object.
492
 * @param[in] vb  to get the value of.
493
 * @param[in] format  format definition, or NULL.
494
 * @return
495
 *  - 0 on success.
496
 *  - -1 on error.
497
 */
498
static inline CC_HINT(always_inline)
499
int json_afrom_value_box(TALLOC_CTX *ctx, json_object **out,
500
       fr_value_box_t const *vb, fr_json_format_t const *format)
501
{
502
  if (!format) {
503
    MEM(*out = json_object_from_value_box(vb));
504
    return 0;
505
  }
506
507
  /*
508
   *  Evaluated before "always_string".
509
   */
510
  if (vb->type == FR_TYPE_OCTETS) {
511
    switch (format->value.binary_format) {
512
    case JSON_BINARY_FORMAT_BASE16:
513
    case JSON_BINARY_FORMAT_BASE64:
514
    {
515
      fr_sbuff_t  *encoded;
516
      FR_SBUFF_TALLOC_THREAD_LOCAL(&encoded, 256, SIZE_MAX);
517
518
      switch (format->value.binary_format) {
519
      /*
520
       *  Hex encode octets values when requested.
521
       */
522
      case JSON_BINARY_FORMAT_BASE16:
523
        fr_base16_encode(encoded, &FR_DBUFF_TMP(vb->vb_octets, vb->vb_length));
524
        break;
525
526
      /*
527
       *  Base64-encode octets values when requested.
528
       */
529
      case JSON_BINARY_FORMAT_BASE64:
530
        fr_base64_encode(encoded, &FR_DBUFF_TMP(vb->vb_octets, vb->vb_length), true);
531
        break;
532
533
      default:
534
        break;
535
      }
536
537
      MEM(*out = json_object_new_string_len(fr_sbuff_start(encoded), fr_sbuff_used(encoded)));
538
      return 0;
539
    }
540
541
    case JSON_BINARY_FORMAT_RAW:
542
      break;
543
    }
544
  }
545
546
  if (format->value.always_string) {
547
    fr_value_box_t    vb_str = FR_VALUE_BOX_INITIALISER_NULL(vb_str);
548
549
    if (unlikely(fr_value_box_cast(ctx, &vb_str, FR_TYPE_STRING, NULL, vb) < 0)) return -1;
550
551
    MEM(*out = json_object_from_value_box(&vb_str));
552
    fr_value_box_clear(&vb_str);
553
554
    return 0;
555
  }
556
557
  MEM(*out = json_object_from_value_box(vb));
558
  return 0;
559
}
560
561
/** Convert fr_pair_t into a JSON object
562
 *
563
 * If format.value.enum_as_int is set, and the given VP is an enum
564
 * value, the integer value is returned as a json_object rather
565
 * than the text representation.
566
 *
567
 * If format.value.always_string is set then a numeric value
568
 * will be returned as a JSON string object.
569
 *
570
 * @param[in] ctx Talloc context.
571
 * @param[out] out  returned json object.
572
 * @param[in] vp  to get the value of.
573
 * @param[in] format  format definition, or NULL.
574
 * @return
575
 *  - 0 on success.
576
 *  - -1 on error.
577
 */
578
static int json_afrom_pair(TALLOC_CTX *ctx, json_object **out,
579
         fr_pair_t *vp, fr_json_format_t const *format)
580
0
{
581
0
  fr_value_box_t const  *vb;
582
583
0
  fr_assert(vp);
584
585
0
  vb = &vp->data;
586
587
0
  if (format->value.enum_as_int) {
588
0
    (void)fr_pair_value_enum_box(&vb, vp);
589
0
  }
590
591
0
  return json_afrom_value_box(ctx, out, vb, format);
592
0
}
593
594
595
/** Get attribute name with optional prefix
596
 *
597
 * If the format "attr.prefix" string is set then prepend this
598
 * to the given attribute name, otherwise just return name alone.
599
 *
600
 * @param[out] out sbuff to write the new name
601
 * @param[in] da dictionary attribute to get name of
602
 * @param[in] format json format structure
603
 * @return length of attribute name
604
 */
605
static inline ssize_t attr_name_with_prefix(fr_sbuff_t *out, fr_dict_attr_t const *da, fr_json_format_t const *format)
606
0
{
607
0
  fr_sbuff_t our_out;
608
609
0
  if (!out) return 0;
610
611
0
  our_out = FR_SBUFF(out);
612
613
0
  if (format->attr.prefix) {
614
0
    FR_SBUFF_IN_STRCPY_RETURN(&our_out, format->attr.prefix);
615
0
    FR_SBUFF_IN_CHAR_RETURN(&our_out, ':');
616
0
  }
617
618
0
  FR_SBUFF_IN_BSTRNCPY_RETURN(&our_out, da->name, da->name_len);
619
620
0
  FR_SBUFF_SET_RETURN(out, &our_out);
621
0
}
622
623
624
/** Verify that the options in fr_json_format_t are valid
625
 *
626
 * Warnings are optional, will fatal error if the format is corrupt.
627
 *
628
 * @param[in] format  the format structure to check
629
 * @param[in] verbose print out warnings if set
630
 * @return    true if format is good, otherwise false
631
 */
632
bool fr_json_format_verify(fr_json_format_t const *format, bool verbose)
633
0
{
634
0
  bool ret = true;
635
636
0
  fr_assert(format);
637
638
0
  switch (format->output_mode) {
639
0
  case JSON_MODE_OBJECT:
640
0
  case JSON_MODE_OBJECT_SIMPLE:
641
0
  case JSON_MODE_ARRAY:
642
    /* all options are valid */
643
0
    return true;
644
0
  case JSON_MODE_ARRAY_OF_VALUES:
645
0
    if (format->attr.prefix) {
646
0
      if (verbose) WARN("attribute name prefix not valid in output_mode 'array_of_values' and will be ignored");
647
0
      ret = false;
648
0
    }
649
0
    if (format->value.value_is_always_array) {
650
0
      if (verbose) WARN("'value_is_always_array' not valid in output_mode 'array_of_values' and will be ignored");
651
0
      ret = false;
652
0
    }
653
0
    return ret;
654
0
  case JSON_MODE_ARRAY_OF_NAMES:
655
0
    if (format->value.value_is_always_array) {
656
0
      if (verbose) WARN("'value_is_always_array' not valid in output_mode 'array_of_names' and will be ignored");
657
0
      ret = false;
658
0
    }
659
0
    if (format->value.enum_as_int) {
660
0
      if (verbose) WARN("'enum_as_int' not valid in output_mode 'array_of_names' and will be ignored");
661
0
      ret = false;
662
0
    }
663
0
    if (format->value.always_string) {
664
0
      if (verbose) WARN("'always_string' not valid in output_mode 'array_of_names' and will be ignored");
665
0
      ret = false;
666
0
    }
667
0
    return ret;
668
0
  default:
669
0
    ERROR("JSON format output mode is invalid");
670
0
  }
671
672
  /* If we get here, something has gone wrong */
673
0
  fr_assert(0);
674
675
0
  return false;
676
0
}
677
678
0
#define INVALID_TYPE \
679
0
do { \
680
0
  fr_assert(0); \
681
0
  fr_strerror_printf("Invalid type %s for attribute %s", fr_type_to_str(vp->vp_type), vp->da->name); \
682
0
  goto error; \
683
0
} while (0)
684
685
/** Returns a JSON object representation of a list of value pairs
686
 *
687
 * The result is a struct json_object, which should be free'd with
688
 * json_object_put() by the caller. Intended to only be called by
689
 * fr_json_afrom_pair_list().
690
 *
691
 * This function generates the "object" format, JSON_MODE_OBJECT.
692
 * @see fr_json_format_s
693
 *
694
@verbatim
695
{
696
  "<attribute0>":{
697
    "type":"<type0>",
698
    "value":[<value0>,<value1>,<valueN>]    // if value_is_always_array is true
699
  },              // or
700
  "<attribute1>":{
701
    "type":"<type1>",
702
    "value":<value0>        // if value_is_always_array is false
703
                // and there is only one value
704
  },
705
  "<attributeN>":{
706
    "type":"<typeN>",
707
    "value":[...]
708
  }
709
}
710
@endverbatim
711
 *
712
 * @param[in] ctx Talloc context.
713
 * @param[in] vps a list of value pairs.
714
 * @param[in] format  Formatting control, must be set.
715
 * @return JSON object with the generated representation.
716
 */
717
static CC_HINT(warn_unused_result)
718
json_object *json_object_afrom_pair_list(TALLOC_CTX *ctx, fr_pair_list_t *vps, fr_json_format_t const *format)
719
{
720
  fr_pair_t   *vp;
721
  struct json_object  *obj;
722
  char      buf[FR_DICT_ATTR_MAX_NAME_LEN + 32];
723
724
  /* Check format and type */
725
  fr_assert(format);
726
  fr_assert(format->output_mode == JSON_MODE_OBJECT);
727
728
  MEM(obj = json_object_new_object());
729
730
  for (vp = fr_pair_list_head(vps);
731
       vp;
732
       vp = fr_pair_list_next(vps, vp)) {
733
    fr_sbuff_t    attr_name;
734
    struct json_object  *vp_object, *values, *value, *type_name;
735
736
    if (vp->vp_raw) continue;
737
738
    /*
739
     *  Get attribute name and value.
740
     */
741
    fr_sbuff_init_in(&attr_name, buf, sizeof(buf) - 1);
742
    if (attr_name_with_prefix(&attr_name, vp->da, format) < 0) {
743
    error:
744
      json_object_put_assert(obj);
745
      return NULL;
746
    }
747
748
    switch (vp->vp_type) {
749
    case FR_TYPE_LEAF:
750
      if (json_afrom_pair(ctx, &value, vp, format) < 0) {
751
        fr_strerror_const("Failed to convert attribute value to JSON object");
752
        goto error;
753
      }
754
      break;
755
    /*
756
     *  For nested attributes we recurse.  The nesting is represented
757
     *  as a table, either as the single value, or as an element in
758
     *  an array.
759
     *
760
     *  ...
761
     *  "value" : { "nested_attr" : { "type" : "<nested_type>", "value" : "<nested_attr_value>" } }
762
     *  ...
763
     *
764
     *  ...
765
     *  "value" : [ { "nested_attr" : { "type" : "<nested_type>", "value" : "<nested_attr_value>" } } ]
766
     *  ...
767
     *
768
     *  The formatting of nested attributes and their structure is
769
     *  identical to top level attributes.
770
     */
771
    case FR_TYPE_STRUCTURAL:
772
      value = json_object_afrom_pair_list(ctx, &vp->vp_group, format);
773
      if (unlikely(value == NULL)) goto error;
774
      break;
775
776
    default:
777
      INVALID_TYPE;
778
    }
779
780
    /*
781
     *  Look in the table to see if we already have a key for the attribute
782
     *  we're working on.
783
     *
784
     *  If we don't we create a new object in either the form:
785
     *
786
     *  "<attribute>": {
787
     *    "type": "<type>",
788
     *    "value": [<value>]    // if value_is_always_array is true
789
     *            // or
790
     *    "value": <value>    // if value_is_always_array is false
791
     *            // and there is only one value
792
     *  }
793
     */
794
    if (!json_object_object_get_ex(obj, fr_sbuff_start(&attr_name), &vp_object)) {
795
      /*
796
       *  Wasn't there, so create a new object for this attribute.
797
       */
798
      MEM(vp_object = json_object_new_object());
799
      json_object_object_add(obj, fr_sbuff_start(&attr_name), vp_object);
800
801
      /*
802
       *  Add "type" to newly created keys.
803
       */
804
      MEM(type_name = json_object_new_string(fr_type_to_str(vp->vp_type)));
805
      json_object_object_add_ex(vp_object, "type", type_name, JSON_C_OBJECT_KEY_IS_CONSTANT);
806
807
      /*
808
       *  Create a "value" array to hold any attribute values for this attribute...
809
       */
810
      if (format->value.value_is_always_array) {
811
        MEM(values = json_object_new_array());
812
        json_object_object_add_ex(vp_object, "value", values, JSON_C_OBJECT_KEY_IS_CONSTANT);
813
        json_object_array_add(values, value);
814
        continue;
815
      }
816
817
      /*
818
       *  ...or just add the value directly.
819
       */
820
      json_object_object_add_ex(vp_object, "value", value, JSON_C_OBJECT_KEY_IS_CONSTANT);
821
822
      continue; /* Next attribute! */
823
    }
824
825
    /*
826
     *  Find the 'values' array to add the current value to.
827
     */
828
    if (!fr_cond_assert(json_object_object_get_ex(vp_object, "value", &values))) {
829
      fr_strerror_const("Inconsistent JSON tree");
830
      goto error;
831
    }
832
833
    /*
834
     *  If value_is_always_array is no set then "values" may not be an array, so it will
835
     *  need converting to an array to add this extra attribute.
836
     */
837
    if (!format->value.value_is_always_array) {
838
      json_type   type;
839
      struct json_object  *convert_value = values;
840
841
      /* Check "values" type */
842
      type = json_object_get_type(values);
843
844
      /* It wasn't an array, so turn it into one with the old value as the first entry */
845
      if (type != json_type_array) {
846
        MEM(values = json_object_new_array());
847
        json_object_array_add(values, json_object_get(convert_value));
848
        json_object_object_del(vp_object, "value");
849
        json_object_object_add_ex(vp_object, "value", values,
850
                JSON_C_OBJECT_KEY_IS_CONSTANT);
851
      }
852
    }
853
    json_object_array_add(values, value);
854
  }
855
856
  return obj;
857
}
858
859
860
/** Returns a JSON object representation of a list of value pairs
861
 *
862
 * The result is a struct json_object, which should be free'd with
863
 * json_object_put() by the caller. Intended to only be called by
864
 * fr_json_afrom_pair_list().
865
 *
866
 * This function generates the "simple object" format, JSON_MODE_OBJECT_SIMPLE.
867
 * @see fr_json_format_s
868
 *
869
@verbatim
870
{
871
  "<attribute0>":[<value0>,<value1>,<valueN>] // if value_is_always_array is true
872
              // or
873
  "<attribute1>":<value0>       // if value_is_always_array is false,
874
              // and there is only one value
875
  "<attributeN>":[<value0>,<value1>,<valueN>]
876
}
877
@endverbatim
878
 *
879
 * @param[in] ctx Talloc context.
880
 * @param[in] vps a list of value pairs.
881
 * @param[in] format  Formatting control, must be set.
882
 * @return JSON object with the generated representation.
883
 */
884
static json_object *json_simple_obj_afrom_pair_list(TALLOC_CTX *ctx, fr_pair_list_t *vps,
885
             fr_json_format_t const *format)
886
0
{
887
0
  fr_pair_t   *vp;
888
0
  struct json_object  *obj;
889
0
  char      buf[FR_DICT_ATTR_MAX_NAME_LEN + 32];
890
0
  json_type   type;
891
892
  /* Check format and type */
893
0
  fr_assert(format);
894
0
  fr_assert(format->output_mode == JSON_MODE_OBJECT_SIMPLE);
895
896
0
  MEM(obj = json_object_new_object());
897
898
0
  for (vp = fr_pair_list_head(vps);
899
0
       vp;
900
0
       vp = fr_pair_list_next(vps, vp)) {
901
0
    fr_sbuff_t    attr_name;
902
0
    struct json_object  *vp_object, *value;
903
0
    struct json_object  *values = NULL;
904
0
    bool      add_single = false;
905
906
0
    if (vp->vp_raw) continue;
907
908
    /*
909
     *  Get attribute name and value.
910
     */
911
0
    fr_sbuff_init_in(&attr_name, buf, sizeof(buf) - 1);
912
0
    if (attr_name_with_prefix(&attr_name, vp->da, format) < 0) {
913
0
    error:
914
0
      json_object_put_assert(obj);
915
0
      return NULL;
916
0
    }
917
918
0
    switch (vp->vp_type) {
919
0
    case FR_TYPE_LEAF:
920
0
      if (json_afrom_pair(ctx, &value, vp, format) < 0) {
921
0
        fr_strerror_const("Failed to convert attribute value to JSON object");
922
0
        goto error;
923
0
      }
924
0
      break;
925
    /*
926
     *  For nested attributes we recurse.  The nesting is represented
927
     *  as a table, either as the single value, or as an element in
928
     *  an array.
929
     *
930
     *  ...
931
     *  "<parent>" : { "<nested_attr>" : <nested_attr_value> }
932
     *  ...
933
     *
934
     *  ...
935
     *  "<parent>" : [ { "<nested_attr>" : "<nested_attr_value>" } ]
936
     *  ...
937
     *
938
     *  The formatting of nested attributes and their structure is
939
     *  identical to top level attributes.
940
     */
941
0
    case FR_TYPE_STRUCTURAL:
942
0
      value = json_simple_obj_afrom_pair_list(ctx, &vp->vp_group, format);
943
0
      if (unlikely(value == NULL)) goto error;
944
0
      break;
945
946
0
    default:
947
0
      INVALID_TYPE;
948
0
    }
949
950
    /*
951
     *  See if we already have a key in the table we're working on,
952
     *  if not then create a new one.
953
     */
954
0
    if (!json_object_object_get_ex(obj, fr_sbuff_start(&attr_name), &vp_object)) {
955
0
      if (format->value.value_is_always_array) {
956
        /*
957
         *  We have been asked to ensure /all/ values are lists,
958
         *  even if there's only one attribute.
959
         */
960
0
        MEM(values = json_object_new_array());
961
0
        json_object_object_add(obj, fr_sbuff_start(&attr_name), values);
962
0
      } else {
963
        /*
964
         *  Deal with it later on.
965
         */
966
0
        add_single = true;
967
0
      }
968
    /*
969
     *  If we do have the key already, get its value array.
970
     */
971
0
    } else {
972
0
      type = json_object_get_type(vp_object);
973
974
0
      if (type == json_type_array) {
975
0
        values = vp_object;
976
0
      } else {
977
        /*
978
         *  We've seen one of these before, but didn't add
979
         *  it as an array the first time. Sort that out.
980
         */
981
0
        MEM(values = json_object_new_array());
982
0
        json_object_array_add(values, json_object_get(vp_object));
983
984
        /*
985
         *  Existing key will have refcount decremented
986
         *  and will be freed if this drops to zero.
987
         */
988
0
        json_object_object_add(obj, fr_sbuff_start(&attr_name), values);
989
0
      }
990
0
    }
991
992
0
    if (add_single) {
993
      /*
994
       *  Only ever used the first time adding a new
995
       *  attribute when "value_is_always_array" is not set.
996
       */
997
0
      json_object_object_add(obj, fr_sbuff_start(&attr_name), value);
998
0
    } else {
999
      /*
1000
       *  Otherwise we're always appending to a JSON array.
1001
       */
1002
0
      json_object_array_add(values, value);
1003
0
    }
1004
0
  }
1005
1006
0
  return obj;
1007
0
}
1008
1009
1010
/** Returns a JSON array representation of a list of value pairs
1011
 *
1012
 * The result is a struct json_object, which should be free'd with
1013
 * json_object_put() by the caller. Intended to only be called by
1014
 * fr_json_afrom_pair_list().
1015
 *
1016
 * This function generates the "array" format, JSON_MODE_ARRAY.
1017
 * @see fr_json_format_s
1018
 *
1019
 * @param[in] ctx Talloc context.
1020
 * @param[in] vps a list of value pairs.
1021
 * @param[in] format  Formatting control, must be set.
1022
 * @return JSON object with the generated representation.
1023
 */
1024
static struct json_object *json_array_afrom_pair_list(TALLOC_CTX *ctx, fr_pair_list_t *vps,
1025
                  fr_json_format_t const *format)
1026
0
{
1027
0
  fr_pair_t   *vp;
1028
0
  struct json_object  *obj;
1029
0
  struct json_object  *seen_attributes = NULL;
1030
0
  char      buf[FR_DICT_ATTR_MAX_NAME_LEN + 32];
1031
1032
  /* Check format and type */
1033
0
  fr_assert(format);
1034
0
  fr_assert(format->output_mode == JSON_MODE_ARRAY);
1035
1036
0
  MEM(obj = json_object_new_array());
1037
1038
  /*
1039
   *  If attribute values should be in a list format, then keep track
1040
   *  of the attributes we've previously seen in a JSON object.
1041
   */
1042
0
  if (format->value.value_is_always_array) {
1043
0
    seen_attributes = json_object_new_object();
1044
0
  }
1045
1046
0
  for (vp = fr_pair_list_head(vps);
1047
0
       vp;
1048
0
       vp = fr_pair_list_next(vps, vp)) {
1049
0
    fr_sbuff_t    attr_name;
1050
0
    struct json_object  *name, *value, *type_name;
1051
0
    struct json_object  *values = NULL;
1052
0
    struct json_object  *attrobj = NULL;
1053
0
    bool      already_seen = false;
1054
1055
0
    if (vp->vp_raw) continue;
1056
1057
    /*
1058
     *  Get attribute name and value.
1059
     */
1060
0
    fr_sbuff_init_in(&attr_name, buf, sizeof(buf) - 1);
1061
0
    if (attr_name_with_prefix(&attr_name, vp->da, format) < 0) {
1062
0
    error:
1063
0
      json_object_put_assert(seen_attributes);
1064
0
      json_object_put_assert(obj);
1065
0
      return NULL;
1066
0
    }
1067
1068
0
    switch (vp->vp_type) {
1069
0
    case FR_TYPE_LEAF:
1070
0
      if (json_afrom_pair(ctx, &value, vp, format) < 0) {
1071
0
        fr_strerror_const("Failed to convert attribute value to JSON object");
1072
0
        goto error;
1073
0
      }
1074
0
      break;
1075
1076
0
    case FR_TYPE_STRUCTURAL:
1077
0
      value = json_array_afrom_pair_list(ctx, &vp->vp_group, format);
1078
0
      if (unlikely(value == NULL)) goto error;
1079
0
      break;
1080
1081
0
    default:
1082
0
      INVALID_TYPE;
1083
0
    }
1084
1085
0
    if (format->value.value_is_always_array) {
1086
      /*
1087
       *  Try and find this attribute in the "seen_attributes" object. If it is
1088
       *  there then get the "values" array to add this attribute value to.
1089
       */
1090
0
      already_seen = json_object_object_get_ex(seen_attributes, fr_sbuff_start(&attr_name), &values);
1091
0
    }
1092
1093
    /*
1094
     *  If we're adding all attributes to the toplevel array, or we're adding values
1095
     *  to an array of an existing attribute but haven't seen it before, then we need
1096
     *  to create a new JSON object for this attribute.
1097
     */
1098
0
    if (!format->value.value_is_always_array || !already_seen) {
1099
      /*
1100
       * Create object and add it to top-level array
1101
       */
1102
0
      MEM(attrobj = json_object_new_object());
1103
0
      json_object_array_add(obj, attrobj);
1104
1105
      /*
1106
       * Add the attribute name in the "name" key and the type in the "type" key
1107
       */
1108
0
      MEM(name = json_object_new_string(fr_sbuff_start(&attr_name)));
1109
0
      json_object_object_add_ex(attrobj, "name", name, JSON_C_OBJECT_KEY_IS_CONSTANT);
1110
1111
0
      MEM(type_name = json_object_new_string(fr_type_to_str(vp->vp_type)));
1112
0
      json_object_object_add_ex(attrobj, "type", type_name, JSON_C_OBJECT_KEY_IS_CONSTANT);
1113
0
    }
1114
1115
0
    if (format->value.value_is_always_array) {
1116
      /*
1117
       *  We're adding values to an array for the first copy of this attribute
1118
       *  that we saw. First time around we need to create an array.
1119
       */
1120
0
      if (!already_seen) {
1121
0
        MEM(values = json_object_new_array());
1122
        /*
1123
         * Add "value":[] key to the attribute object
1124
         */
1125
0
        json_object_object_add_ex(attrobj, "value", values, JSON_C_OBJECT_KEY_IS_CONSTANT);
1126
1127
        /*
1128
         * Also add to "seen_attributes" to check later
1129
         */
1130
0
        json_object_object_add(seen_attributes, fr_sbuff_start(&attr_name), json_object_get(values));
1131
0
      }
1132
1133
      /*
1134
       *  Always add the value to the respective "values" array.
1135
       */
1136
0
      json_object_array_add(values, value);
1137
0
    } else {
1138
      /*
1139
       * This is simpler; just add a "value": key to the attribute object.
1140
       */
1141
0
      json_object_object_add_ex(attrobj, "value", value, JSON_C_OBJECT_KEY_IS_CONSTANT);
1142
0
    }
1143
1144
0
  }
1145
1146
  /*
1147
   *  No longer need the "seen_attributes" object, it was just used for tracking.
1148
   */
1149
0
  if (format->value.value_is_always_array) {
1150
0
    json_object_put_assert(seen_attributes);
1151
0
  }
1152
1153
0
  return obj;
1154
0
}
1155
1156
1157
/** Returns a JSON array of a list of value pairs
1158
 *
1159
 * The result is a struct json_object, which should be free'd with
1160
 * json_object_put() by the caller. Intended to only be called by
1161
 * fr_json_afrom_pair_list().
1162
 *
1163
 * This function generates the "array_of_values" format,
1164
 * JSON_MODE_ARRAY_OF_VALUES, listing just the attribute values.
1165
 * @see fr_json_format_s
1166
 *
1167
 * @param[in] ctx Talloc context.
1168
 * @param[in] vps a list of value pairs.
1169
 * @param[in] format  Formatting control, must be set.
1170
 * @return JSON object with the generated representation.
1171
 */
1172
static struct json_object *json_value_array_afrom_pair_list(TALLOC_CTX *ctx, fr_pair_list_t *vps,
1173
                  fr_json_format_t const *format)
1174
0
{
1175
0
  fr_pair_t   *vp;
1176
0
  struct json_object  *obj;
1177
1178
  /* Check format and type */
1179
0
  fr_assert(format);
1180
0
  fr_assert(format->output_mode == JSON_MODE_ARRAY_OF_VALUES);
1181
1182
0
  MEM(obj = json_object_new_array());
1183
1184
  /*
1185
   *  This array format is very simple - just add all the
1186
   *  attribute values to the array in order.
1187
   */
1188
0
  for (vp = fr_pair_list_head(vps);
1189
0
       vp;
1190
0
       vp = fr_pair_list_next(vps, vp)) {
1191
0
    struct json_object  *value;
1192
1193
0
    if (vp->vp_raw) continue;
1194
1195
0
    switch (vp->vp_type) {
1196
0
    case FR_TYPE_LEAF:
1197
0
      if (json_afrom_pair(ctx, &value, vp, format) < 0) {
1198
0
        fr_strerror_const("Failed to convert attribute value to JSON object");
1199
0
      error:
1200
0
        json_object_put_assert(obj);
1201
0
        return NULL;
1202
0
      }
1203
0
      break;
1204
1205
0
    case FR_TYPE_STRUCTURAL:
1206
0
      value = json_value_array_afrom_pair_list(ctx, &vp->vp_group, format);
1207
0
      if (value == NULL) goto error;
1208
0
      break;
1209
1210
0
    default:
1211
0
      INVALID_TYPE;
1212
0
    }
1213
1214
0
    json_object_array_add(obj, value);
1215
0
  }
1216
1217
0
  return obj;
1218
0
}
1219
1220
1221
/** Returns a JSON array of a list of value pairs
1222
 *
1223
 * The result is a struct json_object, which should be free'd with
1224
 * json_object_put() by the caller. Intended to only be called by
1225
 * fr_json_afrom_pair_list().
1226
 *
1227
 * This function generates the "array_of_names" format,
1228
 * JSON_MODE_ARRAY_OF_NAMES, listing just the attribute names.
1229
 * @see fr_json_format_s
1230
 *
1231
 * @param[in] ctx Talloc context.
1232
 * @param[in] vps a list of value pairs.
1233
 * @param[in] format  Formatting control, must be set.
1234
 * @return JSON object with the generated representation.
1235
 */
1236
static struct json_object *json_attr_array_afrom_pair_list(TALLOC_CTX *ctx, fr_pair_list_t *vps,
1237
                 fr_json_format_t const *format)
1238
0
{
1239
0
  fr_pair_t   *vp;
1240
0
  struct json_object  *obj;
1241
0
  char      buf[FR_DICT_ATTR_MAX_NAME_LEN + 32];
1242
1243
  /* Check format and type */
1244
0
  fr_assert(format);
1245
0
  fr_assert(format->output_mode == JSON_MODE_ARRAY_OF_NAMES);
1246
1247
0
  MEM(obj = json_object_new_array());
1248
1249
  /*
1250
   *  Add all the attribute names to the array in order.
1251
   */
1252
0
  for (vp = fr_pair_list_head(vps);
1253
0
       vp;
1254
0
       vp = fr_pair_list_next(vps, vp)) {
1255
0
    struct json_object  *value;
1256
0
    fr_sbuff_t    attr_name;
1257
1258
0
    if (vp->vp_raw) continue;
1259
1260
0
    fr_sbuff_init_in(&attr_name, buf, sizeof(buf) - 1);
1261
0
    if (attr_name_with_prefix(&attr_name, vp->da, format) < 0) {
1262
0
    error:
1263
0
      json_object_put_assert(obj);
1264
0
      return NULL;
1265
0
    }
1266
0
    value = json_object_new_string(fr_sbuff_start(&attr_name));
1267
1268
0
    switch (vp->vp_type) {
1269
0
    case FR_TYPE_LEAF:
1270
0
      break;
1271
1272
0
    case FR_TYPE_STRUCTURAL:
1273
0
      json_object_array_add(obj, value);
1274
0
      value = json_attr_array_afrom_pair_list(ctx, &vp->vp_group, format);
1275
0
      if (value == NULL) goto error;
1276
0
      break;
1277
1278
0
    default:
1279
0
      INVALID_TYPE;
1280
0
    }
1281
1282
0
    json_object_array_add(obj, value);
1283
0
  }
1284
1285
0
  return obj;
1286
0
}
1287
1288
1289
/** Returns a JSON string of a list of value pairs
1290
 *
1291
 * The result is a talloc-ed string, freeing the string is
1292
 * the responsibility of the caller.
1293
 *
1294
 * The 'format' struct contains settings to configure the output
1295
 * JSON document format.
1296
 * @see fr_json_format_s
1297
 *
1298
 * Default output, when format is NULL, is:
1299
@verbatim
1300
{
1301
  "<attribute0>":{
1302
    "type":"<type0>",
1303
    "value":[<value0>,<value1>,<valueN>]
1304
  },
1305
  "<attribute1>":{
1306
    "type":"<type1>",
1307
    "value":[...]
1308
  },
1309
  "<attributeN>":{
1310
    "type":"<typeN>",
1311
    "value":[...]
1312
  }
1313
}
1314
@endverbatim
1315
 *
1316
 * @param[in] ctx Talloc context.
1317
 * @param[in] vps a list of value pairs.
1318
 * @param[in] format  Formatting control, can be NULL to use default format.
1319
 * @return JSON string representation of the value pairs
1320
 */
1321
char *fr_json_afrom_pair_list(TALLOC_CTX *ctx, fr_pair_list_t *vps,
1322
            fr_json_format_t const *format)
1323
{
1324
  struct json_object  *obj = NULL;
1325
  const char    *p;
1326
  char      *out;
1327
1328
  if (!format) format = &default_json_format;
1329
1330
  switch (format->output_mode) {
1331
  case JSON_MODE_OBJECT:
1332
    MEM(obj = json_object_afrom_pair_list(ctx, vps, format));
1333
    break;
1334
  case JSON_MODE_OBJECT_SIMPLE:
1335
    MEM(obj = json_simple_obj_afrom_pair_list(ctx, vps, format));
1336
    break;
1337
  case JSON_MODE_ARRAY:
1338
    MEM(obj = json_array_afrom_pair_list(ctx, vps, format));
1339
    break;
1340
  case JSON_MODE_ARRAY_OF_VALUES:
1341
    MEM(obj = json_value_array_afrom_pair_list(ctx, vps, format));
1342
    break;
1343
  case JSON_MODE_ARRAY_OF_NAMES:
1344
    MEM(obj = json_attr_array_afrom_pair_list(ctx, vps, format));
1345
    break;
1346
  default:
1347
    /* This should never happen */
1348
    fr_assert(0);
1349
  }
1350
1351
  /*
1352
   *  p is a buff inside obj, and will be freed
1353
   *  when it is freed.
1354
   */
1355
  MEM(p = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN));
1356
  MEM(out = talloc_strdup(ctx, p));
1357
1358
  /*
1359
   * Free the JSON structure, it's not needed any more
1360
   */
1361
  json_object_put_assert(obj);
1362
1363
  return out;
1364
}