Asterisk - The Open Source Telephony Project GIT-master-a358458
Macros | Enumerations | Functions | Variables
func_json.c File Reference

JSON parsing function. More...

#include "asterisk.h"
#include "asterisk/module.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/utils.h"
#include "asterisk/test.h"
#include "asterisk/app.h"
#include "asterisk/conversions.h"
Include dependency graph for func_json.c:

Go to the source code of this file.

Macros

#define MAX_JSON_STACK   32
 

Enumerations

enum  json_option_flags { OPT_COUNT = (1 << 0) }
 

Functions

static void __init_result_buf (void)
 
 AST_MODULE_INFO_STANDARD_EXTENDED (ASTERISK_GPL_KEY, "JSON decoding function")
 
static int json_decode_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 
static int load_module (void)
 
static int parse_node (char **key, char *currentkey, char *nestchar, int count, struct ast_json *json, char *buf, size_t len, int *depth)
 
static int unload_module (void)
 

Variables

static struct ast_custom_function json_decode_function
 
static const struct ast_app_option json_options [128] = { [ 'c' ] = { .flag = OPT_COUNT }, }
 
static struct ast_threadstorage result_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_result_buf , .custom_init = NULL , }
 

Detailed Description

JSON parsing function.

Author
Naveen Albert aster.nosp@m.isk@.nosp@m.phrea.nosp@m.knet.nosp@m..org

Definition in file func_json.c.

Macro Definition Documentation

◆ MAX_JSON_STACK

#define MAX_JSON_STACK   32

Definition at line 95 of file func_json.c.

Enumeration Type Documentation

◆ json_option_flags

Enumerator
OPT_COUNT 

Definition at line 87 of file func_json.c.

87 {
88 OPT_COUNT = (1 << 0),
89};
@ OPT_COUNT
Definition: func_json.c:88

Function Documentation

◆ __init_result_buf()

static void __init_result_buf ( void  )
static

Definition at line 85 of file func_json.c.

