Browse Source

Merge pull request #1404 from kamailio/json-transformation

json: add transformation
Daniel-Constantin Mierla 7 năm trước cách đây
mục cha
commit
9d8e957419

+ 26 - 0
src/modules/json/doc/json_admin.xml

@@ -74,5 +74,31 @@ xlog("foo is $var(foo)");
 	    </example>
 	</section>
 	</section>
+
+	<section>
+	<title>Transformations</title>
+	<para>You can use the transformation to extract values from the json structured pseudo-variables</para>
+	<itemizedlist>
+		<listitem>
+			<para>
+				<emphasis>json</emphasis>
+			</para>
+			<example>
+				<title>
+					<function>json.parse</function> usage
+				</title>
+				<programlisting format="linespecific">
+	...
+	# extract value of "Custom-Data" from $rb pseudo-variable and set it to $var(Custom-Data)
+	$var(Custom-Data) = $(rb{json.parse,Custom-Data});
+	if($var(Custom-Data) != $null) {	xlog("L_INFO", "$ci|log|custom data: $var(Custom-Data) from Request: $rb");
+	}
+	...
+				</programlisting>
+			</example>
+		</listitem>
+	</itemizedlist>
+	</section>
+</section>
 </chapter>
 

+ 283 - 0
src/modules/json/json_funcs.c

@@ -76,3 +76,286 @@ int json_get_field(struct sip_msg* msg, char* json, char* field, char* dst)
 	json_object_put(j);
 	return ret;
 }
