Newer
Older
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);
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
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);
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
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@%s:%d] %s(\"%s\", \"%s\") %s\n",
exten, 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;
}
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);