Skip to content
Snippets Groups Projects
pbx.c 168 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				snprintf(workspace, workspacelen, "%d", c->cid.cid_ani2);
    				*ret = workspace;
    			} else if (!strcmp(var + 7, "TON")) {
    				/* CALLINGTON */
    				snprintf(workspace, workspacelen, "%d", c->cid.cid_ton);
    				*ret = workspace;
    			} else if (!strcmp(var + 7, "TNS")) {
    				/* CALLINGTNS */
    				snprintf(workspace, workspacelen, "%d", c->cid.cid_tns);
    				*ret = workspace;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else
    				goto icky;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else if (c && !strcmp(var, "DNID")) {
    
    		if (c->cid.cid_dnid) {
    
    			ast_copy_string(workspace, c->cid.cid_dnid, workspacelen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*ret = workspace;
    		} else
    			*ret = NULL;
    
    	} else if (c && !strcmp(var, "HINT")) {
    
    		if (!ast_get_hint(workspace, workspacelen, NULL, 0, c, c->context, c->exten))
    			*ret = NULL;
    		else
    			*ret = workspace;
    	} else if (c && !strcmp(var, "HINTNAME")) {
    		if (!ast_get_hint(NULL, 0, workspace, workspacelen, c, c->context, c->exten))
    
    			*ret = NULL;
    
    			*ret = workspace;
    
    	} else if (c && !strcmp(var, "EXTEN")) {
    
    		ast_copy_string(workspace, c->exten, workspacelen);
    
    		*ret = workspace;
    
    	} else if (c && !strcmp(var, "RDNIS")) {
    
    		if (c->cid.cid_rdnis) {
    
    			ast_copy_string(workspace, c->cid.cid_rdnis, workspacelen);
    
    			*ret = workspace;
    		} else
    			*ret = NULL;
    
    	} else if (c && !strcmp(var, "CONTEXT")) {
    
    		ast_copy_string(workspace, c->context, workspacelen);
    
    		*ret = workspace;
    
    	} else if (c && !strcmp(var, "PRIORITY")) {
    
    		snprintf(workspace, workspacelen, "%d", c->priority);
    
    		*ret = workspace;
    
    	} else if (c && !strcmp(var, "CHANNEL")) {
    
    		ast_copy_string(workspace, c->name, workspacelen);
    
    		*ret = workspace;
    
    	} else if (!strcmp(var, "EPOCH")) {
    
    		snprintf(workspace, workspacelen, "%u",(int)time(NULL));
    
    	} else if (!strcmp(var, "DATETIME")) {
    
    		thistime=time(NULL);
    		localtime_r(&thistime, &brokentime);
    
    		snprintf(workspace, workspacelen, "%02d%02d%04d-%02d:%02d:%02d",
    
    			brokentime.tm_mday,
    			brokentime.tm_mon+1,
    			brokentime.tm_year+1900,
    			brokentime.tm_hour,
    			brokentime.tm_min,
    			brokentime.tm_sec
    		);
    		*ret = workspace;
    
    	} else if (!strcmp(var, "TIMESTAMP")) {
    
    		thistime=time(NULL);
    		localtime_r(&thistime, &brokentime);
    
    		snprintf(workspace, workspacelen, "%04d%02d%02d-%02d%02d%02d",
    
    			brokentime.tm_year+1900,
    			brokentime.tm_mon+1,
    			brokentime.tm_mday,
    			brokentime.tm_hour,
    			brokentime.tm_min,
    			brokentime.tm_sec
    		);
    		*ret = workspace;
    
    	} else if (c && !strcmp(var, "UNIQUEID")) {
    
    		snprintf(workspace, workspacelen, "%s", c->uniqueid);
    
    		*ret = workspace;
    	} else if (c && !strcmp(var, "HANGUPCAUSE")) {
    
    		snprintf(workspace, workspacelen, "%d", c->hangupcause);
    
    		*ret = workspace;
    	} else if (c && !strcmp(var, "ACCOUNTCODE")) {
    
    		ast_copy_string(workspace, c->accountcode, workspacelen);
    
    		*ret = workspace;
    	} else if (c && !strcmp(var, "LANGUAGE")) {
    
    		ast_copy_string(workspace, c->language, workspacelen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (headp) {
    
    			AST_LIST_TRAVERSE(headp,variables,entries) {
    
    				ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",var,ast_var_name(variables));
    
    				if (strcasecmp(ast_var_name(variables),var)==0) {
    					*ret=ast_var_value(variables);
    					if (*ret) {
    
    						ast_copy_string(workspace, *ret, workspacelen);
    
    		if (!(*ret)) {
    
    			/* Try globals */
    			AST_LIST_TRAVERSE(&globals,variables,entries) {
    #if 0
    
    				ast_log(LOG_WARNING,"Comparing variable '%s' with '%s'\n",var,ast_var_name(variables));
    
    				if (strcasecmp(ast_var_name(variables),var)==0) {
    
    					*ret = ast_var_value(variables);
    
    					if (*ret) {
    
    						ast_copy_string(workspace, *ret, workspacelen);
    
    						*ret = workspace;
    					}
    				}
    
    static int handle_show_functions(int fd, int argc, char *argv[])
    {
    
    	struct ast_custom_function *acf;
    
    
    	ast_cli(fd, "Installed Custom Functions:\n--------------------------------------------------------------------------------\n");
    
    	for (acf = acf_root ; acf; acf = acf->next) {
    
    		ast_cli(fd, "%-20.20s  %-35.35s  %s\n", acf->name, acf->syntax, acf->synopsis);
    
    	ast_cli(fd, "%d custom functions installed.\n", count_acf);
    
    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(char *line, char *word, int pos, int state)
    {
    	struct ast_custom_function *acf;
    	int which = 0;
    
    	/* try to lock functions list ... */
    	if (ast_mutex_lock(&acflock)) {
    		ast_log(LOG_ERROR, "Unable to lock function list\n");
    		return NULL;
    	}
    
    	acf = acf_root;
    	while (acf) {
    		if (!strncasecmp(word, acf->name, strlen(word))) {
    			if (++which > state) {
    				char *ret = strdup(acf->name);
    				ast_mutex_unlock(&acflock);
    				return ret;
    			}
    		}
    		acf = acf->next; 
    	}
    
    	ast_mutex_unlock(&acflock);
    	return NULL; 
    }
    
    struct ast_custom_function* ast_custom_function_find(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) {
    
    		if (!strcmp(name, acfptr->name)) {
    			break;
    		}
    
    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;
    
    
    	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) 
    
    	/* 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;
    	}
    
    	acf->next = acf_root;
    	acf_root = acf;
    
    	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;
    
    	function = ast_strdupa(in);
    
    	if (!function) {
    		ast_log(LOG_ERROR, "Out of memory\n");
    		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);
    
    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_log(LOG_ERROR, "Out of memory\n");
    		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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void pbx_substitute_variables_helper_full(struct ast_channel *c, const char *cp1, char *cp2, int count, struct varshead *headp)
    
    	char *cp4;
    	const char *tmp, *whereweare;
    	int length;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char workspace[4096];
    	char ltmp[4096], var[4096];
    
    	char *nextvar, *nextexp, *nextthing;
    
    	char *vars, *vare;
    
    	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;
    				break;
    			case '[':
    				nextexp = nextthing;
    				break;
    			}
    
    		/* If there is one, we only go that far */
    
    		if (nextvar)
    			pos = nextvar - whereweare;
    		else if (nextexp)
    			pos = nextexp - whereweare;
    
    		/* 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 */
    			while(brackets && *vare) {
    				if ((vare[0] == '$') && (vare[1] == '{')) {
    					needsub++;
    					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 name */
    
    			whereweare += (len + 3);
    
    			/* Store variable name (and truncate) */
    			memset(var, 0, sizeof(var));
    
    			ast_copy_string(var, vars, sizeof(var));
    
    			var[len] = '\0';
    
    			/* Substitute if necessary */
    			if (needsub) {
    				memset(ltmp, 0, sizeof(ltmp));
    				pbx_substitute_variables_helper(c, var, ltmp, sizeof(ltmp) - 1);
    				vars = ltmp;
    			} else {
    				vars = var;
    
    
    			if (var[len - 1] == ')') {
    				/* Evaluate function */
    				cp4 = ast_func_read(c, vars, workspace, sizeof(workspace));
    
    
    				ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
    
    			} else {
    				/* Retrieve variable value */
    				pbx_retrieve_variable(c, vars, &cp4, workspace, sizeof(workspace), headp);
    			}
    
    			if (cp4) {
    				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 variable name */
    			whereweare += ( len + 3);
    			
    			/* Store variable name (and truncate) */
    			memset(var, 0, sizeof(var));
    
    			ast_copy_string(var, vars, sizeof(var));
    
    			var[len] = '\0';
    			
    			/* Substitute if necessary */
    
    				memset(ltmp, 0, sizeof(ltmp));
    				pbx_substitute_variables_helper(c, var, ltmp, sizeof(ltmp) - 1);
    				vars = ltmp;
    			} else {
    				vars = var;
    			}
    
    			/* Evaluate expression */			
    			cp4 = ast_expr(vars);
    			
    
    			ast_log(LOG_DEBUG, "Expression is '%s'\n", cp4);
    
    			if (cp4) {
    				length = strlen(cp4);
    				if (length > count)
    					length = count;
    				memcpy(cp2, cp4, length);
    				count -= length;
    				cp2 += length;
    				free(cp4);
    			}
    
    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, cp1, cp2, count, NULL);
    }
    
    void pbx_substitute_variables_varshead(struct varshead *headp, const char *cp1, char *cp2, int count)
    {
    	pbx_substitute_variables_helper_full(NULL, cp1, cp2, count, headp);
    }
    
    
    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 (!strstr(e->data,"${") && !strstr(e->data,"$[") && !strstr(e->data,"$(")) {
    
    		ast_copy_string(passdata, e->data, datalen);
    
    		passdata[datalen-1] = '\0';
    		return;
    	}
    	
    
    	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) 
    
    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
    	struct ast_switch *sw;
    	char *data;
    
    	const char *foundcontext=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int newstack = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int status = 0;
    	char *incstack[AST_PBX_MAX_STACK];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int stacklen = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char tmp[80];
    	char tmp2[80];
    
    	char atmp[80];
    	char atmp2[EXT_DATA_SIZE+100];
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to obtain lock\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((action == HELPER_EXISTS) || (action == HELPER_CANMATCH) || (action == HELPER_MATCHMORE))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		else
    			return -1;
    	}
    
    	e = pbx_find_extension(c, con, context, exten, priority, label, callerid, action, incstack, &stacklen, &status, &sw, &data, &foundcontext);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (e) {
    		switch(action) {
    		case HELPER_CANMATCH:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		case HELPER_EXISTS:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    		case HELPER_FINDLABEL:
    			res = e->priority;
    			ast_mutex_unlock(&conlock);
    			return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		case HELPER_MATCHMORE:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		case HELPER_SPAWN:
    			newstack++;
    			/* Fall through */
    		case HELPER_EXEC:
    			app = pbx_findapp(e->app);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (app) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (c->context != context)
    
    					ast_copy_string(c->context, context, sizeof(c->context));
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (c->exten != exten)
    
    					ast_copy_string(c->exten, exten, sizeof(c->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    				c->priority = priority;
    
    				pbx_substitute_variables(passdata, sizeof(passdata), c, e);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						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, (!ast_strlen_zero(passdata) ? (char *)passdata : ""), (newstack ? "in new stack" : "in same stack"));
    						pbx_builtin_setvar_helper(c, atmp, atmp2);
    				}
    
    				if (option_verbose > 2)
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_verbose( VERBOSE_PREFIX_3 "Executing %s(\"%s\", \"%s\") %s\n", 
    
    Mark Spencer's avatar
    Mark Spencer committed
    								term_color(tmp, app->name, COLOR_BRCYAN, 0, sizeof(tmp)),
    								term_color(tmp2, c->name, COLOR_BRMAGENTA, 0, sizeof(tmp2)),
    
    								term_color(tmp3, (!ast_strlen_zero(passdata) ? (char *)passdata : ""), COLOR_BRMAGENTA, 0, sizeof(tmp3)),
    
    Mark Spencer's avatar
    Mark Spencer committed
    								(newstack ? "in new stack" : "in same 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 ? passdata : "(NULL)", c->uniqueid);
    
    				res = pbx_exec(c, app, passdata, newstack);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return res;
    			} else {
    				ast_log(LOG_WARNING, "No application '%s' for extension (%s, %s, %d)\n", e->app, context, exten, priority);
    				return -1;
    			}
    		default:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Huh (%d)?\n", action);			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else if (sw) {
    		switch(action) {
    		case HELPER_CANMATCH:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		case HELPER_EXISTS:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		case HELPER_MATCHMORE:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    
    		case HELPER_FINDLABEL:
    			ast_mutex_unlock(&conlock);
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		case HELPER_SPAWN:
    			newstack++;
    			/* Fall through */
    		case HELPER_EXEC:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (sw->exec)
    
    				res = sw->exec(c, foundcontext ? foundcontext : context, exten, priority, callerid, newstack, data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		switch(status) {
    		case STATUS_NO_CONTEXT:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((action != HELPER_EXISTS) && (action != HELPER_MATCHMORE))
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
    			break;
    		case STATUS_NO_EXTENSION:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((action != HELPER_EXISTS) && (action !=  HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
    
    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:
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((action != HELPER_EXISTS) && (action !=  HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
    
    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");
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((action != HELPER_EXISTS) && (action != HELPER_CANMATCH) && (action != HELPER_MATCHMORE))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		else
    			return 0;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*--- 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;
    
    
    		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_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;
    	int busy = 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) {
    	    		*rest = 0;
    			rest++;
    		}
    
    		res = ast_device_state(cur);
    		switch (res) {
    
    		case AST_DEVICE_NOT_INUSE:
    
    			allunavailable = 0;
    			allbusy = 0;
    			break;
    
    			return AST_EXTENSION_INUSE;
    
    			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 (allfree)			
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return AST_EXTENSION_NOT_INUSE;
    
    	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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return AST_EXTENSION_INUSE;
    	
    
    	return AST_EXTENSION_NOT_INUSE;
    
    /*--- ast_extension_state: Check extension state for an extension by using hint */
    
    int ast_extension_state(struct ast_channel *c, char *context, char *exten)
    {
    
    	struct ast_exten *e;
    
    	e = ast_hint_extension(c, context, exten);	/* Do we have a hint for this extension ? */ 
    
    		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)
    
    	struct ast_state_cb *cblist;
    
    	char buf[AST_MAX_EXTENSION];
    	char *parse;
    	char *cur;
    
    	for (hint = hints; hint; hint = hint->next) {
    		ast_copy_string(buf, ast_get_extension_app(hint->exten), sizeof(buf));
    		parse = buf;
    		for (cur = strsep(&parse, "&"); cur; cur = strsep(&parse, "&")) {
    			if (strcmp(cur, device))
    				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;
    			break;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	ast_mutex_unlock(&hintlock);
    }
    
    /*--- 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)
    
    	struct ast_hint *list;
    	struct ast_state_cb *cblist;
    	struct ast_exten *e;
    
    	/* If there's no context and extension:  add callback to statecbs list */
    
    	if (!context && !exten) {
    		ast_mutex_lock(&hintlock);
    
    		cblist = statecbs;
    		while (cblist) {
    			if (cblist->callback == callback) {
    				cblist->data = data;
    				ast_mutex_unlock(&hintlock);
    
    			}
    			cblist = cblist->next;
    		}
    	
    
    		/* Now insert the callback */
    
    		cblist = malloc(sizeof(struct ast_state_cb));
    		if (!cblist) {
    			ast_mutex_unlock(&hintlock);
    			return -1;
    		}
    		memset(cblist, 0, sizeof(struct ast_state_cb));
    		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 */
    
    	e = ast_hint_extension(NULL, context, exten);    
    	if (!e) {
    		return -1;
    
    	/* Find the hint in the list of hints */
    
    	ast_mutex_lock(&hintlock);
    	list = hints;        
    
    	while (list) {
    		if (list->exten == e)
    			break;	    
    		list = list->next;    
    	}
    
    	if (!list) {
    
    		/* We have no hint, sorry */
    
    		ast_mutex_unlock(&hintlock);
    		return -1;
    	}
    
    
    	/* Now insert the callback in the callback list  */
    
    	cblist = malloc(sizeof(struct ast_state_cb));
    	if (!cblist) {
    
    		ast_mutex_unlock(&hintlock);
    		return -1;
    
    	}
    	memset(cblist, 0, sizeof(struct ast_state_cb));
    
    	cblist->id = stateid++;		/* Unique ID for this callback */
    	cblist->callback = callback;	/* Pointer to callback routine */
    	cblist->data = data;		/* Data for the callback */
    
    	cblist->next = list->callbacks;
    	list->callbacks = cblist;
    
    	return cblist->id;
    
    /*--- 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_hint *list;
    	struct ast_state_cb *cblist, *cbprev;
    
    	if (!id && !callback)
    		return -1;
    
    	ast_mutex_lock(&hintlock);
    
    	/* id is zero is a callback without extension */
    	if (!id) {
    		cbprev = NULL;
    		cblist = statecbs;
    		while (cblist) {
    	 		if (cblist->callback == callback) {
    				if (!cbprev)
    		    			statecbs = cblist->next;
    				else
    		    			cbprev->next = cblist->next;
    
    				free(cblist);
    
    	        		ast_mutex_unlock(&hintlock);
    				return 0;
    	    		}
    	    		cbprev = cblist;
    	    		cblist = cblist->next;
    		}
    
    
    		ast_mutex_lock(&hintlock);
    
    		return -1;
    
    	/* id greater than zero is a callback with extension */
    
    	/* Find the callback based on ID */
    
    	list = hints;
    	while (list) {
    		cblist = list->callbacks;
    		cbprev = NULL;
    		while (cblist) {
    	    		if (cblist->id==id) {
    				if (!cbprev)
    		    			list->callbacks = cblist->next;		
    				else
    		    			cbprev->next = cblist->next;
    
    				free(cblist);
    
    				ast_mutex_unlock(&hintlock);
    				return 0;		
    	    		}		
    
    	    		cblist = cblist->next;
    		}
    		list = list->next;
    
    	ast_mutex_unlock(&hintlock);
    	return -1;
    
    /*--- ast_add_hint: Add hint to hint list, check initial extension state */
    
    static int ast_add_hint(struct ast_exten *e)
    {
    
    	struct ast_hint *list;