Skip to content
Snippets Groups Projects
pbx.c 145 KiB
Newer Older
  • Learn to ignore specific revisions
  • 			if (len > (len_env+1) && !strncasecmp(var,"ENV(",len_env) && !strcmp(var+len-1,")")) {
    				char cp3[80] = "";
    				strncpy(cp3, var, sizeof(cp3) - 1);
    
    				cp3[len-1]='\0';
    
    				*ret=getenv(cp3+len_env);
    				if (*ret) {
    					strncpy(workspace, *ret, workspacelen - 1);
    					*ret = workspace;
    				}
    
    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;
    
    	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);
    
    		/* Look for a variable */
    
    		nextvar = strstr(whereweare, "${");
    		
    
    		/* Look for an expression */
    
    		nextexp = strstr(whereweare, "$[");
    		
    
    		/* Pick the first one only */
    
    		if (nextvar && nextexp) {
    			if (nextvar < nextexp)
    				nextexp = NULL;
    			else
    				nextvar = NULL;
    		}
    
    		
    		/* 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));
    			strncpy(var, vars, sizeof(var) - 1);
    			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;
    
    			
    			/* 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));
    			strncpy(var, vars, sizeof(var) - 1);
    			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;
    			}
    
    			/* 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,"$[")) {
    		strncpy(passdata, e->data, datalen - 1);
    		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];
    
    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)
    
    					strncpy(c->context, context, sizeof(c->context)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (c->exten != exten)
    					strncpy(c->exten, exten, sizeof(c->exten)-1);
    
    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
    				if (option_debug)
    						ast_log(LOG_DEBUG, "Launching '%s'\n", app->name);
    
    				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
    }
    
    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);
    
    	return e;
    }
    
    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;
    
    	strncpy(hint, ast_get_extension_app(e), sizeof(hint)-1);
    
    	cur = hint;    
    	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;
        		case AST_DEVICE_INUSE:
    			return AST_EXTENSION_INUSE;
        		case AST_DEVICE_BUSY:
    			allunavailable = 0;
    			allfree = 0;
    			busy = 1;
    			break;
        		case AST_DEVICE_UNAVAILABLE:
        		case AST_DEVICE_INVALID:
    			allbusy = 0;
    			allfree = 0;
    			break;
        		default:
    			allunavailable = 0;
    			allbusy = 0;
    			allfree = 0;
    		}
           		cur = rest;
    	} while (cur);
    
    	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;
    
    }
    
    
    int ast_extension_state(struct ast_channel *c, char *context, char *exten)
    {
    
    	struct ast_exten *e;
    
    	e = ast_hint_extension(c, context, exten);    
    	if (!e) 
    		return -1;
    
    	return ast_extension_state2(e);    
    
    int ast_device_state_changed(const char *fmt, ...) 
    
    	struct ast_hint *list;
    	struct ast_state_cb *cblist;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_devstate_cb *devcb;
    
    	char hint[AST_MAX_EXTENSION] = "";
    	char device[AST_MAX_EXTENSION];
    	char *cur, *rest;
    	int state;
    
    	va_start(ap, fmt);
    	vsnprintf(device, sizeof(device), fmt, ap);
    	va_end(ap);
    
    	rest = strchr(device, '-');
    	if (rest) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	state = ast_device_state(device);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	devcb = devcbs;
    	while(devcb) {
    		if (devcb->callback)
    			devcb->callback(device, state, devcb->data);
    		devcb = devcb->next;
    	}
    
    	list = hints;
    
    	while (list) {
    
    		strncpy(hint, ast_get_extension_app(list->exten), sizeof(hint) - 1);
    		cur = hint;
    		do {
    			rest = strchr(cur, '&');
    			if (rest) {
    				*rest = 0;
    				rest++;
    			}
    			
    			if (!strcmp(cur, device)) {
    
    				/* Found extension execute callbacks  */
    
    				state = ast_extension_state2(list->exten);
    				if ((state != -1) && (state != list->laststate)) {
    
    					/* For general callbacks */
    
    					cblist = statecbs;
    					while (cblist) {
    						cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
    						cblist = cblist->next;
    					}
    
    
    					/* For extension callbacks */
    
    					cblist = list->callbacks;
    					while (cblist) {
    						cblist->callback(list->exten->parent->name, list->exten->exten, state, cblist->data);
    						cblist = cblist->next;
    					}
    			
    					list->laststate = state;
    				}
    				break;
    			}
    			cur = rest;
    		} while (cur);
    		list = list->next;
    	}
    	ast_mutex_unlock(&hintlock);
    	return 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_devstate_add(ast_devstate_cb_type callback, void *data)
    {
    	struct ast_devstate_cb *devcb;
    	devcb = malloc(sizeof(struct ast_devstate_cb));
    	if (devcb) {
    		memset(devcb, 0, sizeof(struct ast_devstate_cb));
    		ast_mutex_lock(&hintlock);
    		devcb->data = data;
    		devcb->callback = callback;
    		devcb->next = devcbs;
    		devcbs = devcb;
    		ast_mutex_unlock(&hintlock);
    	}
    	return 0;
    }
    
    void ast_devstate_del(ast_devstate_cb_type callback, void *data)
    {
    	struct ast_devstate_cb *devcb, *prev = NULL, *next;
    	ast_mutex_lock(&hintlock);
    	devcb = devcbs;
    	while(devcb) {
    		next = devcb->next;
    		if ((devcb->data == data) && (devcb->callback == callback)) {
    			if (prev)
    				prev->next = next;
    			else
    				devcbs = next;
    			free(devcb);
    		} else
    			prev = devcb;
    		devcb = next;
    	}
    	ast_mutex_unlock(&hintlock);
    }
    
    
    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;
    
    	/* 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 inserts 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;
    
    
    		return 0;
        	}
    
    	if (!context || !exten)
    		return -1;
    
    	/* This callback type is for only one hint */
    	e = ast_hint_extension(NULL, context, exten);    
    	if (!e) {
    		return -1;
    
        
    	ast_mutex_lock(&hintlock);
    	list = hints;        
        
    	while (list) {
    		if (list->exten == e)
    			break;	    
    		list = list->next;    
    	}
    
    	if (!list) {
    		ast_mutex_unlock(&hintlock);
    		return -1;
    	}
    
    
    	/* Now inserts 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 = stateid++;
    
    	cblist->callback = callback;
    	cblist->data = data;
    
    
    	cblist->next = list->callbacks;
    	list->callbacks = cblist;
    
    	return cblist->id;
    
    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 */
    	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;		
    	    		}		
        	    		cbprev = cblist;				
    	    		cblist = cblist->next;
    		}
    		list = list->next;
    
    	ast_mutex_unlock(&hintlock);
    	return -1;
    
    }
    
    static int ast_add_hint(struct ast_exten *e)
    {
    
    	struct ast_hint *list;
    
    	if (!e) 
    		return -1;
    
    	ast_mutex_lock(&hintlock);
    	list = hints;        
    
    	/* Search if hint exists, do nothing */
    	while (list) {
    		if (list->exten == e) {
    			ast_mutex_unlock(&hintlock);
    			return -1;
    		}
    		list = list->next;    
        	}
    
    	list = malloc(sizeof(struct ast_hint));
    	if (!list) {
    		ast_mutex_unlock(&hintlock);
    		return -1;
    
    	/* Initialize and insert new item */
    	memset(list, 0, sizeof(struct ast_hint));
    	list->exten = e;
    	list->laststate = ast_extension_state2(e);
    	list->next = hints;
    	hints = list;
    
    }
    
    static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
    { 
    
    	struct ast_hint *list;
    
    	ast_mutex_lock(&hintlock);
    	list = hints;
    
    	while(list) {
    		if (list->exten == oe) {
    	    		list->exten = ne;
    			ast_mutex_unlock(&hintlock);	
    			return 0;
    		}
    		list = list->next;
    
    	ast_mutex_unlock(&hintlock);
    
    	return -1;
    
    }
    
    static int ast_remove_hint(struct ast_exten *e)
    
    	/* Cleanup the Notifys if hint is removed */
    	struct ast_hint *list, *prev = NULL;
    	struct ast_state_cb *cblist, *cbprev;
    
    	if (!e) 
    		return -1;
    
    	ast_mutex_lock(&hintlock);
    
    	list = hints;    
    	while(list) {
    		if (list->exten==e) {
    			cbprev = NULL;
    			cblist = list->callbacks;
    			while (cblist) {
    				/* Notify with -1 and remove all callbacks */
    				cbprev = cblist;	    
    				cblist = cblist->next;
    				cbprev->callback(list->exten->parent->name, list->exten->exten, -1, cbprev->data);
    				free(cbprev);
    	    		}
    	    		list->callbacks = NULL;
    
    	    		if (!prev)
    				hints = list->next;
    	    		else
    				prev->next = list->next;
    	    		free(list);
    
    			ast_mutex_unlock(&hintlock);
    			return 0;
    		} else {
    			prev = list;
    			list = list->next;    
    		}
        	}
    
    	ast_mutex_unlock(&hintlock);
    	return -1;
    
    int ast_get_hint(char *hint, int hintsize, struct ast_channel *c, const char *context, const char *exten)
    
    {
    	struct ast_exten *e;
    	e = ast_hint_extension(c, context, exten);
    	if (e) {	
    
    	    strncpy(hint, ast_get_extension_app(e), hintsize - 1);
    
    int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_EXISTS);
    
    int ast_findlabel_extension(struct ast_channel *c, const char *context, const char *exten, const char *label, const char *callerid) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return pbx_extension_helper(c, NULL, context, exten, 0, label, callerid, HELPER_FINDLABEL);
    }
    
    int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid) 
    {
    	return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, HELPER_FINDLABEL);
    
    int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_CANMATCH);
    
    int ast_matchmore_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_MATCHMORE);
    
    }
    
    int ast_spawn_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid) 
    {
    
    	return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_SPAWN);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_exec_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid) 
    {
    	return pbx_extension_helper(c, NULL, context, exten, priority, NULL, callerid, HELPER_EXEC);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_pbx_run(struct ast_channel *c)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int firstpass = 1;
    	char digit;
    	char exten[256];
    	int pos;
    	int waittime;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* A little initial setup here */
    	if (c->pbx)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "%s already has PBX structure??\n", c->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	c->pbx = malloc(sizeof(struct ast_pbx));
    	if (!c->pbx) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (c->amaflags) {
    
    		if (!c->cdr) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			c->cdr = ast_cdr_alloc();
    			if (!c->cdr) {
    				ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
    				free(c->pbx);
    				return -1;
    			}
    			ast_cdr_init(c->cdr, c);
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	memset(c->pbx, 0, sizeof(struct ast_pbx));
    	/* Set reasonable defaults */
    	c->pbx->rtimeout = 10;
    	c->pbx->dtimeout = 5;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Start by trying whatever the channel is set to */
    
    	if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* JK02: If not successfull fall back to 's' */
    
    		if (option_verbose > 1)
    			ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d failed so falling back to exten 's'\n", c->name, c->context, c->exten, c->priority);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		strncpy(c->exten, "s", sizeof(c->exten)-1);
    
    		if (!ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* JK02: And finally back to default if everything else failed */
    
    			if (option_verbose > 1)
    				ast_verbose( VERBOSE_PREFIX_2 "Starting %s at %s,%s,%d still failed so falling back to context 'default'\n", c->name, c->context, c->exten, c->priority);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			strncpy(c->context, "default", sizeof(c->context)-1);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		c->priority = 1;
    	}
    
    	if (c->cdr && !c->cdr->start.tv_sec && !c->cdr->start.tv_usec)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cdr_start(c->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for(;;) {
    		pos = 0;
    		digit = 0;
    
    		while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			memset(exten, 0, sizeof(exten));
    
    			if ((res = ast_spawn_extension(c, c->context, c->exten, c->priority, c->cid.cid_num))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Something bad happened, or a hangup has been requested. */
    
    				if (((res >= '0') && (res <= '9')) || ((res >= 'A') && (res <= 'F')) ||
    					(res == '*') || (res == '#')) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_log(LOG_DEBUG, "Oooh, got something to jump out with ('%c')!\n", res);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					memset(exten, 0, sizeof(exten));
    					pos = 0;
    
    					exten[pos++] = digit = res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				switch(res) {
    				case AST_PBX_KEEPALIVE:
    					if (option_debug)
    						ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
    					else if (option_verbose > 1)
    						ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited KEEPALIVE on '%s'\n", c->context, c->exten, c->priority, c->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					goto out;
    					break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				default:
    					if (option_debug)
    						ast_log(LOG_DEBUG, "Spawn extension (%s,%s,%d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
    					else if (option_verbose > 1)
    						ast_verbose( VERBOSE_PREFIX_2 "Spawn extension (%s, %s, %d) exited non-zero on '%s'\n", c->context, c->exten, c->priority, c->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
    						c->_softhangup =0;
    						break;
    					}
    
    					/* atimeout */
    					if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
    						break;
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					goto out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			if ((c->_softhangup == AST_SOFTHANGUP_TIMEOUT) && (ast_exists_extension(c,c->context,"T",1,c->cid.cid_num))) {
    
    				strncpy(c->exten,"T",sizeof(c->exten) - 1);
    				/* If the AbsoluteTimeout is not reset to 0, we'll get an infinite loop */
    				c->whentohangup = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				c->priority = 0;
    				c->_softhangup &= ~AST_SOFTHANGUP_TIMEOUT;
    
    			} else if (c->_softhangup) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_DEBUG, "Extension %s, priority %d returned normally even though call was hung up\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    					c->exten, c->priority);
    				goto out;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			firstpass = 0;
    			c->priority++;
    		}
    
    		if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* It's not a valid extension anymore */
    
    			if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (option_verbose > 2)
    					ast_verbose(VERBOSE_PREFIX_3 "Sent into invalid extension '%s' in context '%s' on %s\n", c->exten, c->context, c->name);
    
    				pbx_builtin_setvar_helper(c, "INVALID_EXTEN", c->exten);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				strncpy(c->exten, "i", sizeof(c->exten)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				c->priority = 1;
    			} else {
    				ast_log(LOG_WARNING, "Channel '%s' sent into invalid extension '%s' in context '%s', but no invalid handler\n",
    					c->name, c->exten, c->context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				goto out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    		} else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
    			/* If we get this far with AST_SOFTHANGUP_TIMEOUT, then we know that the "T" extension is next. */
    			c->_softhangup = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Done, wait for an extension */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			waittime = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (digit)
    				waittime = c->pbx->dtimeout;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else if (!autofallthrough)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				waittime = c->pbx->rtimeout;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (waittime) {
    				while (ast_matchmore_extension(c, c->context, exten, 1, c->cid.cid_num)) {
    					/* As long as we're willing to wait, and as long as it's not defined, 
    					   keep reading digits until we can't possibly get a right answer anymore.  */
    					digit = ast_waitfordigit(c, waittime * 1000);
    					if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
    						c->_softhangup = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						if (!digit)
    							/* No entry */
    							break;
    						if (digit < 0)
    							/* Error, maybe a  hangup */
    							goto out;
    						exten[pos++] = digit;
    						waittime = c->pbx->dtimeout;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    				if (ast_exists_extension(c, c->context, exten, 1, c->cid.cid_num)) {
    					/* Prepare the next cycle */
    					strncpy(c->exten, exten, sizeof(c->exten)-1);
    					c->priority = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					/* No such extension */
    					if (!ast_strlen_zero(exten)) {
    						/* An invalid extension */
    						if (ast_exists_extension(c, c->context, "i", 1, c->cid.cid_num)) {
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "Invalid extension '%s' in context '%s' on %s\n", exten, c->context, c->name);
    							pbx_builtin_setvar_helper(c, "INVALID_EXTEN", exten);
    							strncpy(c->exten, "i", sizeof(c->exten)-1);
    							c->priority = 1;
    						} else {
    							ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", exten, c->context);
    							goto out;
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* A simple timeout */
    						if (ast_exists_extension(c, c->context, "t", 1, c->cid.cid_num)) {
    							if (option_verbose > 2)
    								ast_verbose( VERBOSE_PREFIX_3 "Timeout on %s\n", c->name);
    							strncpy(c->exten, "t", sizeof(c->exten)-1);
    							c->priority = 1;
    						} else {
    							ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);