Newer
Older
return RESULT_SHOWUSAGE;
}
ast_cli(fd, "%s Custom Functions:\n--------------------------------------------------------------------------------\n", like ? "Matching" : "Installed");
for (acf = acf_root ; acf; acf = acf->next) {
print_acf = 0;
if (like) {
if (strstr(acf->name, argv[3])) {
print_acf = 1;
count_acf++;
}
} else {
print_acf = 1;
count_acf++;
}
if (print_acf) {
ast_cli(fd, "%-20.20s %-35.35s %s\n", acf->name, acf->syntax, acf->synopsis);
}
ast_cli(fd, "%d %scustom functions installed.\n", count_acf, like ? "matching " : "");
return 0;
}
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;
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
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);
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);
/* try to lock functions list ... */
if (ast_mutex_lock(&acflock)) {
ast_log(LOG_ERROR, "Unable to lock function list\n");
return NULL;
}
Russell Bryant
committed
/* case-insensitive for convenience in this 'complete' function */
for (acf = acf_root; acf && !ret; acf = acf->next) {
if (!strncasecmp(word, acf->name, wordlen) && ++which > state)
ret = strdup(acf->name);
}
ast_mutex_unlock(&acflock);
return ret;
struct ast_custom_function* ast_custom_function_find(const char *name)
{
struct ast_custom_function *acfptr;
/* try to lock functions list ... */
if (ast_mutex_lock(&acflock)) {
ast_log(LOG_ERROR, "Unable to lock function list\n");
return NULL;
}
for (acfptr = acf_root; acfptr; acfptr = acfptr->next) {
Russell Bryant
committed
if (!strcmp(name, acfptr->name))
}
ast_mutex_unlock(&acflock);
return acfptr;
}
int ast_custom_function_unregister(struct ast_custom_function *acf)
struct ast_custom_function *acfptr, *lastacf = NULL;
int res = -1;
if (!acf)
return -1;
/* try to lock functions list ... */
if (ast_mutex_lock(&acflock)) {
ast_log(LOG_ERROR, "Unable to lock function list\n");
return -1;
}
for (acfptr = acf_root; acfptr; acfptr = acfptr->next) {
if (acfptr == acf) {
if (lastacf) {
lastacf->next = acf->next;
} else {
acf_root = acf->next;
res = 0;
break;
ast_mutex_unlock(&acflock);
if (!res && (option_verbose > 1))
ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
return res;
int ast_custom_function_register(struct ast_custom_function *acf)
struct ast_custom_function *cur, *last = NULL;
int found = 0;
if (!acf)
return -1;
/* try to lock functions list ... */
if (ast_mutex_lock(&acflock)) {
ast_log(LOG_ERROR, "Unable to lock function list. Failed registering function %s\n", acf->name);
if (ast_custom_function_find(acf->name)) {
ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
ast_mutex_unlock(&acflock);
return -1;
}
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
for (cur = acf_root; cur; cur = cur->next) {
if (strcmp(acf->name, cur->name) < 0) {
found = 1;
if (last) {
acf->next = cur;
last->next = acf;
} else {
acf->next = acf_root;
acf_root = acf;
}
break;
}
last = cur;
}
/* Wasn't before anything else, put it at the end */
if (!found) {
if (last)
last->next = acf;
else
acf_root = acf;
acf->next = NULL;
}
ast_mutex_unlock(&acflock);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Registered custom function %s\n", acf->name);
return 0;
char *ast_func_read(struct ast_channel *chan, const char *in, char *workspace, size_t len)
{
char *args = NULL, *function, *p;
char *ret = "0";
struct ast_custom_function *acfptr;
if (!(function = ast_strdupa(in)))
Russell Bryant
committed
return ret;
if ((args = strchr(function, '('))) {
*args = '\0';
args++;
if ((p = strrchr(args, ')'))) {
*p = '\0';
ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
} else {
ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
}
if ((acfptr = ast_custom_function_find(function))) {
/* run the custom function */
if (acfptr->read) {
return acfptr->read(chan, function, args, workspace, len);
ast_log(LOG_ERROR, "Function %s cannot be read\n", function);
ast_log(LOG_ERROR, "Function %s not registered\n", function);
}
return ret;
}
Anthony Minessale II
committed
void ast_func_write(struct ast_channel *chan, const char *in, const char *value)
{
char *args = NULL, *function, *p;
struct ast_custom_function *acfptr;
if (!(function = ast_strdupa(in)))
Russell Bryant
committed
return;
if ((args = strchr(function, '('))) {
*args = '\0';
args++;
if ((p = strrchr(args, ')'))) {
*p = '\0';
ast_log(LOG_WARNING, "Can't find trailing parenthesis?\n");
} else {
ast_log(LOG_WARNING, "Function doesn't contain parentheses. Assuming null argument.\n");
}
if ((acfptr = ast_custom_function_find(function))) {
/* run the custom function */
if (acfptr->write) {
acfptr->write(chan, function, args, value);
ast_log(LOG_ERROR, "Function %s is read-only, it cannot be written to\n", function);
ast_log(LOG_ERROR, "Function %s not registered\n", function);
}
}
static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
char *cp4;
const char *tmp, *whereweare;
Kevin P. Fleming
committed
int length, offset, offset2, isfunction;
char *nextvar, *nextexp, *nextthing;
int pos, brackets, needsub, len;
/* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
zero-filled */
whereweare=tmp=cp1;
while(!ast_strlen_zero(whereweare) && count) {
/* 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;
/* Find the end of it */
while(brackets && *vare) {
if ((vare[0] == '$') && (vare[1] == '{')) {
needsub++;
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);
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);
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, int action)
const char *foundcontext=NULL;
int status = 0;
char *incstack[AST_PBX_MAX_STACK];
char passdata[EXT_DATA_SIZE];
char tmp3[EXT_DATA_SIZE];
char atmp[80];
char atmp2[EXT_DATA_SIZE+100];
if (ast_mutex_lock(&conlock)) {
if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH) || (action == HELPER_MATCHMORE))
e = pbx_find_extension(c, con, context, exten, priority, label, callerid, action, incstack, &stacklen, &status, &sw, &data, &foundcontext);
if (e) {
switch(action) {
case HELPER_CANMATCH:
ast_mutex_unlock(&conlock);
ast_mutex_unlock(&conlock);
case HELPER_FINDLABEL:
res = e->priority;
ast_mutex_unlock(&conlock);
return res;
ast_mutex_unlock(&conlock);
case HELPER_SPAWN:
newstack++;
/* Fall through */
case HELPER_EXEC:
app = pbx_findapp(e->app);
ast_mutex_unlock(&conlock);
ast_copy_string(c->context, context, sizeof(c->context));
ast_copy_string(c->exten, exten, sizeof(c->exten));
pbx_substitute_variables(passdata, sizeof(passdata), c, e);
if (option_debug) {
ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
snprintf(atmp, 80, "STACK-%s-%s-%d", context, exten, priority);
snprintf(atmp2, EXT_DATA_SIZE+100, "%s(\"%s\", \"%s\") %s", app->name, c->name, passdata, (newstack ? "in new stack" : "in same stack"));
pbx_builtin_setvar_helper(c, atmp, atmp2);
}
ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n",
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)),
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);
res = pbx_exec(c, app, passdata, newstack);
return res;
} else {
ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
return -1;
}
default:
ast_log(LOG_WARNING, "Huh (%d)?\n", action);
return -1;
} else if (sw) {
switch(action) {
case HELPER_CANMATCH:
ast_mutex_unlock(&conlock);
ast_mutex_unlock(&conlock);
ast_mutex_unlock(&conlock);
case HELPER_FINDLABEL:
ast_mutex_unlock(&conlock);
return -1;
case HELPER_SPAWN:
newstack++;
/* Fall through */
case HELPER_EXEC:
ast_mutex_unlock(&conlock);
res = sw->exec(c, foundcontext ? foundcontext : context, exten, priority, callerid, newstack, data);
else {
ast_log(LOG_WARNING, "No execution engine for switch %s\n", sw->name);
res = -1;
}
return res;
default:
ast_log(LOG_WARNING, "Huh (%d)?\n", action);
return -1;
}
ast_mutex_unlock(&conlock);
if ((action != HELPER_EXISTS) && (action != HELPER_MATCHMORE))
ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
break;
case STATUS_NO_EXTENSION:
if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
break;
case STATUS_NO_PRIORITY:
if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
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");
}
if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
/*! \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 ast_switch *sw;
char *data;
const char *foundcontext = NULL;
int status = 0;
char *incstack[AST_PBX_MAX_STACK];
int stacklen = 0;
if (ast_mutex_lock(&conlock)) {
ast_log(LOG_WARNING, "Unable to obtain lock\n");
return NULL;
}
e = pbx_find_extension(c, NULL, context, exten, PRIORITY_HINT, NULL, "", HELPER_EXISTS, incstack, &stacklen, &status, &sw, &data, &foundcontext);
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 hint[AST_MAX_EXTENSION] = "";
char *cur, *rest;
int res = -1;
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));
cur = hint; /* On or more devices separated with a & character */
do {
rest = strchr(cur, '&');
if (rest) {
Kevin P. Fleming
committed
*rest = 0;
res = ast_device_state(cur);
switch (res) {
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_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) {
return extension_states[i].text;
}
}
return "Unknown";
}
/*! \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 ? */
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;
struct ast_state_cb *cblist;
Kevin P. Fleming
committed
char buf[AST_MAX_EXTENSION];
char *parse;
char *cur;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
Russell Bryant
committed
AST_LIST_TRAVERSE(&hints, hint, list) {
Kevin P. Fleming
committed
ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
parse = buf;
for (cur = strsep(&parse, "&"); cur; cur = strsep(&parse, "&")) {
Kevin P. Fleming
committed
continue;
Kevin P. Fleming
committed
/* Get device state for this hint */
state = ast_extension_state2(hint->exten);
Kevin P. Fleming
committed
if ((state == -1) || (state == hint->laststate))
continue;
Kevin P. Fleming
committed
/* Device state changed since last check - notify the watchers */
Kevin P. Fleming
committed
/* For general callbacks */
for (cblist = statecbs; cblist; cblist = cblist->next)
cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
Kevin P. Fleming
committed
/* 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;
break;
}
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
Kevin P. Fleming
committed
/*! \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
AST_LIST_UNLOCK(&hints);
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 */
e = ast_hint_extension(NULL, context, exten);
if (!e) {
return -1;
/* 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 */
if (!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)
{
Russell Bryant
committed
struct ast_hint *hint;
struct ast_state_cb *cblist, *cbprev;
if (!id && !callback)
return -1;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
/* id is zero is a callback without extension */
if (!id) {
cbprev = NULL;
for (cblist = statecbs; cblist; cblist = cblist->next) {
if (cblist->callback == callback) {
if (!cbprev)
statecbs = cblist->next;
else
cbprev->next = cblist->next;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
return 0;
}
cbprev = cblist;
}
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/* id greater than zero is a callback with extension */
Russell Bryant
committed
AST_LIST_TRAVERSE(&hints, hint, list) {
Russell Bryant
committed
for (cblist = hint->callbacks; cblist; cblist = cblist->next) {
if (cblist->id==id) {
if (!cbprev)
Russell Bryant
committed
hint->callbacks = cblist->next;
else
cbprev->next = cblist->next;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
cbprev = cblist;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/*! \brief ast_add_hint: Add hint to hint list, check initial extension state */
static int ast_add_hint(struct ast_exten *e)
{
Russell Bryant
committed
struct ast_hint *hint;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
/* Search if hint exists, do nothing */
Russell Bryant
committed
AST_LIST_TRAVERSE(&hints, hint, list) {
if (hint->exten == e) {
AST_LIST_UNLOCK(&hints);
if (option_debug > 1)
ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
if (option_debug > 1)
ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
Russell Bryant
committed
hint = calloc(1, sizeof(struct ast_hint));
if (!hint) {
AST_LIST_UNLOCK(&hints);
if (option_debug > 1)
ast_log(LOG_DEBUG, "HINTS: Out of memory...\n");
/* Initialize and insert new item at the top */
Russell Bryant
committed
hint->exten = e;
hint->laststate = ast_extension_state2(e);
AST_LIST_INSERT_HEAD(&hints, hint, list);
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
/*! \brief ast_change_hint: Change hint for an extension */
static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
{
Russell Bryant
committed
struct ast_hint *hint;
int res = -1;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
AST_LIST_TRAVERSE(&hints, hint, list) {
if (hint->exten == oe) {
hint->exten = ne;
res = 0;
break;
Russell Bryant
committed
AST_LIST_UNLOCK(&hints);
Russell Bryant
committed
return res;
/*! \brief ast_remove_hint: Remove hint from extension */
static int ast_remove_hint(struct ast_exten *e)
/* Cleanup the Notifys if hint is removed */
Russell Bryant
committed
struct ast_hint *hint;
struct ast_state_cb *cblist, *cbprev;
Russell Bryant
committed
int res = -1;
Russell Bryant
committed
AST_LIST_LOCK(&hints);
AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
if (hint->exten == e) {
Russell Bryant
committed
cblist = hint->callbacks;
while (cblist) {
/* Notify with -1 and remove all callbacks */
cbprev = cblist;
cblist = cblist->next;
Russell Bryant
committed
cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
Russell Bryant
committed
hint->callbacks = NULL;