Skip to content
Snippets Groups Projects
pbx.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		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)))
    
    				return e;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return NULL;
    }
    
    
    /* 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)
     */
    
    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 == '(') {
    
    		} else if (*var == ':' && parens == 0) {
    			*var++ = '\0';
    			sscanf(var, "%d:%d", offset, length);
    			return 1; /* offset:length valid */
    
    /*! \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)
    
    	int lr;	/* length of the input string after the copy */
    
    	ast_copy_string(workspace, value, workspace_len); /* always make a copy */
    
    	/* Quick check if no need to do anything */
    
    	if (offset == 0 && length < 0)	/* take the whole string */
    		return ret;
    
    	lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
    
    	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;
    
    	/* too large offset result in empty string so we know what to return */
    	if (offset >= lr)
    		return ret + lr;	/* the final '\0' */
    
    	ret += offset;		/* move to the start position */
    	if (length >= 0 && length < lr - offset)	/* truncate if necessary */
    
    /*! \brief  pbx_retrieve_variable: Support for Asterisk built-in variables and
    
          functions in the dialplan
      ---*/
    
    void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
    
    	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 == &not_found (set at the beginning) means that we did not find a
    
    	 *	matching variable and need to look into more places.
    
    	 * If s != &not_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 = &not_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 == &not_found) { /* look for more */
    		if (!strcmp(var, "EPOCH")) {
    			snprintf(workspace, workspacelen, "%u",(int)time(NULL));
    			s = workspace;
    
    		} else if (!strcmp(var, "SYSTEMNAME")) {
    
    		}
    	}
    	/* if not found, look into chanvars or global vars */
    	for (i = 0; s == &not_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 == &not_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);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    /*! \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;
    
    	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 " : "");
    
    
    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);
    
    	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;
    }
    
    
    static char *complete_show_function(const char *line, const char *word, int pos, int state)
    
    {
    	struct ast_custom_function *acf;
    
    	int wordlen = strlen(word);
    
    	/* 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) {
    
    	AST_LIST_UNLOCK(&acf_root);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	return ret;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    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);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    int ast_custom_function_unregister(struct ast_custom_function *acf)
    
    	struct ast_custom_function *cur;
    
    	AST_LIST_LOCK(&acf_root);
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
    
    		if (cur == acf) {
    
    			AST_LIST_REMOVE_CURRENT(&acf_root, acflist);
    			if (option_verbose > 1)
    				ast_verbose(VERBOSE_PREFIX_2 "Unregistered custom function %s\n", acf->name);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	AST_LIST_UNLOCK(&acf_root);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    int ast_custom_function_register(struct ast_custom_function *acf)
    
    	struct ast_custom_function *cur;
    
    	if (ast_custom_function_find(acf->name)) {
    		ast_log(LOG_ERROR, "Function %s already registered.\n", acf->name);
    
    		AST_LIST_UNLOCK(&acf_root);
    
    	/* Store in alphabetical order */
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&acf_root, cur, acflist) {
    		if (strcasecmp(acf->name, cur->name) < 0) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			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,
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
     * and terminates the function name with '\\0'
    
     */
    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;
    
    		*args++ = '\0';
    
    		if ((p = strrchr(args, ')')) )
    
    			*p = '\0';
    
    			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);
    
    	return -1;
    
    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);
    
    	if (acfptr == NULL)
    
    		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;
    
    	int length, offset, offset2, isfunction;
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	char *workspace = NULL;
    	char *ltmp = NULL, *var = NULL;
    
    	char *nextvar, *nextexp, *nextthing;
    
    	char *vars, *vare;
    
    	int pos, brackets, needsub, len;
    
    	whereweare=tmp=cp1;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	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;
    
    				pos = nextexp - whereweare;
    
    
    		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;
    
    			brackets = 1;
    			needsub = 0;
    
    			/* Find the end of it */
    
    				if ((vare[0] == '$') && (vare[1] == '{')) {
    					needsub++;
    
    				} else if (vare[0] == '{') {
    					brackets++;
    
    				} else if (vare[0] == '}') {
    					brackets--;
    
    				} else if ((vare[0] == '$') && (vare[1] == '['))
    
    					needsub++;
    				vare++;
    
    			if (brackets)
    				ast_log(LOG_NOTICE, "Error in extension logic (missing '}')\n");
    			len = vare - vars - 1;
    
    			/* Skip totally over variable string */
    
    			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 (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);
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			parse_variable_name(vars, &offset, &offset2, &isfunction);
    
    				/* Evaluate function */
    
    				cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
    
    				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);
    
    				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);
    
    			length = ast_expr(vars, cp2, count);
    
    			if (length) {
    				ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
    
    Mark Spencer's avatar
    Mark Spencer committed
    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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    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? */
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_exten *e;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_app *app;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    	struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
    
    	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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (e) {
    
    		if (matching_action) {
    
    			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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			app = pbx_findapp(e->app);
    
    			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",
    
    Russell Bryant's avatar
    Russell Bryant committed
    					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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    	} 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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				res = -1;
    			}
    
    			return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    	} else {	/* not found anywhere, see what happened */
    
    		switch (q.status) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		case STATUS_NO_CONTEXT:
    
    			if (!matching_action)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
    			break;
    		case STATUS_NO_EXTENSION:
    
    			if (!matching_action)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
    			break;
    		case STATUS_NO_PRIORITY:
    
    			if (!matching_action)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
    			break;
    
    			if (context)
    				ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		default:
    			ast_log(LOG_DEBUG, "Shouldn't happen!\n");
    		}
    
    		return (matching_action) ? 0 : -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*! \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 pbx_find_info q = { .stacklen = 0 }; /* the rest is set in pbx_find_context */
    
    	e = pbx_find_extension(c, NULL, &q, context, exten, PRIORITY_HINT, NULL, "", E_MATCH);
    
    /*! \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 allunavailable = 1, allbusy = 1, allfree = 1;
    
    	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);
    
    		switch (res) {
    
    		case AST_DEVICE_NOT_INUSE:
    
    			allunavailable = 0;
    			allbusy = 0;
    			break;
    
    			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;
    
    			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;
    		}
    
    	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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return AST_EXTENSION_NOT_INUSE;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (allbusy)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return AST_EXTENSION_BUSY;
    
    	if (allunavailable)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return AST_EXTENSION_UNAVAILABLE;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (busy)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return AST_EXTENSION_INUSE;
    
    	return AST_EXTENSION_NOT_INUSE;
    
    /*! \brief  ast_extension_state2str: Return extension_state as string */
    
    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)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	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)
    
    	struct ast_exten *e;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	e = ast_hint_extension(c, context, exten);	/* Do we have a hint for this extension ? */
    	if (!e)
    
    		return -1;				/* No hint, return -1 */
    
    	return ast_extension_state2(e);    		/* Check all devices in the hint */
    
    void ast_hint_state_changed(const char *device)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		struct ast_state_cb *cblist;
    		char buf[AST_MAX_EXTENSION];
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		char *parse = buf;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		char *cur;
    		int state;
    
    
    		ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		while ( (cur = strsep(&parse, "&")) ) {
    			if (!strcasecmp(cur, device))
    				break;
    		}
    		if (!cur)
    			continue;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* Get device state for this hint */
    		state = ast_extension_state2(hint->exten);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if ((state == -1) || (state == hint->laststate))
    			continue;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* Device state changed since last check - notify the watchers */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* For general callbacks */
    		for (cblist = statecbs; cblist; cblist = cblist->next)
    			cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* For extension callbacks */
    		for (cblist = hint->callbacks; cblist; cblist = cblist->next)
    			cblist->callback(hint->exten->parent->name, hint->exten->exten, state, cblist->data);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    
    		hint->laststate = state;	/* record we saw the change */
    
    /*! \brief  ast_extension_state_add: Add watcher for extension states */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    int ast_extension_state_add(const char *context, const char *exten,
    
    			    ast_state_cb_type callback, void *data)
    
    	struct ast_state_cb *cblist;
    	struct ast_exten *e;
    
    	/* If there's no context and extension:  add callback to statecbs list */
    
    	if (!context && !exten) {
    
    Russell Bryant's avatar
    Russell Bryant committed
    		for (cblist = statecbs; cblist; cblist = cblist->next) {
    
    			if (cblist->callback == callback) {
    				cblist->data = data;
    
    		/* Now insert the callback */
    
    		if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			AST_LIST_UNLOCK(&hints);
    
    			return -1;
    		}
    		cblist->id = 0;
    		cblist->callback = callback;
    
    		cblist->next = statecbs;
    
    		statecbs = cblist;
    
    
    
    	if (!context || !exten)
    		return -1;
    
    
    	/* This callback type is for only one hint, so get the hint */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	e = ast_hint_extension(NULL, context, exten);
    
    	if (!e) {
    		return -1;
    
    	/* Find the hint in the list of hints */
    
    	AST_LIST_TRAVERSE(&hints, hint, list) {
    		if (hint->exten == e)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			break;
    
    		/* We have no hint, sorry */
    
    	/* Now insert the callback in the callback list  */
    
    	if (!(cblist = ast_calloc(1, sizeof(*cblist)))) {
    
    		return -1;
    
    	cblist->id = stateid++;		/* Unique ID for this callback */
    	cblist->callback = callback;	/* Pointer to callback routine */
    	cblist->data = data;		/* Data for the callback */
    
    	cblist->next = hint->callbacks;
    	hint->callbacks = cblist;
    
    	return cblist->id;
    
    /*! \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;
    
    	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) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				if ((*p_cur)->id == id)
    
    					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;