87{

◆ AST_MODULE_INFO_STANDARD_EXTENDED()

AST_MODULE_INFO_STANDARD_EXTENDED ( ASTERISK_GPL_KEY  ,
"JSON decoding function"   
)

◆ json_decode_read()

static int json_decode_read ( struct ast_channel chan,
const char *  cmd,
char *  data,
char *  buf,
size_t  len 
)
static

Definition at line 195 of file func_json.c.

196{
197 int count = 0;
198 struct ast_flags flags = {0};
199 struct ast_json *json = NULL, *start = NULL;
200 char *nestchar = "."; /* default delimeter for nesting key indexing is . */
201 int index, res, depth = 0;
202
204 AST_APP_ARG(varname);
205 AST_APP_ARG(key);
206 AST_APP_ARG(nestchar);
208 );
209 char *varsubst, *key, *currentkey, *nextkey, *firstkey, *tmp;
211
213
214 if (!ast_strlen_zero(args.options)) {
215 ast_app_parse_options(json_options, &flags, NULL, args.options);
216 if (ast_test_flag(&flags, OPT_COUNT)) {
217 count = 1;
218 }
219 }
220
221 if (ast_strlen_zero(args.varname)) {
222 ast_log(LOG_WARNING, "%s requires a variable name\n", cmd);
223 return -1;
224 }
225
226 if (ast_strlen_zero(args.key)) {
227 ast_log(LOG_WARNING, "%s requires a key\n", cmd);
228 return -1;
229 }
230
231 key = ast_strdupa(args.key);
232 if (!ast_strlen_zero(args.nestchar)) {
233 int seplen = strlen(args.nestchar);
234 if (seplen != 1) {
235 ast_log(LOG_WARNING, "Nesting separator '%s' has length %d and is invalid (must be a single character)\n", args.nestchar, seplen);
236 } else {
237 nestchar = args.nestchar;
238 }
239 }
240
241 varsubst = ast_alloca(strlen(args.varname) + 4); /* +4 for ${} and null terminator */
242 if (!varsubst) {
243 ast_log(LOG_ERROR, "Failed to allocate string\n");
244 return -1;
245 }
246 sprintf(varsubst, "${%s}", args.varname); /* safe, because of the above allocation */
247 ast_str_substitute_variables(&str, 0, chan, varsubst);
248
249 ast_debug(1, "Parsing JSON using nesting delimeter '%s'\n", nestchar);
250
251 if (ast_str_strlen(str) == 0) {
252 ast_debug(1, "Variable '%s' contains no data, nothing to search!\n", args.varname);
253 return -1; /* empty json string */
254 }
255
256 /* allow for multiple key nesting */
257 currentkey = key;
258 firstkey = ast_strdupa(currentkey);
259 tmp = strstr(firstkey, nestchar);
260 if (tmp) {
261 *tmp = '\0';
262 }
263
264 /* parse a string as JSON */
265 ast_debug(1, "Parsing JSON: %s (key: '%s')\n", ast_str_buffer(str), currentkey);
266 if (ast_strlen_zero(currentkey)) {
267 ast_debug(1, "Empty JSON key\n");
268 return -1;
269 }
270 if (ast_str_strlen(str) == 0) {
271 ast_debug(1, "JSON node '%s', contains no data, nothing to search!\n", currentkey);
272 return -1; /* empty json string */
273 }
274
275 json = ast_json_load_str(str, NULL);
276 if (!json) {
277 ast_log(LOG_WARNING, "Failed to parse as JSON: %s\n", ast_str_buffer(str));
278 return -1;
279 }
280
281 /* parse the JSON object, potentially recursively */
282 nextkey = strsep(&key, nestchar);
283 if (ast_json_is_object(json)) {
284 start = ast_json_object_get(json, firstkey);
285 } else {
286 if (ast_str_to_int(currentkey, &index)) {
287 ast_debug(1, "Requested index '%s' is not numeric or is invalid\n", currentkey);
288 return -1;
289 }
290 start = ast_json_array_get(json, index);
291 }
292
293 res = parse_node(&key, nextkey, nestchar, count, start, buf, len, &depth);
294 ast_json_unref(json);
295 return res;
296}
const char * str
Definition: app_jack.c:147
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
static int tmp()
Definition: bt_open.c:389
int ast_str_to_int(const char *str, int *res)
Convert the given string to a signed integer.
Definition: conversions.c:44
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static struct ast_threadstorage result_buf
Definition: func_json.c:85
static const struct ast_app_option json_options[128]
Definition: func_json.c:93
static int parse_node(char **key, char *currentkey, char *nestchar, int count, struct ast_json *json, char *buf, size_t len, int *depth)
Definition: func_json.c:97
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3056
char * strsep(char **str, const char *delims)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_array_get(const struct ast_json *array, size_t index)
Get an element from an array.
Definition: json.c:370
struct ast_json * ast_json_load_str(const struct ast_str *input, struct ast_json_error *error)
Parse ast_str into a JSON object or array.
Definition: json.c:580
struct ast_json * ast_json_object_get(struct ast_json *object, const char *key)
Get a field from a JSON object.
Definition: json.c:407
int ast_json_is_object(const struct ast_json *value)
Check if value is JSON object.
Definition: json.c:258
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
#define NULL
Definition: resample.c:96
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Abstract JSON element (object, array, string, int, ...).
Support for dynamic strings.
Definition: strings.h:623
const char * args
static struct test_options options
#define ast_test_flag(p, flag)
Definition: utils.h:63

References args, ast_alloca, AST_APP_ARG, ast_app_parse_options(), ast_debug, AST_DECLARE_APP_ARGS, ast_json_array_get(), ast_json_is_object(), ast_json_load_str(), ast_json_object_get(), ast_json_unref(), ast_log, AST_STANDARD_APP_ARGS, ast_str_buffer(), ast_str_strlen(), ast_str_substitute_variables(), ast_str_thread_get(), ast_str_to_int(), ast_strdupa, ast_strlen_zero(), ast_test_flag, buf, ast_flags::flags, json_options, len(), LOG_ERROR, LOG_WARNING, NULL, OPT_COUNT, options, parse_node(), result_buf, str, strsep(), and tmp().

◆ load_module()

static int load_module ( void  )
static

Definition at line 407 of file func_json.c.