+
+#define json_foreach_key(obj, key)                                        \
+	char *key;                                                            \
+	struct lh_entry *entry##key;                                          \
+	struct lh_entry *entry_next##key = NULL;                              \
+	for(entry##key = json_object_get_object(obj)->head;                   \
+			(entry##key ? (key = (char *)entry##key->k,                   \
+						  entry_next##key = entry##key->next, entry##key) \
+						: 0);                                             \
+			entry##key = entry_next##key)
+
+
+static str json_pv_str_empty = {"", 0};
+
+char **str_split(char *a_str, const char a_delim)
+{
+	char **result = 0;
+	size_t count = 0;
+	char *tmp = a_str;
+	char *last_comma = 0;
+	char delim[2];
+	delim[0] = a_delim;
+	delim[1] = 0;
+	int len = 0;
+
+	/* Count how many elements will be extracted. */
+	while(*tmp) {
+		if(a_delim == *tmp) {
+			count++;
+			last_comma = tmp;
+		}
+		tmp++;
+	}
+
+	/* Add space for trailing token. */
+	count += last_comma < (a_str + strlen(a_str) - 1);
+
+	/* Add space for terminating null string so caller
+	   knows where the list of returned strings ends. */
+	count++;
+
+	result = pkg_malloc(sizeof(char *) * count);
+
+	if(result) {
+		size_t idx = 0;
+		char *token = strtok(a_str, delim);
+
+		while(token) {
+			assert(idx < count);
+			len = strlen(token);
+			char *ptr = pkg_malloc((len + 1) * sizeof(char));
+			*(result + idx) = ptr;
+			memcpy(ptr, token, len);
+			ptr[len] = '\0';
+			int i = 0;
+			while(i < len) {
+				if(ptr[i] == tr_json_escape_char)
+					ptr[i] = '.';
+				i++;
+			}
+			token = strtok(0, delim);
+			idx++;
+		}
+		assert(idx == count - 1);
+		*(result + idx) = 0;
+	}
+
+	return result;
+}
+
+struct json_object *tr_json_get_field_object(str *json, str *field)
+{
+	char **tokens;
+	char *dup;
+	char f1[25], f2[25]; //, f3[25];
+	int i;
+
+	dup = pkg_malloc(json->len + 1);
+	memcpy(dup, json->s, json->len);
+	dup[json->len] = '\0';
+	struct json_object *j = json_tokener_parse(dup);
+	pkg_free(dup);
+
+	if(is_error(j)) {
+		LM_ERR("empty or invalid JSON\n");
+		return NULL;
+	}
+
+	struct json_object *jtree = NULL;
+	struct json_object *ret = NULL;
+
+	LM_DBG("getting json %.*s\n", field->len, field->s);
+
+	dup = pkg_malloc(field->len + 1);
+	memcpy(dup, field->s, field->len);
+	dup[field->len] = '\0';
+	tokens = str_split(dup, '.');
+	pkg_free(dup);
+
+	if(tokens) {
+		jtree = j;
+		for(i = 0; *(tokens + i); i++) {
+			if(jtree != NULL) {
+				str field = str_init(*(tokens + i));
+				// check for idx []
+				int sresult = sscanf(field.s, "%[^[][%[^]]]", f1, f2); //, f3);
+				LM_DBG("CHECK IDX %d - %s , %s, %s\n", sresult, field.s, f1,
+						(sresult > 1 ? f2 : "(null)"));
+
+				jtree = json_get_object(jtree, f1);
+				if(jtree != NULL) {
+					char *value = (char *)json_object_get_string(jtree);
+					LM_DBG("JTREE OK %s\n", value);
+				}
+				if(jtree != NULL && sresult > 1
+						&& json_object_is_type(jtree, json_type_array)) {
+					int idx = atoi(f2);
+					jtree = json_object_array_get_idx(jtree, idx);
+					if(jtree != NULL) {
+						char *value = (char *)json_object_get_string(jtree);
+						LM_DBG("JTREE IDX OK %s\n", value);
+					}
+				}
+			}
+			pkg_free(*(tokens + i));
+		}
+		pkg_free(tokens);
+	}
+
+
+	if(jtree != NULL)
+		ret = json_object_get(jtree);
+
+	json_object_put(j);
+
+	return ret;
+}
+
+
+int tr_json_get_field_ex(str *json, str *field, pv_value_p dst_val)
+{
+	struct json_object *jtree = tr_json_get_field_object(json, field);
+
+
+	if(jtree != NULL) {
+		char *value = (char *)json_object_get_string(jtree);
+		int len = strlen(value);
+		dst_val->rs.s = pkg_malloc(len + 1);
+		memcpy(dst_val->rs.s, value, len);
+		dst_val->rs.s[len] = '\0';
+		dst_val->rs.len = len;
+		dst_val->flags = PV_VAL_STR | PV_VAL_PKG;
+		dst_val->ri = 0;
+		json_object_put(jtree);
+	} else {
+		dst_val->flags = PV_VAL_NULL;
+		dst_val->rs = json_pv_str_empty;
+		dst_val->ri = 0;
+	}
+	return 1;
+}
+
+
+int tr_json_get_field(struct sip_msg *msg, char *json, char *field, char *dst)
+{
+	str json_s;
+	str field_s;
+	pv_spec_t *dst_pv;
+	pv_value_t dst_val;
+
+	if(fixup_get_svalue(msg, (gparam_p)json, &json_s) != 0) {
+		LM_ERR("cannot get json string value\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_p)field, &field_s) != 0) {
+		LM_ERR("cannot get field string value\n");
+		return -1;
+	}
+
+	if(tr_json_get_field_ex(&json_s, &field_s, &dst_val) != 1)
+		return -1;
+
+	dst_pv = (pv_spec_t *)dst;
+	dst_pv->setf(msg, &dst_pv->pvp, (int)EQ_T, &dst_val);
+	if(dst_val.flags & PV_VAL_PKG) {
+		pkg_free(dst_val.rs.s);
+	} else if(dst_val.flags & PV_VAL_SHM) {
+		shm_free(dst_val.rs.s);
+	}
+
+	return 1;
+}
+
+struct json_object *json_parse(const char *str)
+{
+	struct json_tokener *tok;
+	struct json_object *obj;
+
+	tok = json_tokener_new();
+	if(!tok) {
+		LM_ERR("Error parsing json: could not allocate tokener\n");
+		return NULL;
+	}
+
+	obj = json_tokener_parse_ex(tok, str, -1);
+	if(tok->err != json_tokener_success) {
+		LM_ERR("Error parsing json: %s\n", json_tokener_error_desc(tok->err));
+		LM_ERR("%s\n", str);
+		if(obj != NULL) {
+			json_object_put(obj);
+		}
+		obj = NULL;
+	}
+
+	json_tokener_free(tok);
+	return obj;
+}
+
+struct json_object *json_get_object(
+		struct json_object *jso, const char *key)
+{
+	struct json_object *result = NULL;
+	json_object_object_get_ex(jso, key, &result);
+	return result;
+}
+
+int tr_json_get_keys(struct sip_msg *msg, char *json, char *field, char *dst)
+{
+	str json_s;
+	str field_s;
+	int_str keys_avp_name;
+	unsigned short keys_avp_type;
+	pv_spec_t *avp_spec;
+
+	if(fixup_get_svalue(msg, (gparam_p)json, &json_s) != 0) {
+		LM_ERR("cannot get json string value\n");
+		return -1;
+	}
+
+	if(fixup_get_svalue(msg, (gparam_p)field, &field_s) != 0) {
+		LM_ERR("cannot get field string value\n");
+		return -1;
+	}
+
+	if(dst == NULL) {
+		LM_ERR("avp spec is null\n");
+		return -1;
+	}
+
+	avp_spec = (pv_spec_t *)dst;
+
+	if(avp_spec->type != PVT_AVP) {
+		LM_ERR("invalid avp spec\n");
+		return -1;
+	}
+
+	if(pv_get_avp_name(0, &avp_spec->pvp, &keys_avp_name, &keys_avp_type)
+			!= 0) {
+		LM_ERR("invalid AVP definition\n");
+		return -1;
+	}
+
+	struct json_object *jtree = tr_json_get_field_object(&json_s, &field_s);
+
+	if(jtree != NULL) {
+		json_foreach_key(jtree, k)
+		{
+			LM_DBG("ITERATING KEY %s\n", k);
+			int_str v1;
+			v1.s.s = k;
+			v1.s.len = strlen(k);
+			if(add_avp(AVP_VAL_STR | keys_avp_type, keys_avp_name, v1) < 0) {
+				LM_ERR("failed to create AVP\n");
+				json_object_put(jtree);
+				return -1;
+			}
+		}
+		json_object_put(jtree);
+	}
+
+	return 1;
+}

+ 27 - 0
src/modules/json/json_funcs.h

@@ -25,7 +25,34 @@
 #define _JSON_FUNCS_H_
 
 #include "../../core/parser/msg_parser.h"
+#include <json.h>
 
 int json_get_field(struct sip_msg* msg, char* json, char* field, char* dst);
 
+#define json_extract_field(json_name, field)                                \
+	do {                                                                    \
+		struct json_object *obj = json_get_object(json_obj, json_name); \
+		field.s = (char *)json_object_get_string(obj);                      \
+		if(field.s == NULL) {                                               \
+			LM_DBG("Json-c error - failed to extract field [%s]\n",         \
+					json_name);                                             \
+			field.s = "";                                                   \
+		} else {                                                            \
+			field.len = strlen(field.s);                                    \
+		}                                                                   \
+		LM_DBG("%s: [%s]\n", json_name, field.s ? field.s : "Empty");       \
+	} while(0);
+
+
+extern char tr_json_escape_char;
+extern str json_event_key;
+extern str json_event_sub_key;
+
+int tr_json_get_field(struct sip_msg *msg, char *json, char *field, char *dst);
+int tr_json_get_keys(struct sip_msg *msg, char *json, char *field, char *dst);
+
+struct json_object *json_parse(const char *str);
+struct json_object *json_get_object(
+		struct json_object *jso, const char *key);
+
 #endif

+ 28 - 12
src/modules/json/json_mod.c

@@ -28,34 +28,50 @@
 #include "../../core/sr_module.h"
 
 #include "json_funcs.h"
+#include "json_trans.h"
 
 MODULE_VERSION
 
 static int fixup_get_field(void** param, int param_no);
 static int fixup_get_field_free(void** param, int param_no);
+str tr_json_escape_str = str_init("%");
+char tr_json_escape_char = '%';
 
 /* Exported functions */
+static tr_export_t mod_trans[] = {
+		{{"json", sizeof("json") - 1}, json_tr_parse}, {{0, 0}, 0}};
+
 static cmd_export_t cmds[]={
 	{"json_get_field", (cmd_function)json_get_field, 3,
 		fixup_get_field, fixup_get_field_free, ANY_ROUTE},
 	{0, 0, 0, 0, 0, 0}
 };
 
+static param_export_t params[] = {
+		{"json_escape_char", PARAM_STR, &tr_json_escape_str}, {0, 0, 0}};
+
 struct module_exports exports = {
-		"json",
-		DEFAULT_DLFLAGS, /* dlopen flags */
-		cmds,			 /* Exported functions */
-		0,		 /* Exported parameters */
-		0,		 /* exported statistics */
-		0,	             /* exported MI functions */
-		0,				 /* exported pseudo-variables */
-		0,				 /* extra processes */
-		0,        /* module initialization function */
-		0,				 /* response function*/
-		0,	 /* destroy function */
-		0       /* per-child init function */
+		"json", DEFAULT_DLFLAGS, /* dlopen flags */
+		cmds,					 /* Exported functions */
+		params,					 /* Exported parameters */
+		0,						 /* exported statistics */
+		0,						 /* exported MI functions */
+		0,						 /* exported pseudo-variables */
+		0,						 /* extra processes */
+		0,						 /* module initialization function */
+		0,						 /* response function*/
+		0,						 /* destroy function */
+		0						 /* per-child init function */
 };
 
+int mod_register(char *path, int *dlflags, void *p1, void *p2)
+{
+	if(json_tr_init_buffers() < 0) {
+		LM_ERR("failed to initialize transformations buffers\n");
+		return -1;
+	}
+	return register_trans_mod(path, mod_trans);
+}
 
 static int fixup_get_field(void** param, int param_no)
 {

+ 451 - 0
src/modules/json/json_trans.c

@@ -0,0 +1,451 @@
+/*
+ * JSON module interface
+ *
+ * Copyright (C) 2010-2014 2600Hz
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Contributor(s):
+ * Emmanuel Schmidbauer <[email protected]>
+ *
+ */
+
+#include "../../core/trim.h"
+#include "../../core/mod_fix.h"
+
+#include "json_trans.h"
+
+/*! transformation buffer size */
+#define JSON_TR_BUFFER_SIZE 65536
+#define JSON_TR_BUFFER_SLOTS 4
+
+/*! transformation buffer */
+static char **_json_tr_buffer_list = NULL;
+
+static char *_json_tr_buffer = NULL;
+
+static int _json_tr_buffer_idx = 0;
+
+#define JSON_TR_ALLOC_PARSE_SIZE 2048
+
+static pv_spec_t **_json_parse_specs = NULL;
+static tr_param_t **_json_parse_params = NULL;
+static int _json_tr_parse_spec = 0;
+static int _json_tr_parse_params = 0;
+
+
+/*!
+ *
+ */
+int json_tr_init_buffers(void)
+{
+	int i;
+
+	_json_tr_buffer_list = (char **)malloc(JSON_TR_BUFFER_SLOTS * sizeof(char *));
+
+	if(_json_tr_buffer_list == NULL)
+		return -1;
+	for(i = 0; i < JSON_TR_BUFFER_SLOTS; i++) {
+		_json_tr_buffer_list[i] = (char *)malloc(JSON_TR_BUFFER_SIZE);
+		if(_json_tr_buffer_list[i] == NULL)
+			return -1;
+	}
+
+	_json_parse_specs =
+			(pv_spec_t **)malloc(JSON_TR_ALLOC_PARSE_SIZE * sizeof(pv_spec_t *));
+	for(i = 0; i < JSON_TR_ALLOC_PARSE_SIZE; i++)
+		_json_parse_specs[i] = NULL;
+
+	_json_parse_params = (tr_param_t **)malloc(
+			JSON_TR_ALLOC_PARSE_SIZE * sizeof(tr_param_t *));
+	for(i = 0; i < JSON_TR_ALLOC_PARSE_SIZE; i++)
+		_json_parse_params[i] = NULL;
+
+	return 0;
+}
+
+void json_tr_clear_buffers(void)
+{
+	int i;
+	if(_json_tr_buffer_list != NULL) {
+		for(i = 0; i < JSON_TR_BUFFER_SLOTS; i++) {
+			if(_json_tr_buffer_list[i] != NULL) {
+				free(_json_tr_buffer_list[i]);
+				_json_tr_buffer_list[i] = NULL;
+			}
+		}
+		free(_json_tr_buffer_list);
+		_json_tr_buffer_list = NULL;
+	}
+
+	if(_json_parse_specs != NULL) {
+		for(i = 0; i < JSON_TR_ALLOC_PARSE_SIZE; i++) {
+			if(_json_parse_specs[i] != NULL) {
+				free(_json_parse_specs[i]);
+				_json_parse_specs[i] = NULL;
+			}
+		}
+		free(_json_parse_specs);
+		_json_parse_specs = NULL;
+	}
+
+	if(_json_parse_params != NULL) {
+		for(i = 0; i < JSON_TR_ALLOC_PARSE_SIZE; i++) {
+			if(_json_parse_params[i] != NULL) {
+				free(_json_parse_params[i]);
+				_json_parse_params[i] = NULL;
+			}
+		}
+		free(_json_parse_params);
+		_json_parse_params = NULL;
+	}
+}
+
+char *json_tr_set_crt_buffer(void)
+{
+	_json_tr_buffer = _json_tr_buffer_list[_json_tr_buffer_idx];
+	_json_tr_buffer_idx = (_json_tr_buffer_idx + 1) % JSON_TR_BUFFER_SLOTS;
+	return _json_tr_buffer;
+}
+
+#define json_tr_string_clone_result                       \
+	do {                                                 \
+		if(val->rs.len > JSON_TR_BUFFER_SIZE - 1) {       \
+			LM_ERR("result is too big\n");               \
+			return -1;                                   \
+		}                                                \
+		strncpy(_json_tr_buffer, val->rs.s, val->rs.len); \
+		val->rs.s = _json_tr_buffer;                      \
+	} while(0);
+
+void json_destroy_pv_value(pv_value_t *val)
+{
+	if(val->flags & PV_VAL_PKG)
+		pkg_free(val->rs.s);
+	else if(val->flags & PV_VAL_SHM)
+		shm_free(val->rs.s);
+	pkg_free(val);
+}
+
+void json_free_pv_value(pv_value_t *val)
+{
+	if(val->flags & PV_VAL_PKG)
+		pkg_free(val->rs.s);
+	else if(val->flags & PV_VAL_SHM)
+		shm_free(val->rs.s);
+}
+
+pv_value_t *json_alloc_pv_value()
+{
+	pv_value_t *v = (pv_value_t *)pkg_malloc(sizeof(pv_value_t));
+	if(v != NULL)
+		memset(v, 0, sizeof(pv_value_t));
+	return v;
+}
+
+#define KEY_SAFE(C)                                   \
+	((C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z') \
+			|| (C >= '0' && C <= '9') || (C == '-' || C == '~' || C == '_'))
+
+#define HI4(C) (C >> 4)
+#define LO4(C) (C & 0x0F)
+
+#define hexint(C) (C < 10 ? ('0' + C) : ('A' + C - 10))
+
+char *json_util_encode(const str *key, char *dest)
+{
+	if((key->len == 1) && (key->s[0] == '#' || key->s[0] == '*')) {
+		*dest++ = key->s[0];
+		return dest;
+	}
+	char *p, *end;
+	for(p = key->s, end = key->s + key->len; p < end; p++) {
+		if(KEY_SAFE(*p)) {
+			*dest++ = *p;
+		} else if(*p == '.') {
+			memcpy(dest, "\%2E", 3);
+			dest += 3;
+		} else if(*p == ' ') {
+			*dest++ = '+';
+		} else {
+			*dest++ = '%';
+			sprintf(dest, "%c%c", hexint(HI4(*p)), hexint(LO4(*p)));
+			dest += 2;
+		}
+	}
+	*dest = '\0';
+	return dest;
+}
+
+int json_encode_ex(str *unencoded, pv_value_p dst_val)
+{
+	char routing_key_buff[256];
+	memset(routing_key_buff, 0, sizeof(routing_key_buff));
+	json_util_encode(unencoded, routing_key_buff);
+
+	int len = strlen(routing_key_buff);
+	dst_val->rs.s = pkg_malloc(len + 1);
+	memcpy(dst_val->rs.s, routing_key_buff, len);
+	dst_val->rs.s[len] = '\0';
+	dst_val->rs.len = len;
+	dst_val->flags = PV_VAL_STR | PV_VAL_PKG;
+
+	return 1;
+}
+
+/*!
+ * \brief Evaluate JSON transformations
+ * \param msg SIP message
+ * \param tp transformation
+ * \param subtype transformation type
+ * \param val pseudo-variable
+ * \return 0 on success, -1 on error
+ */
+int json_tr_eval(
+		struct sip_msg *msg, tr_param_t *tp, int subtype, pv_value_t *val)
+{
+
+	str sv;
+	pv_value_t *pv;
+	pv_value_t v;
+	str v2 = {0, 0};
+	void *v1 = NULL;
+
+	if(val == NULL || (val->flags & PV_VAL_NULL))
+		return -1;
+
+
+	json_tr_set_crt_buffer();
+
+	switch(subtype) {
+		case TR_JSON_ENCODE:
+			if(!(val->flags & PV_VAL_STR))
+				return -1;
+
+			pv = json_alloc_pv_value();
+			if(pv == NULL) {
+				LM_ERR("JSON encode transform : no more private memory\n");
+				return -1;
+			}
+
+			if(json_encode_ex(&val->rs, pv) != 1) {
+				LM_ERR("error encoding value\n");
+				json_destroy_pv_value(pv);
+				return -1;
+			}
+
+			strncpy(_json_tr_buffer, pv->rs.s, pv->rs.len);
+			_json_tr_buffer[pv->rs.len] = '\0';
+
+			val->flags = PV_VAL_STR;
+			val->ri = 0;
+			val->rs.s = _json_tr_buffer;
+			val->rs.len = pv->rs.len;
+
+			json_destroy_pv_value(pv);
+			json_free_pv_value(val);
+
+			break;
+		case TR_JSON_PARSE:
+			if(!(val->flags & PV_VAL_STR))
+				return -1;
+
+			if(tp == NULL) {
+				LM_ERR("JSON json transform invalid parameter\n");
+				return -1;
+			}
+
+			pv = json_alloc_pv_value();
+			if(pv == NULL) {
+				LM_ERR("JSON encode transform : no more private memory\n");
+				return -1;
+			}
+
+
+			if(tp->type == TR_PARAM_STRING) {
+				v1 = tp->v.s.s;
+				if(fixup_spve_null(&v1, 1) != 0) {
+					LM_ERR("cannot get spve_value from TR_PARAM_STRING : "
+						   "%.*s\n",
+							tp->v.s.len, tp->v.s.s);
+					return -1;
+				}
+				if(fixup_get_svalue(msg, (gparam_p)v1, &v2) != 0) {
+					LM_ERR("cannot get value from TR_PARAM_STRING\n");
+					fixup_free_spve_null(&v1, 1);
+					return -1;
+				}
+				fixup_free_spve_null(&v1, 1);
+				sv = v2;
+			} else {
+				if(pv_get_spec_value(msg, (pv_spec_p)tp->v.data, &v) != 0
+						|| (!(v.flags & PV_VAL_STR)) || v.rs.len <= 0) {
+					LM_ERR("value cannot get spec value in json transform\n");
+					json_destroy_pv_value(pv);
+					return -1;
+				}
+				sv = v.rs;
+			}
+
+
+			if(tr_json_get_field_ex(&val->rs, &sv, pv) != 1) {
+				LM_ERR("error getting json\n");
+				json_destroy_pv_value(pv);
+				return -1;
+			}
+
+			strncpy(_json_tr_buffer, pv->rs.s, pv->rs.len);
+			_json_tr_buffer[pv->rs.len] = '\0';
+
+			val->flags = PV_VAL_STR;
+			val->ri = 0;
+			val->rs.s = _json_tr_buffer;
+			val->rs.len = pv->rs.len;
+
+			json_destroy_pv_value(pv);
+			json_free_pv_value(val);
+
+			break;
+
+		default:
+			LM_ERR("unknown JSON transformation subtype %d\n", subtype);
+			return -1;
+	}
+	return 0;
+}
+
+#define _json_tr_parse_sparam(_p, _p0, _tp, _spec, _ps, _in, _s)                \
+	while(is_in_str(_p, _in) && (*_p == ' ' || *_p == '\t' || *_p == '\n'))    \
+		_p++;                                                                  \
+	if(*_p == PV_MARKER) { /* pseudo-variable */                               \
+		_spec = (pv_spec_t *)malloc(sizeof(pv_spec_t));                        \
+		if(_spec == NULL) {                                                    \
+			LM_ERR("no more private memory!\n");                               \
+			goto error;                                                        \
+		}                                                                      \
+		_s.s = _p;                                                             \
+		_s.len = _in->s + _in->len - _p;                                       \
+		_p0 = pv_parse_spec(&_s, _spec);                                       \
+		if(_p0 == NULL) {                                                      \
+			LM_ERR("invalid spec in substr transformation: %.*s!\n", _in->len, \
+					_in->s);                                                   \
+			goto error;                                                        \
+		}                                                                      \
+		_p = _p0;                                                              \
+		_tp = (tr_param_t *)malloc(sizeof(tr_param_t));                        \
+		if(_tp == NULL) {                                                      \
+			LM_ERR("no more private memory!\n");                               \
+			goto error;                                                        \
+		}                                                                      \
+		memset(_tp, 0, sizeof(tr_param_t));                                    \
+		_tp->type = TR_PARAM_SPEC;                                             \
+		_tp->v.data = (void *)_spec;                                           \
+		_json_parse_specs[_json_tr_parse_spec++] = _spec;                        \
+		_json_parse_params[_json_tr_parse_params++] = _tp;                       \
+	} else { /* string */                                                      \
+		_ps = _p;                                                              \
+		while(is_in_str(_p, _in) && *_p != '\t' && *_p != '\n'                 \
+				&& *_p != TR_PARAM_MARKER && *_p != TR_RBRACKET)               \
+			_p++;                                                              \
+		if(*_p == '\0') {                                                      \
+			LM_ERR("invalid param in transformation: %.*s!!\n", _in->len,      \
+					_in->s);                                                   \
+			goto error;                                                        \
+		}                                                                      \
+		_tp = (tr_param_t *)malloc(sizeof(tr_param_t));                        \
+		if(_tp == NULL) {                                                      \
+			LM_ERR("no more private memory!\n");                               \
+			goto error;                                                        \
+		}                                                                      \
+		memset(_tp, 0, sizeof(tr_param_t));                                    \
+		_tp->type = TR_PARAM_STRING;                                           \
+		_tp->v.s.len = _p - _ps;                                               \
+		_tp->v.s.s = (char *)malloc((tp->v.s.len + 1) * sizeof(char));         \
+		strncpy(_tp->v.s.s, _ps, tp->v.s.len);                                 \
+		_tp->v.s.s[tp->v.s.len] = '\0';                                        \
+		_json_parse_params[_json_tr_parse_params++] = _tp;                       \
+	}
+
+
+/*!
+ * \brief Helper fuction to parse a JSON transformation
+ * \param in parsed string
+ * \param t transformation
+ * \return pointer to the end of the transformation in the string - '}', null on error
+ */
+char *json_tr_parse(str *in, trans_t *t)
+{
+	char *p;
+	char *p0;
+	char *ps;
+	str name;
+	str s;
+	pv_spec_t *spec = NULL;
+	tr_param_t *tp = NULL;
+
+	if(in == NULL || t == NULL)
+		return NULL;
+
+	p = in->s;
+	name.s = in->s;
+	t->type = TR_JSON;
+	t->trf = json_tr_eval;
+
+	/* find next token */
+	while(is_in_str(p, in) && *p != TR_PARAM_MARKER && *p != TR_RBRACKET)
+		p++;
+	if(*p == '\0') {
+		LM_ERR("invalid transformation: %.*s\n", in->len, in->s);
+		goto error;
+	}
+	name.len = p - name.s;
+	trim(&name);
+
+	if(name.len == 5 && strncasecmp(name.s, "encode", 6) == 0) {
+		t->subtype = TR_JSON_ENCODE;
+		goto done;
+	} else if(name.len == 5 && strncasecmp(name.s, "parse", 5) == 0) {
+		t->subtype = TR_JSON_PARSE;
+		if(*p != TR_PARAM_MARKER) {
+			LM_ERR("invalid json transformation: %.*s!\n", in->len, in->s);
+			goto error;
+		}
+		p++;
+		_json_tr_parse_sparam(p, p0, tp, spec, ps, in, s);
+		t->params = tp;
+		tp = 0;
+		while(*p && (*p == ' ' || *p == '\t' || *p == '\n'))
+			p++;
+		if(*p != TR_RBRACKET) {
+			LM_ERR("invalid json transformation: %.*s!!\n", in->len, in->s);
+			goto error;
+		}
+		goto done;
+	}
+
+	LM_ERR("unknown JSON transformation: %.*s/%.*s/%d!\n", in->len, in->s,
+			name.len, name.s, name.len);
+error:
+	if(tp)
+		free(tp);
+	if(spec)
+		free(spec);
+	return NULL;
+done:
+	t->name = name;
+	return p;
+}

+ 51 - 0
src/modules/json/json_trans.h

@@ -0,0 +1,51 @@
+/*
+ * JSON module interface
+ *
+ * Copyright (C) 2010-2014 2600Hz
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Contributor(s):
+ * Emmanuel Schmidbauer <[email protected]>
+ *
+ */
+
+#ifndef _JSON_TRANS_H_
+#define _JSON_TRANS_H_
+
+#include "../../core/pvar.h"
+
+enum _json_tr_type
+{
+	TR_NONE = 0,
+	TR_JSON
+};
+enum _json_tr_subtype
+{
+	TR_JSON_NONE = 0,
+	TR_JSON_ENCODE,
+	TR_JSON_PARSE
+};
+
+char *json_tr_parse(str *in, trans_t *tr);
+int tr_json_get_field_ex(str *json, str *field, pv_value_p dst_val);
+
+int json_tr_init_buffers(void);
+void json_tr_clear_buffers(void);
+
+
+#endif