Integrate functionality tested on svncommunity users back into trunk
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@49030 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
068fb38804
commit
8d086303b6
|
@ -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