408{
409 int res;
410
411#ifdef TEST_FRAMEWORK
412 AST_TEST_REGISTER(test_JSON_DECODE);
413#endif
415
416 return res;
417}
static struct ast_custom_function json_decode_function
Definition: func_json.c:298
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
#define AST_TEST_REGISTER(cb)
Definition: test.h:127

References ast_custom_function_register, AST_TEST_REGISTER, and json_decode_function.

◆ parse_node()

static int parse_node ( char **  key,
char *  currentkey,
char *  nestchar,
int  count,
struct ast_json json,
char *  buf,
size_t  len,
int *  depth 
)
static

Definition at line 97 of file func_json.c.

98{
99 const char *result = NULL;
100 char *previouskey;
101 struct ast_json *jsonval = json;
102
103 /* Prevent a huge JSON string from blowing the stack. */
104 (*depth)++;
105 if (*depth > MAX_JSON_STACK) {
106 ast_log(LOG_WARNING, "Max JSON stack (%d) exceeded\n", MAX_JSON_STACK);
107 return -1;
108 }
109
110 snprintf(buf, len, "%s", ""); /* clear the buffer from previous round if necessary */
111 if (!json) { /* no error or warning should be thrown */
112 ast_debug(1, "Could not find key '%s' in parsed JSON\n", currentkey);
113 return -1;
114 }
115
116 switch(ast_json_typeof(jsonval)) {
117 unsigned long int size;
118 int r;
119 double d;
120
121 case AST_JSON_STRING:
122 result = ast_json_string_get(jsonval);
123 ast_debug(1, "Got JSON string: %s\n", result);
125 break;
126 case AST_JSON_INTEGER:
127 r = ast_json_integer_get(jsonval);
128 ast_debug(1, "Got JSON integer: %d\n", r);
129 snprintf(buf, len, "%d", r); /* the snprintf below is mutually exclusive with this one */
130 break;
131 case AST_JSON_REAL:
132 d = ast_json_real_get(jsonval);
133 ast_debug(1, "Got JSON real: %.17g\n", d);
134 snprintf(buf, len, "%.17g", d); /* the snprintf below is mutually exclusive with this one */
135 break;
136 case AST_JSON_ARRAY:
137 ast_debug(1, "Got JSON array\n");
138 previouskey = currentkey;
139 currentkey = strsep(key, nestchar); /* retrieve the desired index */
140 size = ast_json_array_size(jsonval);
141 ast_debug(1, "Parsed JSON array of size %lu, key: %s\n", size, currentkey);
142 if (!currentkey) { /* this is the end, so just dump the array */
143 if (count) {
144 ast_debug(1, "No key on which to index in the array, so returning count: %lu\n", size);
145 snprintf(buf, len, "%lu", size);
146 return 0;
147 } else {
148 char *result2 = ast_json_dump_string(jsonval);
149 ast_debug(1, "No key on which to index in the array, so dumping '%s' array\n", previouskey);
150 ast_copy_string(buf, result2, len);
151 ast_json_free(result2);
152 }
153 } else if (ast_str_to_int(currentkey, &r) || r < 0) {
154 ast_debug(1, "Requested index '%s' is not numeric or is invalid\n", currentkey);
155 } else if (r >= size) {
156 ast_debug(1, "Requested index '%d' does not exist in parsed array\n", r);
157 } else {
158 ast_debug(1, "Recursing on index %d in array\n", r);
159 if (parse_node(key, currentkey, nestchar, count, ast_json_array_get(jsonval, r), buf, len, depth)) { /* recurse on this node */
160 return -1;
161 }
162 }
163 break;
164 case AST_JSON_TRUE:
165 case AST_JSON_FALSE:
166 r = ast_json_is_true(jsonval);
167 ast_debug(1, "Got JSON %s for key %s\n", r ? "true" : "false", currentkey);
168 snprintf(buf, len, "%d", r); /* the snprintf below is mutually exclusive with this one */
169 break;
170 case AST_JSON_NULL:
171 ast_debug(1, "Got JSON null for key %s\n", currentkey);
172 break;
173 case AST_JSON_OBJECT:
174 ast_debug(1, "Got generic JSON object for key %s\n", currentkey);
175 previouskey = currentkey;
176 currentkey = strsep(key, nestchar); /* retrieve the desired index */
177 if (!currentkey) { /* this is the end, so just dump the object */
178 char *result2 = ast_json_dump_string(jsonval);
179 ast_copy_string(buf, result2, len);
180 ast_json_free(result2);
181 } else {
182 ast_debug(1, "Recursing on object (key was '%s' and is now '%s')\n", previouskey, currentkey);
183 if (parse_node(key, currentkey, nestchar, count, ast_json_object_get(jsonval, currentkey), buf, len, depth)) { /* recurse on this node */
184 return -1;
185 }
186 }
187 break;
188 default:
189 ast_log(LOG_WARNING, "Got unsuported type %d\n", ast_json_typeof(jsonval));
190 return -1;
191 }
192 return 0;
193}
static PGresult * result
Definition: cel_pgsql.c:84
#define MAX_JSON_STACK
Definition: func_json.c:95
enum ast_json_type ast_json_typeof(const struct ast_json *value)
Get the type of value.
Definition: json.c:78
void ast_json_free(void *p)
Asterisk's custom JSON allocator. Exposed for use by unit tests.
Definition: json.c:52
#define ast_json_dump_string(root)
Encode a JSON value to a compact string.
Definition: json.h:810
@ AST_JSON_STRING
Definition: json.h:166
@ AST_JSON_ARRAY
Definition: json.h:165
@ AST_JSON_NULL
Definition: json.h:171
@ AST_JSON_OBJECT
Definition: json.h:164
@ AST_JSON_FALSE
Definition: json.h:170
@ AST_JSON_REAL
Definition: json.h:168
@ AST_JSON_INTEGER
Definition: json.h:167
@ AST_JSON_TRUE
Definition: json.h:169
const char * ast_json_string_get(const struct ast_json *string)
Get the value of a JSON string.
Definition: json.c:283
intmax_t ast_json_integer_get(const struct ast_json *integer)
Get the value from a JSON integer.
Definition: json.c:332
int ast_json_is_true(const struct ast_json *value)
Check if value is JSON true.
Definition: json.c:263
size_t ast_json_array_size(const struct ast_json *array)
Get the size of a JSON array.
Definition: json.c:366
double ast_json_real_get(const struct ast_json *real)
Get the value from a JSON real number.
Definition: json.c:347
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
static struct test_val d

