mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-03 20:38:59 +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:
		@@ -17,25 +17,54 @@
 | 
			
		||||
; If you have data which may potentially contain single ticks, you may wish
 | 
			
		||||
; to use the dialplan function SQL_ESC() to escape the data prior to its
 | 
			
		||||
; inclusion in the SQL statement.
 | 
			
		||||
;
 | 
			
		||||
;
 | 
			
		||||
; The following variables are available in this configuration file:
 | 
			
		||||
;
 | 
			
		||||
; readhandle   A comma-separated list of DSNs (from res_odbc.conf) to use when
 | 
			
		||||
;              executing the readsql statement.  Each DSN is tried, in
 | 
			
		||||
;              succession, until the statement succeeds.  You may specify up to
 | 
			
		||||
;              5 DSNs per function class.  If not specified, it will default to
 | 
			
		||||
;              the value of writehandle or dsn, if specified.
 | 
			
		||||
; writehandle  A comma-separated list of DSNs (from res_odbc.conf) to use when
 | 
			
		||||
;              executing the writesql statement.  The same rules apply as to
 | 
			
		||||
;              readhandle.  "dsn" is a synonym for "writehandle".
 | 
			
		||||
; readsql      The statement to execute when reading from the function class.
 | 
			
		||||
; writesql     The statement to execute when writing to the function class.
 | 
			
		||||
; prefix       Normally, all function classes are prefixed with "ODBC" to keep
 | 
			
		||||
;              them uniquely named.  You may choose to change this prefix, which
 | 
			
		||||
;              may be useful to segregate a collection of certain function
 | 
			
		||||
;              classes from others.
 | 
			
		||||
; escapecommas This option may be used to turn off the default behavior of
 | 
			
		||||
;              escaping commas which occur within a field.  If commas are
 | 
			
		||||
;              escaped (the default behavior), then fields containing commas
 | 
			
		||||
;              will be treated as a single value when assigning to ARRAY() or
 | 
			
		||||
;              HASH().  If commas are not escaped, then values will be separated
 | 
			
		||||
;              at the comma within fields.  Please note that turning this option
 | 
			
		||||
;              off is incompatible with the functionality of HASH().
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
; ODBC_SQL - Allow an SQL statement to be built entirely in the dialplan
 | 
			
		||||
[SQL]
 | 
			
		||||
dsn=mysql1
 | 
			
		||||
read=${ARG1}
 | 
			
		||||
readsql=${ARG1}
 | 
			
		||||
 | 
			
		||||
; ODBC_ANTIGF - A blacklist.
 | 
			
		||||
[ANTIGF]
 | 
			
		||||
dsn=mysql1
 | 
			
		||||
read=SELECT COUNT(*) FROM exgirlfriends WHERE callerid='${SQL_ESC(${ARG1})}'
 | 
			
		||||
dsn=mysql1,mysql2   ; Use mysql1 as the primary handle, but fall back to mysql2
 | 
			
		||||
                    ; if mysql1 is down.  Supports up to 5 comma-separated
 | 
			
		||||
                    ; DSNs.  "dsn" may also be specified as "readhandle" and
 | 
			
		||||
                    ; "writehandle", if it is important to separate reads and
 | 
			
		||||
                    ; writes to different databases.
 | 
			
		||||
readsql=SELECT COUNT(*) FROM exgirlfriends WHERE callerid='${SQL_ESC(${ARG1})}'
 | 
			
		||||
 | 
			
		||||
; ODBC_PRESENCE - Retrieve and update presence
 | 
			
		||||
[PRESENCE]
 | 
			
		||||
dsn=mysql1
 | 
			
		||||
read=SELECT location FROM presence WHERE id='${SQL_ESC(${ARG1})}'
 | 
			
		||||
write=UPDATE presence SET location='${SQL_ESC(${VAL1})}' WHERE id='${SQL_ESC(${ARG1})}'
 | 
			
		||||
;prefix=OFFICE		; Changes this function from ODBC_PRESENCE to OFFICE_PRESENCE
 | 
			
		||||
;escapecommas=no	; Normally, commas within a field are escaped such that each
 | 
			
		||||
			; field may be separated into individual variables with ARRAY.
 | 
			
		||||
			; This option turns that behavior off [default=yes].
 | 
			
		||||
readsql=SELECT location FROM presence WHERE id='${SQL_ESC(${ARG1})}'
 | 
			
		||||
writesql=UPDATE presence SET location='${SQL_ESC(${VAL1})}' WHERE id='${SQL_ESC(${ARG1})}'
 | 
			
		||||
;prefix=OFFICE      ; Changes this function from ODBC_PRESENCE to OFFICE_PRESENCE
 | 
			
		||||
;escapecommas=no    ; Normally, commas within a field are escaped such that each
 | 
			
		||||
                    ; field may be separated into individual variables with ARRAY.
 | 
			
		||||
                    ; This option turns that behavior off [default=yes].
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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