Newer
Older
else /* action == E_MATCH */
aswf = asw->exists;
datap = sw->eval ? sw->tmpdata : sw->data;
res = !aswf ? 0 : aswf(chan, context, exten, priority, callerid, datap);
if (res) { /* Got a match */
q->swo = asw;
q->data = datap;
q->foundcontext = context;
/* XXX keep status = STATUS_NO_CONTEXT ? */
return NULL;
q->incstack[q->stacklen++] = tmp->name; /* Setup the stack */
/* Now try any includes we have in this context */
for (i = tmp->includes; i; i = i->next) {
if (include_valid(i)) {
if ((e = pbx_find_extension(chan, bypass, q, i->rname, exten, priority, label, callerid, action)))
Kevin P. Fleming
committed
/* Note that it's negative -- that's important later. */
#define DONT_HAVE_LENGTH 0x80000000
/*! \brief extract offset:length from variable name.
* Returns 1 if there is a offset:length part, which is
* trimmed off (values go into variables)
*/
Kevin P. Fleming
committed
static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
{
int parens=0;
*offset = 0;
*length = DONT_HAVE_LENGTH;
*isfunc = 0;
for (; *var; var++) {
if (*var == '(') {
Kevin P. Fleming
committed
(*isfunc)++;
parens++;
} else if (*var == ')') {
Kevin P. Fleming
committed
parens--;
} else if (*var == ':' && parens == 0) {
*var++ = '\0';
sscanf(var, "%d:%d", offset, length);
return 1; /* offset:length valid */
Kevin P. Fleming
committed
}
}
return 0;
Kevin P. Fleming
committed
}
/*! \brief takes a substring. It is ok to call with value == workspace.
*
* offset < 0 means start from the end of the string and set the beginning
* to be that many characters back.
* length is the length of the substring, -1 means unlimited
* (we take any negative value).
* Always return a copy in workspace.
*/
static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
Kevin P. Fleming
committed
{
char *ret = workspace;
int lr; /* length of the input string after the copy */
Kevin P. Fleming
committed
ast_copy_string(workspace, value, workspace_len); /* always make a copy */
Kevin P. Fleming
committed
/* Quick check if no need to do anything */
if (offset == 0 && length < 0) /* take the whole string */
return ret;
Kevin P. Fleming
committed
lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
Kevin P. Fleming
committed
if (offset < 0) { /* translate negative offset into positive ones */
offset = lr + offset;
if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
offset = 0;
Kevin P. Fleming
committed
}
/* too large offset result in empty string so we know what to return */
if (offset >= lr)
return ret + lr; /* the final '\0' */
Kevin P. Fleming
committed
ret += offset; /* move to the start position */
if (length >= 0 && length < lr - offset) /* truncate if necessary */
Kevin P. Fleming
committed
ret[length] = '\0';
return ret;
}
/*! \brief pbx_retrieve_variable: Support for Asterisk built-in variables and
void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
const char not_found = '\0';
char *tmpvar;
const char *s; /* the result */
int offset, length;
int i, need_substring;
struct varshead *places[2] = { headp, &globals }; /* list of places where we may look */
if (c) {
places[0] = &c->varshead;
}
/*
* Make a copy of var because parse_variable_name() modifies the string.
* Then if called directly, we might need to run substring() on the result;
* remember this for later in 'need_substring', 'offset' and 'length'
*/
tmpvar = ast_strdupa(var); /* parse_variable_name modifies the string */
need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
/*
* Look first into predefined variables, then into variable lists.
* Variable 's' points to the result, according to the following rules:
* s == ¬_found (set at the beginning) means that we did not find a
* matching variable and need to look into more places.
* If s != ¬_found, s is a valid result string as follows:
* s = NULL if the variable does not have a value;
* you typically do this when looking for an unset predefined variable.
* s = workspace if the result has been assembled there;
* typically done when the result is built e.g. with an snprintf(),
* so we don't need to do an additional copy.
* s != workspace in case we have a string, that needs to be copied
* (the ast_copy_string is done once for all at the end).
* Typically done when the result is already available in some string.
*/
s = ¬_found; /* default value */
if (c) { /* This group requires a valid channel */
/* Names with common parts are looked up a piece at a time using strncmp. */
if (!strncmp(var, "CALL", 4)) {
if (!strncmp(var + 4, "ING", 3)) {
if (!strcmp(var + 7, "PRES")) { /* CALLINGPRES */
snprintf(workspace, workspacelen, "%d", c->cid.cid_pres);
s = workspace;
} else if (!strcmp(var + 7, "ANI2")) { /* CALLINGANI2 */
snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
s = workspace;
} else if (!strcmp(var + 7, "TON")) { /* CALLINGTON */
snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
s = workspace;
} else if (!strcmp(var + 7, "TNS")) { /* CALLINGTNS */
snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
s = workspace;
} else if (!strcmp(var, "HINT")) {
s = ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten) ? workspace : NULL;
} else if (!strcmp(var, "HINTNAME")) {
s = ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten) ? workspace : NULL;
} else if (!strcmp(var, "EXTEN")) {
s = c->exten;
} else if (!strcmp(var, "CONTEXT")) {
s = c->context;
} else if (!strcmp(var, "PRIORITY")) {
snprintf(workspace, workspacelen, "%d", c->priority);
s = workspace;
} else if (!strcmp(var, "CHANNEL")) {
s = c->name;
} else if (!strcmp(var, "UNIQUEID")) {
s = c->uniqueid;
} else if (!strcmp(var, "HANGUPCAUSE")) {
snprintf(workspace, workspacelen, "%d", c->hangupcause);
s = workspace;
}
if (s == ¬_found) { /* look for more */
if (!strcmp(var, "EPOCH")) {
snprintf(workspace, workspacelen, "%u",(int)time(NULL));
s = workspace;
} else if (!strcmp(var, "SYSTEMNAME")) {
s = ast_config_AST_SYSTEM_NAME;
}
}
/* if not found, look into chanvars or global vars */
for (i = 0; s == ¬_found && i < (sizeof(places) / sizeof(places[0])); i++) {
struct ast_var_t *variables;
if (!places[i])
continue;
if (places[i] == &globals)
ast_mutex_lock(&globalslock);
AST_LIST_TRAVERSE(places[i], variables, entries) {
if (strcasecmp(ast_var_name(variables), var)==0) {
s = ast_var_value(variables);
break;
if (places[i] == &globals)
ast_mutex_unlock(&globalslock);
if (s == ¬_found || s == NULL)
*ret = NULL;
else {
if (s != workspace)
ast_copy_string(workspace, s, workspacelen);
*ret = workspace;
if (need_substring)
*ret = substring(*ret, offset, length, workspace, workspacelen);
/*! \brief CLI function to show installed custom functions
\addtogroup CLI_functions
*/
static int handle_show_functions(int fd, int argc, char *argv[])
{
struct ast_custom_function *acf;
int count_acf = 0;
int like = 0;
if (argc == 4 && (!strcmp(argv[2], "like")) ) {
like = 1;
} else if (argc != 2) {
return RESULT_SHOWUSAGE;
}
ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
AST_LIST_LOCK(&acf_root);
AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
if (!like || strstr(acf->name, argv[3])) {
count_acf++;
ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
}
AST_LIST_UNLOCK(&acf_root);
ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
return RESULT_SUCCESS;
static int handle_show_function(int fd, int argc, char *argv[])
struct ast_custom_function *acf;
/* Maximum number of characters added by terminal coloring is 22 */
char infotitle[64 + AST_MAX_APP + 22], syntitle[40], destitle[40];
char info[64 + AST_MAX_APP], *synopsis = NULL, *description = NULL;
char stxtitle[40], *syntax = NULL;
int synopsis_size, description_size, syntax_size;
if (argc < 3)
return RESULT_SHOWUSAGE;
if (!(acf = ast_custom_function_find(argv[2]))) {
ast_cli(fd, "No function by that name registered.\n");
return RESULT_FAILURE;
}
if (acf->synopsis)
synopsis_size = strlen(acf->synopsis) + 23;
else
synopsis_size = strlen("Not available") + 23;
synopsis = alloca(synopsis_size);
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
if (acf->desc)
description_size = strlen(acf->desc) + 23;
else
description_size = strlen("Not available") + 23;
description = alloca(description_size);
if (acf->syntax)
syntax_size = strlen(acf->syntax) + 23;
else
syntax_size = strlen("Not available") + 23;
syntax = alloca(syntax_size);
snprintf(info, 64 + AST_MAX_APP, "\n -= Info about function '%s' =- \n\n", acf->name);
term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
term_color(stxtitle, "[Syntax]\n", COLOR_MAGENTA, 0, 40);
term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
term_color(syntax,
acf->syntax ? acf->syntax : "Not available",
COLOR_CYAN, 0, syntax_size);
term_color(synopsis,
acf->synopsis ? acf->synopsis : "Not available",
COLOR_CYAN, 0, synopsis_size);
term_color(description,
acf->desc ? acf->desc : "Not available",
COLOR_CYAN, 0, description_size);
ast_cli(fd,"%s%s%s\n\n%s%s\n\n%s%s\n", infotitle, stxtitle, syntax, syntitle, synopsis, destitle, description);
return RESULT_SUCCESS;
}
Russell Bryant
committed
static char *complete_show_function(const char *line, const char *word, int pos, int state)
{
struct ast_custom_function *acf;
char *ret = NULL;
int wordlen = strlen(word);
Russell Bryant
committed
/* case-insensitive for convenience in this 'complete' function */
AST_LIST_LOCK(&acf_root);
AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
if (!strncasecmp(word, acf->name, wordlen) && ++which > state) {
Russell Bryant
committed
ret = strdup(acf->name);
break;
}
AST_LIST_UNLOCK(&acf_root);
struct ast_custom_function *ast_custom_function_find(const char *name)
struct ast_custom_function *acf = NULL;
AST_LIST_LOCK(&acf_root);
AST_LIST_TRAVERSE(&acf_root, acf, acflist) {
if (!strcmp(name, acf->name))
AST_LIST_UNLOCK(&acf_root);
return acf;
int ast_custom_function_unregister(struct ast_custom_function *acf)
struct ast_custom_function *cur;
if (!acf)
return -1;
AST_LIST_LOCK(&acf_root);
AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
AST_LIST_REMOVE_CURRENT(&acf_root, acflist);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
}
}
AST_LIST_TRAVERSE_SAFE_END
return acf ? 0 : -1;
int ast_custom_function_register(struct ast_custom_function *acf)
struct ast_custom_function *cur;
if (!acf)
return -1;
AST_LIST_LOCK(&acf_root);
if (ast_custom_function_find(acf->name)) {
ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
AST_LIST_UNLOCK(&acf_root);
return -1;
}
/* Store in alphabetical order */
AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
if (strcasecmp(acf->name, cur->name) < 0) {
AST_LIST_INSERT_BEFORE_CURRENT(&acf_root, acf, acflist);
AST_LIST_TRAVERSE_SAFE_END
if (!cur)
AST_LIST_INSERT_TAIL(&acf_root, acf, acflist);
AST_LIST_UNLOCK(&acf_root);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
return 0;
/*! \brief return a pointer to the arguments of the function,
*/
static char *func_args(char *function)
char *args = strchr(function, '(');
if (!args)
ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
else {
char *p;
if ((p = strrchr(args, ')')) )
ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
}
int ast_func_read(struct ast_channel *chan, char *function, char *workspace, size_t len)
{
char *args = func_args(function);
struct ast_custom_function *acfptr = ast_custom_function_find(function);
if (acfptr == NULL)
ast_log(LOG_ERROR, "Function %s not registered\n", function);
else if (!acfptr->read)
ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
else
return acfptr->read(chan, function, args, workspace, len);
int ast_func_write(struct ast_channel *chan, char *function, const char *value)
char *args = func_args(function);
struct ast_custom_function *acfptr = ast_custom_function_find(function);
ast_log(LOG_ERROR, "Function %s not registered\n", function);
else if (!acfptr->write)
ast_log(LOG_ERROR, "Function %s cannot be written to\n", function);
else
return acfptr->write(chan, function, args, value);
static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
/* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
zero-filled */
char *cp4;
const char *tmp, *whereweare;
Kevin P. Fleming
committed
int length, offset, offset2, isfunction;
char *nextvar, *nextexp, *nextthing;
int pos, brackets, needsub, len;
/* Assume we're copying the whole remaining string */
pos = strlen(whereweare);
nextvar = NULL;
nextexp = NULL;
nextthing = strchr(whereweare, '$');
if (nextthing) {
switch(nextthing[1]) {
case '{':
nextvar = nextthing;
pos = nextvar - whereweare;
break;
case '[':
nextexp = nextthing;
pos = nextexp - whereweare;
break;
}
if (pos) {
/* Can't copy more than 'count' bytes */
if (pos > count)
pos = count;
/* Copy that many bytes */
memcpy(cp2, whereweare, pos);
count -= pos;
cp2 += pos;
whereweare += pos;
}
/* We have a variable. Find the start and end, and determine
if we are going to have to recursively call ourselves on the
contents */
vars = vare = nextvar + 2;
while (brackets && *vare) {
if ((vare[0] == '$') && (vare[1] == '{')) {
needsub++;
} else if (vare[0] == '{') {
brackets++;
} else if (vare[0] == '}') {
brackets--;
} else if ((vare[0] == '$') && (vare[1] == '['))
if (brackets)
ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
len = vare - vars - 1;
/* Skip totally over variable string */
if (!var)
var = alloca(VAR_BUF_SIZE);
/* Store variable name (and truncate) */
ast_copy_string(var, vars, len + 1);
/* Substitute if necessary */
if (needsub) {
if (!ltmp)
ltmp = alloca(VAR_BUF_SIZE);
memset(ltmp, 0, VAR_BUF_SIZE);
pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
vars = ltmp;
} else {
vars = var;
if (!workspace)
workspace = alloca(VAR_BUF_SIZE);
workspace[0] = '\0';
parse_variable_name(vars, &offset, &offset2, &isfunction);
Kevin P. Fleming
committed
if (isfunction) {
/* Evaluate function */
cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
} else {
/* Retrieve variable value */
pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
Kevin P. Fleming
committed
cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
length = strlen(cp4);
if (length > count)
length = count;
memcpy(cp2, cp4, length);
count -= length;
cp2 += length;
} else if (nextexp) {
/* We have an expression. Find the start and end, and determine
if we are going to have to recursively call ourselves on the
contents */
vars = vare = nextexp + 2;
brackets = 1;
needsub = 0;
/* Find the end of it */
while(brackets && *vare) {
if ((vare[0] == '$') && (vare[1] == '[')) {
needsub++;
brackets++;
vare++;
} else if (vare[0] == '[') {
brackets++;
} else if (vare[0] == ']') {
brackets--;
} else if ((vare[0] == '$') && (vare[1] == '{')) {
vare++;
}
if (brackets)
ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
len = vare - vars - 1;
/* Skip totally over expression */
whereweare += (len + 3);
if (!var)
var = alloca(VAR_BUF_SIZE);
/* Store variable name (and truncate) */
ast_copy_string(var, vars, len + 1);
/* Substitute if necessary */
if (!ltmp)
ltmp = alloca(VAR_BUF_SIZE);
memset(ltmp, 0, VAR_BUF_SIZE);
pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
vars = ltmp;
} else {
vars = var;
}
length = ast_expr(vars, cp2, count);
if (length) {
ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
count -= length;
cp2 += length;
}
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
{
pbx_substitute_variables_helper_full(c, (c) ? &c->varshead : NULL, cp1, cp2, count);
}
void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
{
pbx_substitute_variables_helper_full(NULL, headp, cp1, cp2, count);
static void pbx_substitute_variables(char *passdata, int datalen, struct ast_channel *c, struct ast_exten *e)
{
memset(passdata, 0, datalen);
/* No variables or expressions in e->data, so why scan it? */
if (!strchr(e->data, '$') && !strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
ast_copy_string(passdata, e->data, datalen);
pbx_substitute_variables_helper(c, e->data, passdata, datalen - 1);
/*! \brief The return value depends on the action:
*
* E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
* and return 0 on failure, -1 on match;
* E_FINDLABEL maps the label to a priority, and returns
* the priority on success, ... XXX
* E_SPAWN, spawn an application,
* and return 0 on success, -1 on failure.
*/
static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
const char *context, const char *exten, int priority,
const char *label, const char *callerid, enum ext_match_t action)
struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
char passdata[EXT_DATA_SIZE];
int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
ast_mutex_lock(&conlock);
e = pbx_find_extension(c, con, &q, context, exten, priority, label, callerid, action);
ast_mutex_unlock(&conlock);
return -1; /* success, we found it */
} else if (action == E_FINDLABEL) { /* map the label to a priority */
res = e->priority;
ast_mutex_unlock(&conlock);
return res; /* the priority we were looking for */
} else { /* spawn */
ast_mutex_unlock(&conlock);
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
if (!app) {
ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
return -1;
}
if (c->context != context)
ast_copy_string(c->context, context, sizeof(c->context));
if (c->exten != exten)
ast_copy_string(c->exten, exten, sizeof(c->exten));
c->priority = priority;
pbx_substitute_variables(passdata, sizeof(passdata), c, e);
if (option_debug) {
char atmp[80];
char atmp2[EXT_DATA_SIZE+100];
ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
snprintf(atmp, sizeof(atmp), "STACK-%s-%s-%d", context, exten, priority);
snprintf(atmp2, sizeof(atmp2), "%s(\"%s\", \"%s\") %s",
app->name, c->name, passdata, "in new stack");
pbx_builtin_setvar_helper(c, atmp, atmp2);
}
if (option_verbose > 2) {
char tmp[80], tmp2[80], tmp3[EXT_DATA_SIZE];
ast_verbose( VERBOSE_PREFIX_3 "Executing [%s:%d] %s(\"%s\", \"%s\") %s\n",
context, priority,
term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
term_color(tmp3, passdata, COLOR_BRMAGENTA, 0, sizeof(tmp3)),
"in new stack");
}
manager_event(EVENT_FLAG_CALL, "Newexten",
"Channel: %s\r\n"
"Context: %s\r\n"
"Extension: %s\r\n"
"Priority: %d\r\n"
"Application: %s\r\n"
"AppData: %s\r\n"
"Uniqueid: %s\r\n",
c->name, c->context, c->exten, c->priority, app->name, passdata, c->uniqueid);
return pbx_exec(c, app, passdata); /* 0 on success, -1 on failure */
} else if (q.swo) { /* not found here, but in another switch */
ast_mutex_unlock(&conlock);
if (matching_action)
else {
if (!q.swo->exec) {
ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
} else { /* not found anywhere, see what happened */
ast_mutex_unlock(&conlock);
ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
break;
case STATUS_NO_EXTENSION:
ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
break;
case STATUS_NO_PRIORITY:
ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
break;
case STATUS_NO_LABEL:
if (context)
ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
default:
ast_log(LOG_DEBUG, "Shouldn't happen!\n");
}
return (matching_action) ? 0 : -1;
/*! \brief ast_hint_extension: Find hint for given extension in context */
static struct ast_exten *ast_hint_extension(struct ast_channel *c, const char *context, const char *exten)
{
struct ast_exten *e;
struct pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
ast_mutex_lock(&conlock);
e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
ast_mutex_unlock(&conlock);
return e;
}
/*! \brief ast_extensions_state2: Check state of extension by using hints */
static int ast_extension_state2(struct ast_exten *e)
{
char *cur, *rest;
int allunavailable = 1, allbusy = 1, allfree = 1;
Kevin P. Fleming
committed
int busy = 0, inuse = 0, ring = 0;
ast_copy_string(hint, ast_get_extension_app(e), sizeof(hint));
rest = hint; /* One or more devices separated with a & character */
while ( (cur = strsep(&rest, "&")) ) {
int res = ast_device_state(cur);
case AST_DEVICE_NOT_INUSE:
allunavailable = 0;
allbusy = 0;
break;
case AST_DEVICE_INUSE:
Kevin P. Fleming
committed
inuse = 1;
allunavailable = 0;
allfree = 0;
break;
case AST_DEVICE_RINGING:
ring = 1;
allunavailable = 0;
allfree = 0;
break;
case AST_DEVICE_RINGINUSE:
inuse = 1;
ring = 1;
allunavailable = 0;
allfree = 0;
break;
case AST_DEVICE_BUSY:
allunavailable = 0;
allfree = 0;
busy = 1;
break;
case AST_DEVICE_UNAVAILABLE:
case AST_DEVICE_INVALID:
allbusy = 0;
allfree = 0;
break;
allunavailable = 0;
allbusy = 0;
allfree = 0;
}
Kevin P. Fleming
committed
if (!inuse && ring)
return AST_EXTENSION_RINGING;
if (inuse && ring)
return (AST_EXTENSION_INUSE | AST_EXTENSION_RINGING);
if (inuse)
return AST_EXTENSION_INUSE;
if (allfree)
/*! \brief ast_extension_state2str: Return extension_state as string */
Kevin P. Fleming
committed
const char *ast_extension_state2str(int extension_state)
{
int i;
for (i = 0; (i < (sizeof(extension_states) / sizeof(extension_states[0]))); i++) {
if (extension_states[i].extension_state == extension_state)
Kevin P. Fleming
committed
return extension_states[i].text;
}
Kevin P. Fleming
committed
}
/*! \brief ast_extension_state: Check extension state for an extension by using hint */
int ast_extension_state(struct ast_channel *c, const char *context, const char *exten)
e = ast_hint_extension(c, context, exten); /* Do we have a hint for this extension ? */
if (!e)
return ast_extension_state2(e); /* Check all devices in the hint */
Kevin P. Fleming
committed
void ast_hint_state_changed(const char *device)
Kevin P. Fleming
committed
struct ast_hint *hint;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
Russell Bryant
committed
AST_LIST_TRAVERSE(&hints, hint, list) {
struct ast_state_cb *cblist;
char buf[AST_MAX_EXTENSION];
Kevin P. Fleming
committed
ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
while ( (cur = strsep(&parse, "&")) ) {
if (!strcasecmp(cur, device))
break;
}
if (!cur)
continue;
/* Get device state for this hint */
state = ast_extension_state2(hint->exten);
if ((state == -1) || (state == hint->laststate))
continue;
/* Device state changed since last check - notify the watchers */
/* For general callbacks */
for (cblist = statecbs; cblist; cblist = cblist->next)
cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
/* For extension callbacks */
for (cblist = hint->callbacks; cblist; cblist = cblist->next)
cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
hint->laststate = state; /* record we saw the change */
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/*! \brief ast_extension_state_add: Add watcher for extension states */
int ast_extension_state_add(const char *context, const char *exten,
ast_state_cb_type callback, void *data)
Russell Bryant
committed
struct ast_hint *hint;
struct ast_state_cb *cblist;
struct ast_exten *e;
/* If there's no context and extension: add callback to statecbs list */
Russell Bryant
committed
AST_LIST_LOCK(&hints);
for (cblist = statecbs; cblist; cblist = cblist->next) {
if (cblist->callback == callback) {
cblist->data = data;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
Russell Bryant
committed
if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
return -1;
}
cblist->id = 0;
cblist->callback = callback;
cblist->data = data;
cblist->next = statecbs;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
if (!context || !exten)
return -1;
/* This callback type is for only one hint, so get the hint */
/* Find the hint in the list of hints */
Russell Bryant
committed
AST_LIST_LOCK(&hints);
Russell Bryant
committed
AST_LIST_TRAVERSE(&hints, hint, list) {
if (hint->exten == e)
Russell Bryant
committed
if (!hint) {
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/* Now insert the callback in the callback list */
Russell Bryant
committed
if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
cblist->id = stateid++; /* Unique ID for this callback */
cblist->callback = callback; /* Pointer to callback routine */
cblist->data = data; /* Data for the callback */
Russell Bryant
committed
cblist->next = hint->callbacks;
hint->callbacks = cblist;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/*! \brief ast_extension_state_del: Remove a watcher from the callback list */
int ast_extension_state_del(int id, ast_state_cb_type callback)
{
struct ast_state_cb **p_cur = NULL; /* address of pointer to us */
int ret = -1;
if (!id && !callback)
return -1;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
if (!id) { /* id == 0 is a callback without extension */
for (p_cur = &statecbs; *p_cur; p_cur = &(*p_cur)->next) {
if ((*p_cur)->callback == callback)
break;
} else { /* callback with extension, find the callback based on ID */
struct ast_hint *hint;
AST_LIST_TRAVERSE(&hints, hint, list) {
for (p_cur = &hint->callbacks; *p_cur; p_cur = &(*p_cur)->next) {
break;
}
if (*p_cur) /* found in the inner loop */
break;
if (p_cur && *p_cur) {
struct ast_state_cb *cur = *p_cur;
*p_cur = cur->next;
free(cur);
ret = 0;