References ast_copy_string(), ast_debug, AST_JSON_ARRAY, ast_json_array_get(), ast_json_array_size(), ast_json_dump_string, AST_JSON_FALSE, ast_json_free(), AST_JSON_INTEGER, ast_json_integer_get(), ast_json_is_true(), AST_JSON_NULL, AST_JSON_OBJECT, ast_json_object_get(), AST_JSON_REAL, ast_json_real_get(), AST_JSON_STRING, ast_json_string_get(), AST_JSON_TRUE, ast_json_typeof(), ast_log, ast_str_to_int(), buf, d, len(), LOG_WARNING, MAX_JSON_STACK, NULL, parse_node(), result, and strsep().

Referenced by json_decode_read(), and parse_node().

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 395 of file func_json.c.

396{
397 int res;
398
399#ifdef TEST_FRAMEWORK
400 AST_TEST_UNREGISTER(test_JSON_DECODE);
401#endif
403
404 return res;
405}
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128

References ast_custom_function_unregister(), AST_TEST_UNREGISTER, and json_decode_function.

Variable Documentation

◆ json_decode_function

struct ast_custom_function json_decode_function
static
Initial value:
= {
.name = "JSON_DECODE",
}
static int json_decode_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_json.c:195

Definition at line 298 of file func_json.c.

Referenced by load_module(), and unload_module().

◆ json_options

const struct ast_app_option json_options[128] = { [ 'c' ] = { .flag = OPT_COUNT }, }
static

Definition at line 93 of file func_json.c.

Referenced by json_decode_read().

◆ result_buf

struct ast_threadstorage result_buf = { .once = PTHREAD_ONCE_INIT , .key_init = __init_result_buf , .custom_init = NULL , }
static

Definition at line 85 of file func_json.c.

Referenced by json_decode_read().