1
+#include "jsmn.h"
2
+#include "../libnetdata.h"
3
+#include "json.h"
4
+#include "libnetdata/libnetdata.h"
5
+#include "../../health/health.h"
6
+
7
+#define JSON_TOKENS 1024
8
+
9
+int json_tokens = JSON_TOKENS;
10
+
11
+/**
12
+ * Json Tokenise
13
+ *
14
+ * Map the string given inside tokens.
15
+ *
16
+ * @param js is the string used to create the tokens
17
+ * @param len is the string length
18
+ * @param count the number of tokens present in the string
19
+ *
20
+ * @return it returns the json parsed in tokens
21
+ */
22
+#ifdef ENABLE_JSONC
23
+json_object *json_tokenise(char *js) {
24
+ if(!js) {
25
+ error("JSON: json string is empty.");
26
+ return NULL;
27
+ }
28
+
29
+ json_object *token = json_tokener_parse(js);
30
+ if(!token) {
31
+ error("JSON: Invalid json string.");
32
+ return NULL;
33
+ }
34
+
35
+ return token;
36
+}
37
+#else
38
+jsmntok_t *json_tokenise(char *js, size_t len, size_t *count)
39
+{
40
+ int n = json_tokens;
41
+ if(!js || !len) {
42
+ error("JSON: json string is empty.");
43
+ return NULL;
44
+ }
45
+
46
+ jsmn_parser parser;
47
+ jsmn_init(&parser);
48
+
49
+ jsmntok_t *tokens = mallocz(sizeof(jsmntok_t) * n);
50
+ if(!tokens) return NULL;
51
+
52
+ int ret = jsmn_parse(&parser, js, len, tokens, n);
53
+ while (ret == JSMN_ERROR_NOMEM) {
54
+ n *= 2;
55
+ jsmntok_t *new = reallocz(tokens, sizeof(jsmntok_t) * n);
56
+ if(!new) {
57
+ freez(tokens);
58
+ return NULL;
59
+ }
60
+ tokens = new;
61
+ ret = jsmn_parse(&parser, js, len, tokens, n);
62
+ }
63
+
64
+ if (ret == JSMN_ERROR_INVAL) {
65
+ error("JSON: Invalid json string.");
66
+ freez(tokens);
67
+ return NULL;
68
+ }
69
+ else if (ret == JSMN_ERROR_PART) {
70
+ error("JSON: Truncated JSON string.");
71
+ freez(tokens);
72
+ return NULL;
73
+ }
74
+
75
+ if(count) *count = (size_t)ret;
76
+
77
+ if(json_tokens < n) json_tokens = n;
78
+ return tokens;
79
+}
80
+#endif
81
+
82
+/**
83
+ * Callback Print
84
+ *
85
+ * Set callback print case necesary and wrinte an information inside a buffer to write in the log.
86
+ *
87
+ * @param e a pointer for a structure that has the complete information about json structure.
88
+ *
89
+ * @return It always return 0
90
+ */
91
+int json_callback_print(JSON_ENTRY *e)
92
+{
93
+ BUFFER *wb=buffer_create(300);
94
+
95
+ buffer_sprintf(wb,"%s = ", e->name);
96
+ char txt[50];
97
+ switch(e->type) {
98
+ case JSON_OBJECT:
99
+ e->callback_function = json_callback_print;
100
+ buffer_strcat(wb,"OBJECT");
101
+ break;
102
+
103
+ case JSON_ARRAY:
104
+ e->callback_function = json_callback_print;
105
+ sprintf(txt,"ARRAY[%lu]", e->data.items);
106
+ buffer_strcat(wb, txt);
107
+ break;
108
+
109
+ case JSON_STRING:
110
+ buffer_strcat(wb, e->data.string);
111
+ break;
112
+
113
+ case JSON_NUMBER:
114
+ sprintf(txt,"%Lf", e->data.number);
115
+ buffer_strcat(wb,txt);
116
+
117
+ break;
118
+
119
+ case JSON_BOOLEAN:
120
+ buffer_strcat(wb, e->data.boolean?"TRUE":"FALSE");
121
+ break;
122
+
123
+ case JSON_NULL:
124
+ buffer_strcat(wb,"NULL");
125
+ break;
126
+ }
127
+ info("JSON: %s", buffer_tostring(wb));
128
+ buffer_free(wb);
129
+ return 0;
130
+}
131
+
132
+/**
133
+ * JSONC Set String
134
+ *
135
+ * Set the string value of the structure JSON_ENTRY.
136
+ *
137
+ * @param e the output structure
138
+ */
139
+static inline void json_jsonc_set_string(JSON_ENTRY *e,char *key,const char *value) {
140
+ size_t length = strlen(key);
141
+ e->type = JSON_STRING;
142
+ memcpy(e->name,key,length);
143
+ e->name[length] = 0x00;
144
+ e->data.string = (char *) value;
145
+}
146
+
147
+
148
+#ifdef ENABLE_JSONC
149
+/**
150
+ * JSONC set Boolean
151
+ *
152
+ * Set the boolean value of the structure JSON_ENTRY
153
+ *
154
+ * @param e the output structure
155
+ * @param value the input value
156
+ */
157
+static inline void json_jsonc_set_boolean(JSON_ENTRY *e,int value) {
158
+ e->type = JSON_BOOLEAN;
159
+ e->data.boolean = value;
160
+}
161
+
162
+/**
163
+ * Parse Array
164
+ *
165
+ * Parse the array object.
166
+ *
167
+ * @param ptr the pointer for the object that we will parse.
168
+ * @param callback_data additional data to be used together the callback function
169
+ * @param callback_function function used to create a silencer.
170
+ */
171
+static inline void json_jsonc_parse_array(json_object *ptr, void *callback_data,int (*callback_function)(struct json_entry *)) {
172
+ int end = json_object_array_length(ptr);
173
+ JSON_ENTRY e;
174
+
175
+ if(end) {
176
+ int i;
177
+ i = 0;
178
+
179
+ enum json_type type;
180
+ do {
181
+ json_object *jvalue = json_object_array_get_idx(ptr, i);
182
+ if(jvalue) {
183
+ e.callback_data = callback_data;
184
+ e.type = JSON_OBJECT;
185
+ callback_function(&e);
186
+ json_object_object_foreach(jvalue, key, val) {
187
+ type = json_object_get_type(val);
188
+ if (type == json_type_array) {
189
+ e.type = JSON_ARRAY;
190
+ json_jsonc_parse_array(val, callback_data, callback_function);
191
+ } else if (type == json_type_object) {
192
+ json_walk(val,callback_data,callback_function);
193
+ } else if (type == json_type_string) {
194
+ json_jsonc_set_string(&e,key,json_object_get_string(val));
195
+ callback_function(&e);
196
+ } else if (type == json_type_boolean) {
197
+ json_jsonc_set_boolean(&e,json_object_get_boolean(val));
198
+ callback_function(&e);
199
+ }
200
+ }
201
+ }
202
+
203
+ } while (++i < end);
204
+ }
205
+}
206
+#else
207
+
208
+/**
209
+ * Walk string
210
+ *
211
+ * Set JSON_ENTRY to string and map the values from jsmntok_t.
212
+ *
213
+ * @param js the original string
214
+ * @param t the tokens
215
+ * @param start the first position
216
+ * @param e the output structure.
217
+ *
218
+ * @return It always return 1
219
+ */
220
+size_t json_walk_string(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e)
221
+{
222
+ char old = js[t[start].end];
223
+ js[t[start].end] = '\0';
224
+ e->original_string = &js[t[start].start];
225
+
226
+ e->type = JSON_STRING;
227
+ e->data.string = e->original_string;
228
+ if(e->callback_function) e->callback_function(e);
229
+ js[t[start].end] = old;
230
+ return 1;
231
+}
232
+
233
+/**
234
+ * Walk Primitive
235
+ *
236
+ * Define the data type of the string
237
+ *
238
+ * @param js the original string
239
+ * @param t the tokens
240
+ * @param start the first position
241
+ * @param e the output structure.
242
+ *
243
+ * @return It always return 1
244
+ */
245
+size_t json_walk_primitive(char *js, jsmntok_t *t, size_t start, JSON_ENTRY *e)
246
+{
247
+ char old = js[t[start].end];
248
+ js[t[start].end] = '\0';
249
+ e->original_string = &js[t[start].start];
250
+
251
+ switch(e->original_string[0]) {
252
+ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7':
253
+ case '8': case '9': case '-': case '.':
254
+ e->type = JSON_NUMBER;
255
+ e->data.number = strtold(e->original_string, NULL);
256
+ break;
257
+
258
+ case 't': case 'T':
259
+ e->type = JSON_BOOLEAN;
260
+ e->data.boolean = 1;
261
+ break;
262
+
263
+ case 'f': case 'F':
264
+ e->type = JSON_BOOLEAN;
265
+ e->data.boolean = 0;
266
+ break;
267
+
268
+ case 'n': case 'N':
269
+ default:
270
+ e->type = JSON_NULL;
271
+ break;
272
+ }
273
+ if(e->callback_function) e->callback_function(e);
274
+ js[t[start].end] = old;
275
+ return 1;
276
+}
277
+
278
+/**
279
+ * Array
280
+ *
281
+ * Measure the array length
282
+ *
283
+ * @param js the original string
284
+ * @param t the tokens
285
+ * @param nest the length of structure t
286
+ * @param start the first position
287
+ * @param e the output structure.
288
+ *
289
+ * @return It returns the array length
290
+ */
291
+size_t json_walk_array(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e)
292
+{
293
+ JSON_ENTRY ne = {
294
+ .name = "",
295
+ .fullname = "",
296
+ .callback_data = NULL,
297
+ .callback_function = NULL
298
+ };
299
+
300
+ char old = js[t[start].end];
301
+ js[t[start].end] = '\0';
302
+ ne.original_string = &js[t[start].start];
303
+
304
+ memcpy(&ne, e, sizeof(JSON_ENTRY));
305
+ ne.type = JSON_ARRAY;
306
+ ne.data.items = t[start].size;
307
+ ne.callback_function = NULL;
308
+ ne.name[0]='\0';
309
+ ne.fullname[0]='\0';
310
+ if(e->callback_function) e->callback_function(&ne);
311
+ js[t[start].end] = old;
312
+
313
+ size_t i, init = start, size = t[start].size;
314
+
315
+ start++;
316
+ for(i = 0; i < size ; i++) {
317
+ ne.pos = i;
318
+ if (!e->name || !e->fullname || strlen(e->name) > JSON_NAME_LEN - 24 || strlen(e->fullname) > JSON_FULLNAME_LEN -24) {
319
+ info("JSON: JSON walk_array ignoring element with name:%s fullname:%s",e->name, e->fullname);
320
+ continue;
321
+ }
322
+ sprintf(ne.name, "%s[%lu]", e->name, i);
323
+ sprintf(ne.fullname, "%s[%lu]", e->fullname, i);
324
+
325
+ switch(t[start].type) {
326
+ case JSMN_PRIMITIVE:
327
+ start += json_walk_primitive(js, t, start, &ne);
328
+ break;
329
+
330
+ case JSMN_OBJECT:
331
+ start += json_walk_object(js, t, nest + 1, start, &ne);
332
+ break;
333
+
334
+ case JSMN_ARRAY:
335
+ start += json_walk_array(js, t, nest + 1, start, &ne);
336
+ break;
337
+
338
+ case JSMN_STRING:
339
+ start += json_walk_string(js, t, start, &ne);
340
+ break;
341
+ }
342
+ }
343
+ return start - init;
344
+}
345
+
346
+/**
347
+ * Object
348
+ *
349
+ * Measure the Object length
350
+ *
351
+ * @param js the original string
352
+ * @param t the tokens
353
+ * @param nest the length of structure t
354
+ * @param start the first position
355
+ * @param e the output structure.
356
+ *
357
+ * @return It returns the Object length
358
+ */
359
+size_t json_walk_object(char *js, jsmntok_t *t, size_t nest, size_t start, JSON_ENTRY *e)
360
+{
361
+ JSON_ENTRY ne = {
362
+ .name = "",
363
+ .fullname = "",
364
+ .callback_data = NULL,
365
+ .callback_function = NULL
366
+ };
367
+
368
+ char old = js[t[start].end];
369
+ js[t[start].end] = '\0';
370
+ ne.original_string = &js[t[start].start];
371
+ memcpy(&ne, e, sizeof(JSON_ENTRY));
372
+ ne.type = JSON_OBJECT;
373
+ ne.callback_function = NULL;
374
+ if(e->callback_function) e->callback_function(&ne);
375
+ js[t[start].end] = old;
376
+
377
+ int key = 1;
378
+ size_t i, init = start, size = t[start].size;
379
+
380
+ start++;
381
+ for(i = 0; i < size ; i++) {
382
+ switch(t[start].type) {
383
+ case JSMN_PRIMITIVE:
384
+ start += json_walk_primitive(js, t, start, &ne);
385
+ key = 1;
386
+ break;
387
+
388
+ case JSMN_OBJECT:
389
+ start += json_walk_object(js, t, nest + 1, start, &ne);
390
+ key = 1;
391
+ break;
392
+
393
+ case JSMN_ARRAY:
394
+ start += json_walk_array(js, t, nest + 1, start, &ne);
395
+ key = 1;
396
+ break;
397
+
398
+ case JSMN_STRING:
399
+ default:
400
+ if(key) {
401
+ int len = t[start].end - t[start].start;
402
+ if (unlikely(len>JSON_NAME_LEN)) len=JSON_NAME_LEN;
403
+ strncpy(ne.name, &js[t[start].start], len);
404
+ ne.name[len] = '\0';
405
+ len=strlen(e->fullname) + strlen(e->fullname[0]?".":"") + strlen(ne.name);
406
+ char *c = mallocz((len+1)*sizeof(char));
407
+ sprintf(c,"%s%s%s", e->fullname, e->fullname[0]?".":"", ne.name);
408
+ if (unlikely(len>JSON_FULLNAME_LEN)) len=JSON_FULLNAME_LEN;
409
+ strncpy(ne.fullname, c, len);
410
+ freez(c);
411
+ start++;
412
+ key = 0;
413
+ }
414
+ else {
415
+ start += json_walk_string(js, t, start, &ne);
416
+ key = 1;
417
+ }
418
+ break;
419
+ }
420
+ }
421
+ return start - init;
422
+}
423
+#endif
424
+
425
+/**
426
+ * Tree
427
+ *
428
+ * Call the correct walk function according its type.
429
+ *
430
+ * @param t the json object to work
431
+ * @param callback_data additional data to be used together the callback function
432
+ * @param callback_function function used to create a silencer.
433
+ *
434
+ * @return It always return 1
435
+ */
436
+#ifdef ENABLE_JSONC
437
+size_t json_walk(json_object *t, void *callback_data, int (*callback_function)(struct json_entry *)) {
438
+ JSON_ENTRY e;
439
+
440
+ e.callback_data = callback_data;
441
+ enum json_type type;
442
+ json_object_object_foreach(t, key, val) {
443
+ type = json_object_get_type(val);
444
+ if (type == json_type_array) {
445
+ e.type = JSON_ARRAY;
446
+ json_jsonc_parse_array(val,NULL,health_silencers_json_read_callback);
447
+ } else if (type == json_type_object) {
448
+ e.type = JSON_OBJECT;
449
+ } else if (type == json_type_string) {
450
+ json_jsonc_set_string(&e,key,json_object_get_string(val));
451
+ callback_function(&e);
452
+ } else if (type == json_type_boolean) {
453
+ json_jsonc_set_boolean(&e,json_object_get_boolean(val));
454
+ callback_function(&e);
455
+ }
456
+ }
457
+
458
+ return 1;
459
+}
460
+#else
461
+/**
462
+ * Tree
463
+ *
464
+ * Call the correct walk function according its type.
465
+ *
466
+ * @param js the original string
467
+ * @param t the tokens
468
+ * @param callback_data additional data to be used together the callback function
469
+ * @param callback_function function used to create a silencer.
470
+ *
471
+ * @return It always return 1
472
+ */
473
+size_t json_walk_tree(char *js, jsmntok_t *t, void *callback_data, int (*callback_function)(struct json_entry *))
474
+{
475
+ JSON_ENTRY e = {
476
+ .name = "",
477
+ .fullname = "",
478
+ .callback_data = callback_data,
479
+ .callback_function = callback_function
480
+ };
481
+
482
+ switch (t[0].type) {
483
+ case JSMN_OBJECT:
484
+ e.type = JSON_OBJECT;
485
+ json_walk_object(js, t, 0, 0, &e);
486
+ break;
487
+
488
+ case JSMN_ARRAY:
489
+ e.type = JSON_ARRAY;
490
+ json_walk_array(js, t, 0, 0, &e);
491
+ break;
492
+
493
+ case JSMN_PRIMITIVE:
494
+ case JSMN_STRING:
495
+ break;
496
+ }
497
+
498
+ return 1;
499
+}
500
+#endif
501
+
502
+/**
503
+ * JSON Parse
504
+ *
505
+ * Parse the json message with the callback function
506
+ *
507
+ * @param js the string that the callback function will parse
508
+ * @param callback_data additional data to be used together the callback function
509
+ * @param callback_function function used to create a silencer.
510
+ *
511
+ * @return JSON_OK case everything happend as expected, JSON_CANNOT_PARSE case there were errors in the
512
+ * parsing procces and JSON_CANNOT_DOWNLOAD case the string given(js) is NULL.
513
+ */
514
+int json_parse(char *js, void *callback_data, int (*callback_function)(JSON_ENTRY *))
515
+{
516
+ if(js) {
517
+#ifdef ENABLE_JSONC
518
+ json_object *tokens = json_tokenise(js);
519
+#else
520
+ size_t count;
521
+ jsmntok_t *tokens = json_tokenise(js, strlen(js), &count);
522
+#endif
523
+
524
+ if(tokens) {
525
+#ifdef ENABLE_JSONC
526
+ json_walk(tokens, callback_data, callback_function);
527
+ json_object_put(tokens);
528
+#else
529
+ json_walk_tree(js, tokens, callback_data, callback_function);
530
+ freez(tokens);
531
+#endif
532
+ return JSON_OK;
533
+ }
534
+
535
+ return JSON_CANNOT_PARSE;
536
+ }
537
+
538
+ return JSON_CANNOT_DOWNLOAD;
539
+}
540
+
541
+/*
542
+int json_test(char *str)
543
+{
544
+ return json_parse(str, NULL, json_callback_print);
545
+}
546
+ */
\ No newline at end of file