mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 10:47:18 +00:00 
			
		
		
		
	Integrate functionality tested on svncommunity users back into trunk
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@49030 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		| @@ -37,6 +37,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| #include "asterisk/module.h" | ||||
| #include "asterisk/file.h" | ||||
| @@ -57,7 +58,8 @@ enum { | ||||
|  | ||||
| struct acf_odbc_query { | ||||
| 	AST_LIST_ENTRY(acf_odbc_query) list; | ||||
| 	char dsn[30]; | ||||
| 	char readhandle[5][30]; | ||||
| 	char writehandle[5][30]; | ||||
| 	char sql_read[2048]; | ||||
| 	char sql_write[2048]; | ||||
| 	unsigned int flags; | ||||
| @@ -96,7 +98,7 @@ static int acf_odbc_write(struct ast_channel *chan, char *cmd, char *s, const ch | ||||
| 	struct odbc_obj *obj; | ||||
| 	struct acf_odbc_query *query; | ||||
| 	char *t, buf[2048]="", varname[15]; | ||||
| 	int i; | ||||
| 	int i, dsn; | ||||
| 	AST_DECLARE_APP_ARGS(values, | ||||
| 		AST_APP_ARG(field)[100]; | ||||
| 	); | ||||
| @@ -119,14 +121,6 @@ static int acf_odbc_write(struct ast_channel *chan, char *cmd, char *s, const ch | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	obj = ast_odbc_request_obj(query->dsn, 0); | ||||
|  | ||||
| 	if (!obj) { | ||||
| 		ast_log(LOG_ERROR, "No database handle available with the name of '%s' (check res_odbc.conf)\n", query->dsn); | ||||
| 		AST_LIST_UNLOCK(&queries); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Parse our arguments */ | ||||
| 	t = value ? ast_strdupa(value) : ""; | ||||
|  | ||||
| @@ -169,7 +163,15 @@ static int acf_odbc_write(struct ast_channel *chan, char *cmd, char *s, const ch | ||||
|  | ||||
| 	AST_LIST_UNLOCK(&queries); | ||||
|  | ||||
| 	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, buf); | ||||
| 	for (dsn = 0; dsn < 5; dsn++) { | ||||
| 		if (!ast_strlen_zero(query->writehandle[dsn])) { | ||||
| 			obj = ast_odbc_request_obj(query->writehandle[dsn], 0); | ||||
| 			if (obj) | ||||
| 				stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, buf); | ||||
| 		} | ||||
| 		if (stmt) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	if (stmt) { | ||||
| 		/* Rows affected */ | ||||
| @@ -195,14 +197,15 @@ static int acf_odbc_read(struct ast_channel *chan, char *cmd, char *s, char *buf | ||||
| { | ||||
| 	struct odbc_obj *obj; | ||||
| 	struct acf_odbc_query *query; | ||||
| 	char sql[2048] = "", varname[15]; | ||||
| 	int res, x, buflen = 0, escapecommas; | ||||
| 	char sql[2048] = "", varname[15], colnames[2048] = ""; | ||||
| 	int res, x, buflen = 0, escapecommas, dsn; | ||||
| 	AST_DECLARE_APP_ARGS(args, | ||||
| 		AST_APP_ARG(field)[100]; | ||||
| 	); | ||||
| 	SQLHSTMT stmt; | ||||
| 	SQLSMALLINT colcount=0; | ||||
| 	SQLLEN indicator; | ||||
| 	SQLSMALLINT collength; | ||||
|  | ||||
| 	AST_LIST_LOCK(&queries); | ||||
| 	AST_LIST_TRAVERSE(&queries, query, list) { | ||||
| @@ -217,14 +220,6 @@ static int acf_odbc_read(struct ast_channel *chan, char *cmd, char *s, char *buf | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	obj = ast_odbc_request_obj(query->dsn, 0); | ||||
|  | ||||
| 	if (!obj) { | ||||
| 		ast_log(LOG_ERROR, "No such DSN registered (or out of connections): %s (check res_odbc.conf)\n", query->dsn); | ||||
| 		AST_LIST_UNLOCK(&queries); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	AST_STANDARD_APP_ARGS(args, s); | ||||
| 	for (x = 0; x < args.argc; x++) { | ||||
| 		snprintf(varname, sizeof(varname), "ARG%d", x + 1); | ||||
| @@ -244,10 +239,20 @@ static int acf_odbc_read(struct ast_channel *chan, char *cmd, char *s, char *buf | ||||
|  | ||||
| 	AST_LIST_UNLOCK(&queries); | ||||
|  | ||||
| 	stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, sql); | ||||
| 	for (dsn = 0; dsn < 5; dsn++) { | ||||
| 		if (!ast_strlen_zero(query->writehandle[dsn])) { | ||||
| 			obj = ast_odbc_request_obj(query->writehandle[dsn], 0); | ||||
| 			if (obj) | ||||
| 				stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, sql); | ||||
| 		} | ||||
| 		if (stmt) | ||||
| 			break; | ||||
| 	} | ||||
|  | ||||
| 	if (!stmt) { | ||||
| 		ast_odbc_release_obj(obj); | ||||
| 		ast_log(LOG_ERROR, "Unable to execute query [%s]\n", sql); | ||||
| 		if (obj) | ||||
| 			ast_odbc_release_obj(obj); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| @@ -278,8 +283,33 @@ static int acf_odbc_read(struct ast_channel *chan, char *cmd, char *s, char *buf | ||||
| 	} | ||||
|  | ||||
| 	for (x = 0; x < colcount; x++) { | ||||
| 		int i; | ||||
| 		char coldata[256]; | ||||
| 		int i, namelen; | ||||
| 		char coldata[256], colname[256]; | ||||
|  | ||||
| 		res = SQLDescribeCol(stmt, x + 1, (unsigned char *)colname, sizeof(colname), &collength, NULL, NULL, NULL, NULL); | ||||
| 		if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || collength == 0) { | ||||
| 			snprintf(colname, sizeof(colname), "field%d", x); | ||||
| 		} | ||||
|  | ||||
| 		if (!ast_strlen_zero(colnames)) | ||||
| 			strncat(colnames, ",", sizeof(colnames) - 1); | ||||
| 		namelen = strlen(colnames); | ||||
|  | ||||
| 		/* Copy data, encoding '\' and ',' for the argument parser */ | ||||
| 		for (i = 0; i < sizeof(colname); i++) { | ||||
| 			if (escapecommas && (colname[i] == '\\' || colname[i] == ',')) { | ||||
| 				colnames[namelen++] = '\\'; | ||||
| 			} | ||||
| 			colnames[namelen++] = colname[i]; | ||||
|  | ||||
| 			if (namelen >= sizeof(colnames) - 2) { | ||||
| 				colnames[namelen >= sizeof(colnames) ? sizeof(colnames) - 1 : namelen] = '\0'; | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			if (colname[i] == '\0') | ||||
| 				break; | ||||
| 		} | ||||
|  | ||||
| 		buflen = strlen(buf); | ||||
| 		res = SQLGetData(stmt, x + 1, SQL_CHAR, coldata, sizeof(coldata), &indicator); | ||||
| @@ -315,6 +345,8 @@ static int acf_odbc_read(struct ast_channel *chan, char *cmd, char *s, char *buf | ||||
| 	/* Trim trailing comma */ | ||||
| 	buf[buflen - 1] = '\0'; | ||||
|  | ||||
| 	pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames); | ||||
|  | ||||
| 	SQLFreeHandle(SQL_HANDLE_STMT, stmt); | ||||
| 	ast_odbc_release_obj(obj); | ||||
| 	return 0; | ||||
| @@ -351,27 +383,68 @@ static struct ast_custom_function escape_function = { | ||||
| static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_query **query) | ||||
| { | ||||
| 	const char *tmp; | ||||
| 	int i; | ||||
|  | ||||
| 	if (!cfg || !catg) { | ||||
| 		return -1; | ||||
| 		return EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	*query = ast_calloc(1, sizeof(struct acf_odbc_query)); | ||||
| 	if (! (*query)) | ||||
| 		return -1; | ||||
| 		return ENOMEM; | ||||
|  | ||||
| 	if ((tmp = ast_variable_retrieve(cfg, catg, "dsn"))) { | ||||
| 		ast_copy_string((*query)->dsn, tmp, sizeof((*query)->dsn)); | ||||
| 	} else { | ||||
| 		return -1; | ||||
| 	if (((tmp = ast_variable_retrieve(cfg, catg, "writehandle"))) || ((tmp = ast_variable_retrieve(cfg, catg, "dsn")))) { | ||||
| 		char *tmp2 = ast_strdupa(tmp); | ||||
| 		AST_DECLARE_APP_ARGS(write, | ||||
| 			AST_APP_ARG(dsn)[5]; | ||||
| 		); | ||||
| 		AST_NONSTANDARD_APP_ARGS(write, tmp2, ','); | ||||
| 		for (i = 0; i < 5; i++) { | ||||
| 			if (!ast_strlen_zero(write.dsn[i])) | ||||
| 				ast_copy_string((*query)->writehandle[i], write.dsn[i], sizeof((*query)->writehandle[i])); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if ((tmp = ast_variable_retrieve(cfg, catg, "readhandle"))) { | ||||
| 		char *tmp2 = ast_strdupa(tmp); | ||||
| 		AST_DECLARE_APP_ARGS(read, | ||||
| 			AST_APP_ARG(dsn)[5]; | ||||
| 		); | ||||
| 		AST_NONSTANDARD_APP_ARGS(read, tmp2, ','); | ||||
| 		for (i = 0; i < 5; i++) { | ||||
| 			if (!ast_strlen_zero(read.dsn[i])) | ||||
| 				ast_copy_string((*query)->readhandle[i], read.dsn[i], sizeof((*query)->readhandle[i])); | ||||
| 		} | ||||
| 	} else { | ||||
| 		/* If no separate readhandle, then use the writehandle for reading */ | ||||
| 		for (i = 0; i < 5; i++) { | ||||
| 			if (!ast_strlen_zero((*query)->writehandle[i])) | ||||
| 				ast_copy_string((*query)->readhandle[i], (*query)->writehandle[i], sizeof((*query)->readhandle[i])); | ||||
| 		} | ||||
|  	} | ||||
|  | ||||
| 	if ((tmp = ast_variable_retrieve(cfg, catg, "read"))) { | ||||
| 		ast_log(LOG_WARNING, "Parameter 'read' is deprecated for category %s.  Please use 'readsql' instead.\n", catg); | ||||
| 		ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read)); | ||||
| 	} else if ((tmp = ast_variable_retrieve(cfg, catg, "readsql"))) | ||||
| 		ast_copy_string((*query)->sql_read, tmp, sizeof((*query)->sql_read)); | ||||
|  | ||||
| 	if (!ast_strlen_zero((*query)->sql_read) && ast_strlen_zero((*query)->readhandle[0])) { | ||||
| 		free(*query); | ||||
| 		ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for reading: %s\n", catg); | ||||
| 		return EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	if ((tmp = ast_variable_retrieve(cfg, catg, "write"))) { | ||||
| 		ast_log(LOG_WARNING, "Parameter 'write' is deprecated for category %s.  Please use 'writesql' instead.\n", catg); | ||||
| 		ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write)); | ||||
| 	} else if ((tmp = ast_variable_retrieve(cfg, catg, "writesql"))) | ||||
| 		ast_copy_string((*query)->sql_write, tmp, sizeof((*query)->sql_write)); | ||||
|  | ||||
| 	if (!ast_strlen_zero((*query)->sql_write) && ast_strlen_zero((*query)->writehandle[0])) { | ||||
| 		free(*query); | ||||
| 		ast_log(LOG_ERROR, "There is SQL, but no ODBC class to be used for writing: %s\n", catg); | ||||
| 		return EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* Allow escaping of embedded commas in fields to be turned off */ | ||||
| @@ -384,7 +457,7 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu | ||||
| 	(*query)->acf = ast_calloc(1, sizeof(struct ast_custom_function)); | ||||
| 	if (! (*query)->acf) { | ||||
| 		free(*query); | ||||
| 		return -1; | ||||
| 		return ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	if ((tmp = ast_variable_retrieve(cfg, catg, "prefix")) && !ast_strlen_zero(tmp)) { | ||||
| @@ -396,7 +469,7 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu | ||||
| 	if (!((*query)->acf->name)) { | ||||
| 		free((*query)->acf); | ||||
| 		free(*query); | ||||
| 		return -1; | ||||
| 		return ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	asprintf((char **)&((*query)->acf->syntax), "%s(<arg1>[...[,<argN>]])", (*query)->acf->name); | ||||
| @@ -405,7 +478,7 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu | ||||
| 		free((char *)(*query)->acf->name); | ||||
| 		free((*query)->acf); | ||||
| 		free(*query); | ||||
| 		return -1; | ||||
| 		return ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	(*query)->acf->synopsis = "Runs the referenced query with the specified arguments"; | ||||
| @@ -432,15 +505,21 @@ static int init_acf_query(struct ast_config *cfg, char *catg, struct acf_odbc_qu | ||||
| 					"${VALUE} or parsed as ${VAL1}, ${VAL2}, ... ${VALn}.\n" | ||||
| 					"This function may only be set.\nSQL:\n%s\n", | ||||
| 					(*query)->sql_write); | ||||
| 	} else { | ||||
| 		free((char *)(*query)->acf->syntax); | ||||
| 		free((char *)(*query)->acf->name); | ||||
| 		free((*query)->acf); | ||||
| 		free(*query); | ||||
| 		ast_log(LOG_WARNING, "Section %s was found, but there was no SQL to execute.  Ignoring.\n", catg); | ||||
| 		return EINVAL; | ||||
| 	} | ||||
|  | ||||
| 	/* Could be out of memory, or could be we have neither sql_read nor sql_write */ | ||||
| 	if (! ((*query)->acf->desc)) { | ||||
| 		free((char *)(*query)->acf->syntax); | ||||
| 		free((char *)(*query)->acf->name); | ||||
| 		free((*query)->acf); | ||||
| 		free(*query); | ||||
| 		return -1; | ||||
| 		return ENOMEM; | ||||
| 	} | ||||
|  | ||||
| 	if (ast_strlen_zero((*query)->sql_read)) { | ||||
| @@ -475,7 +554,7 @@ static int free_acf_query(struct acf_odbc_query *query) | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int odbc_load_module(void) | ||||
| static int load_module(void) | ||||
| { | ||||
| 	int res = 0; | ||||
| 	struct ast_config *cfg; | ||||
| @@ -494,10 +573,15 @@ static int odbc_load_module(void) | ||||
| 	     catg; | ||||
| 	     catg = ast_category_browse(cfg, catg)) { | ||||
| 		struct acf_odbc_query *query = NULL; | ||||
| 		int err; | ||||
|  | ||||
| 		if (init_acf_query(cfg, catg, &query)) { | ||||
| 			ast_log(LOG_ERROR, "Out of memory\n"); | ||||
| 			free_acf_query(query); | ||||
| 		if ((err = init_acf_query(cfg, catg, &query))) { | ||||
| 			if (err == ENOMEM) | ||||
| 				ast_log(LOG_ERROR, "Out of memory\n"); | ||||
| 			else if (err == EINVAL) | ||||
| 				ast_log(LOG_ERROR, "Invalid parameters for category %s\n", catg); | ||||
| 			else | ||||
| 				ast_log(LOG_ERROR, "%s (%d)\n", strerror(err), err); | ||||
| 		} else { | ||||
| 			AST_LIST_INSERT_HEAD(&queries, query, list); | ||||
| 			ast_custom_function_register(query->acf); | ||||
| @@ -505,15 +589,16 @@ static int odbc_load_module(void) | ||||
| 	} | ||||
|  | ||||
| 	ast_config_destroy(cfg); | ||||
| 	ast_custom_function_register(&escape_function); | ||||
| 	res |= ast_custom_function_register(&escape_function); | ||||
|  | ||||
| 	AST_LIST_UNLOCK(&queries); | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| static int odbc_unload_module(void) | ||||
| static int unload_module(void) | ||||
| { | ||||
| 	struct acf_odbc_query *query; | ||||
| 	int res = 0; | ||||
|  | ||||
| 	AST_LIST_LOCK(&queries); | ||||
| 	while (!AST_LIST_EMPTY(&queries)) { | ||||
| @@ -522,10 +607,11 @@ static int odbc_unload_module(void) | ||||
| 		free_acf_query(query); | ||||
| 	} | ||||
|  | ||||
| 	ast_custom_function_unregister(&escape_function); | ||||
| 	res |= ast_custom_function_unregister(&escape_function); | ||||
|  | ||||
| 	/* Allow any threads waiting for this lock to pass (avoids a race) */ | ||||
| 	AST_LIST_UNLOCK(&queries); | ||||
| 	usleep(1); | ||||
| 	AST_LIST_LOCK(&queries); | ||||
|  | ||||
| 	AST_LIST_UNLOCK(&queries); | ||||
| @@ -572,16 +658,6 @@ reload_out: | ||||
| 	return res; | ||||
| } | ||||
|  | ||||
| static int unload_module(void) | ||||
| { | ||||
| 	return odbc_unload_module(); | ||||
| } | ||||
|  | ||||
| static int load_module(void) | ||||
| { | ||||
| 	return odbc_load_module(); | ||||
| } | ||||
|  | ||||
| /* XXX need to revise usecount - set if query_lock is set */ | ||||
|  | ||||
| AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "ODBC lookups", | ||||
|   | ||||
| @@ -155,6 +155,37 @@ static struct ast_custom_function regex_function = { | ||||
| 	.read = regex, | ||||
| }; | ||||
|  | ||||
| #define HASH_PREFIX	"~HASH~%s~" | ||||
| #define HASH_FORMAT	HASH_PREFIX "%s~" | ||||
|  | ||||
| static char *app_clearhash = "ClearHash"; | ||||
| static char *syn_clearhash = "Clear the keys from a specified hashname"; | ||||
| static char *desc_clearhash = | ||||
| "ClearHash(<hashname>)\n" | ||||
| "  Clears all keys out of the specified hashname\n"; | ||||
|  | ||||
| /* This function probably should migrate to main/pbx.c, as pbx_builtin_clearvar_prefix() */ | ||||
| static void clearvar_prefix(struct ast_channel *chan, const char *prefix) | ||||
| { | ||||
| 	struct ast_var_t *var; | ||||
| 	int len = strlen(prefix); | ||||
| 	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->varshead, var, entries) { | ||||
| 		if (strncasecmp(prefix, ast_var_name(var), len) == 0) { | ||||
| 			AST_LIST_REMOVE_CURRENT(&chan->varshead, entries); | ||||
| 			free(var); | ||||
| 		} | ||||
| 	} | ||||
| 	AST_LIST_TRAVERSE_SAFE_END | ||||
| } | ||||
|  | ||||
| static int exec_clearhash(struct ast_channel *chan, void *data) | ||||
| { | ||||
| 	char prefix[80]; | ||||
| 	snprintf(prefix, sizeof(prefix), HASH_PREFIX, data ? (char *)data : "null"); | ||||
| 	clearvar_prefix(chan, prefix); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int array(struct ast_channel *chan, char *cmd, char *var, | ||||
| 		 const char *value) | ||||
| { | ||||
| @@ -164,13 +195,23 @@ static int array(struct ast_channel *chan, char *cmd, char *var, | ||||
| 	AST_DECLARE_APP_ARGS(arg2, | ||||
| 			     AST_APP_ARG(val)[100]; | ||||
| 	); | ||||
| 	char *value2; | ||||
| 	int i; | ||||
| 	char *origvar = "", *value2, varname[256]; | ||||
| 	int i, ishash = 0; | ||||
|  | ||||
| 	value2 = ast_strdupa(value); | ||||
| 	if (!var || !value2) | ||||
| 		return -1; | ||||
|  | ||||
| 	if (!strcmp(cmd, "HASH")) { | ||||
| 		const char *var2 = pbx_builtin_getvar_helper(chan, "~ODBCFIELDS~"); | ||||
| 		origvar = var; | ||||
| 		if (var2) | ||||
| 			var = ast_strdupa(var2); | ||||
| 		else | ||||
| 			return -1; | ||||
| 		ishash = 1; | ||||
| 	} | ||||
|  | ||||
| 	/* The functions this will generally be used with are SORT and ODBC_*, which | ||||
| 	 * both return comma-delimited lists.  However, if somebody uses literal lists, | ||||
| 	 * their commas will be translated to vertical bars by the load, and I don't | ||||
| @@ -194,17 +235,139 @@ static int array(struct ast_channel *chan, char *cmd, char *var, | ||||
| 			ast_log(LOG_DEBUG, "array set value (%s=%s)\n", arg1.var[i], | ||||
| 				arg2.val[i]); | ||||
| 		if (i < arg2.argc) { | ||||
| 			pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]); | ||||
| 			if (ishash) { | ||||
| 				snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]); | ||||
| 				pbx_builtin_setvar_helper(chan, varname, arg2.val[i]); | ||||
| 			} else { | ||||
| 				pbx_builtin_setvar_helper(chan, arg1.var[i], arg2.val[i]); | ||||
| 			} | ||||
| 		} else { | ||||
| 			/* We could unset the variable, by passing a NULL, but due to | ||||
| 			 * pushvar semantics, that could create some undesired behavior. */ | ||||
| 			pbx_builtin_setvar_helper(chan, arg1.var[i], ""); | ||||
| 			if (ishash) { | ||||
| 				snprintf(varname, sizeof(varname), HASH_FORMAT, origvar, arg1.var[i]); | ||||
| 				pbx_builtin_setvar_helper(chan, varname, ""); | ||||
| 			} else { | ||||
| 				pbx_builtin_setvar_helper(chan, arg1.var[i], ""); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int hashkeys_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) | ||||
| { | ||||
| 	struct ast_var_t *newvar; | ||||
| 	int plen; | ||||
| 	char prefix[80]; | ||||
| 	snprintf(prefix, sizeof(prefix), HASH_PREFIX, data); | ||||
| 	plen = strlen(prefix); | ||||
|  | ||||
| 	memset(buf, 0, len); | ||||
| 	AST_LIST_TRAVERSE(&chan->varshead, newvar, entries) { | ||||
| 		if (strncasecmp(prefix, ast_var_name(newvar), plen) == 0) { | ||||
| 			/* Copy everything after the prefix */ | ||||
| 			strncat(buf, ast_var_name(newvar) + plen, len); | ||||
| 			/* Trim the trailing ~ */ | ||||
| 			buf[strlen(buf) - 1] = ','; | ||||
| 		} | ||||
| 	} | ||||
| 	/* Trim the trailing comma */ | ||||
| 	buf[strlen(buf) - 1] = '\0'; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int hash_write(struct ast_channel *chan, char *cmd, char *var, const char *value) | ||||
| { | ||||
| 	char varname[256]; | ||||
| 	AST_DECLARE_APP_ARGS(arg, | ||||
| 		AST_APP_ARG(hashname); | ||||
| 		AST_APP_ARG(hashkey); | ||||
| 	); | ||||
|  | ||||
| 	if (!strchr(var, '|')) { | ||||
| 		/* Single argument version */ | ||||
| 		return array(chan, "HASH", var, value); | ||||
| 	} | ||||
|  | ||||
| 	AST_STANDARD_APP_ARGS(arg, var); | ||||
| 	snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey); | ||||
| 	pbx_builtin_setvar_helper(chan, varname, value); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int hash_read(struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) | ||||
| { | ||||
| 	char varname[256]; | ||||
| 	const char *varvalue; | ||||
| 	AST_DECLARE_APP_ARGS(arg, | ||||
| 		AST_APP_ARG(hashname); | ||||
| 		AST_APP_ARG(hashkey); | ||||
| 	); | ||||
|  | ||||
| 	AST_STANDARD_APP_ARGS(arg, data); | ||||
| 	if (arg.argc == 2) { | ||||
| 		snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg.hashkey); | ||||
| 		varvalue = pbx_builtin_getvar_helper(chan, varname); | ||||
| 		if (varvalue) | ||||
| 			ast_copy_string(buf, varvalue, len); | ||||
| 		else | ||||
| 			*buf = '\0'; | ||||
| 	} else if (arg.argc == 1) { | ||||
| 		char colnames[4096]; | ||||
| 		int i; | ||||
| 		AST_DECLARE_APP_ARGS(arg2, | ||||
| 			AST_APP_ARG(col)[100]; | ||||
| 		); | ||||
|  | ||||
| 		/* Get column names, in no particular order */ | ||||
| 		hashkeys_read(chan, "HASHKEYS", arg.hashname, colnames, sizeof(colnames)); | ||||
| 		pbx_builtin_setvar_helper(chan, "~ODBCFIELDS~", colnames); | ||||
|  | ||||
| 		AST_NONSTANDARD_APP_ARGS(arg2, colnames, ','); | ||||
| 		*buf = '\0'; | ||||
|  | ||||
| 		/* Now get the corresponding column values, in exactly the same order */ | ||||
| 		for (i = 0; i < arg2.argc; i++) { | ||||
| 			snprintf(varname, sizeof(varname), HASH_FORMAT, arg.hashname, arg2.col[i]); | ||||
| 			varvalue = pbx_builtin_getvar_helper(chan, varname); | ||||
| 			strncat(buf, varvalue, len); | ||||
| 			strncat(buf, ",", len); | ||||
| 		} | ||||
|  | ||||
| 		/* Strip trailing comma */ | ||||
| 		buf[strlen(buf) - 1] = '\0'; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct ast_custom_function hash_function = { | ||||
| 	.name = "HASH", | ||||
| 	.synopsis = "Implementation of a dialplan associative array", | ||||
| 	.syntax = "HASH(hashname[|hashkey])", | ||||
| 	.write = hash_write, | ||||
| 	.read = hash_read, | ||||
| 	.desc = | ||||
| 		"In two argument mode, gets and sets values to corresponding keys within a named\n" | ||||
| 		"associative array.  The single-argument mode will only work when assigned to from\n" | ||||
| 		"a function defined by func_odbc.so.\n", | ||||
| }; | ||||
|  | ||||
| static struct ast_custom_function hashkeys_function = { | ||||
| 	.name = "HASHKEYS", | ||||
| 	.synopsis = "Retrieve the keys of a HASH()", | ||||
| 	.syntax = "HASHKEYS(<hashname>)", | ||||
| 	.read = hashkeys_read, | ||||
| 	.desc = | ||||
| 		"Returns a comma-delimited list of the current keys of an associative array\n" | ||||
| 	   	"defined by the HASH() function.  Note that if you iterate over the keys of\n" | ||||
| 		"the result, adding keys during iteration will cause the result of the HASHKEYS\n" | ||||
| 		"function to change.\n", | ||||
| }; | ||||
|  | ||||
| static struct ast_custom_function array_function = { | ||||
| 	.name = "ARRAY", | ||||
| 	.synopsis = "Allows setting multiple variables at once", | ||||
| @@ -589,6 +752,9 @@ static int unload_module(void) | ||||
| 	res |= ast_custom_function_unregister(&eval_function); | ||||
| 	res |= ast_custom_function_unregister(&keypadhash_function); | ||||
| 	res |= ast_custom_function_unregister(&sprintf_function); | ||||
| 	res |= ast_custom_function_unregister(&hashkeys_function); | ||||
| 	res |= ast_custom_function_unregister(&hash_function); | ||||
| 	res |= ast_unregister_application(app_clearhash); | ||||
|  | ||||
| 	return res; | ||||
| } | ||||
| @@ -608,6 +774,9 @@ static int load_module(void) | ||||
| 	res |= ast_custom_function_register(&eval_function); | ||||
| 	res |= ast_custom_function_register(&keypadhash_function); | ||||
| 	res |= ast_custom_function_register(&sprintf_function); | ||||
| 	res |= ast_custom_function_register(&hashkeys_function); | ||||
| 	res |= ast_custom_function_register(&hash_function); | ||||
| 	res |= ast_register_application(app_clearhash, exec_clearhash, syn_clearhash, desc_clearhash); | ||||
|  | ||||
| 	return res; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user