Skip to content
Snippets Groups Projects
pbx.c 166 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		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;
    
    /*! \brief  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;
    
    	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);
    
    			if (option_debug > 1)
    				ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
    
    			return -1;
    		}
    		list = list->next;    
    
    	if (option_debug > 1)
    		ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
    
    
    	list = malloc(sizeof(struct ast_hint));
    	if (!list) {
    		ast_mutex_unlock(&hintlock);
    
    		if (option_debug > 1)
    			ast_log(LOG_DEBUG, "HINTS: Out of memory...\n");
    
    		return -1;
    
    	/* Initialize and insert new item at the top */
    
    	memset(list, 0, sizeof(struct ast_hint));
    	list->exten = e;
    	list->laststate = ast_extension_state2(e);
    	list->next = hints;
    	hints = list;
    
    /*! \brief  ast_change_hint: Change hint for an extension */
    
    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;
    
    /*! \brief  ast_remove_hint: Remove hint from extension */
    
    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, AST_EXTENSION_DEACTIVATED, 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;
    
    /*! \brief  ast_get_hint: Get hint for channel */
    
    int ast_get_hint(char *hint, int hintsize, char *name, int namesize, struct ast_channel *c, const char *context, const char *exten)
    
    	e = ast_hint_extension(c, context, exten);
    
    	if (e) {
    		if (hint) 
    
    		    ast_copy_string(hint, ast_get_extension_app(e), hintsize);
    
    		if (name) {
    			tmp = ast_get_extension_app_data(e);
    			if (tmp)
    
    				ast_copy_string(name, (char *) tmp, namesize);
    
    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
    static int __ast_pbx_run(struct ast_channel *c)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int firstpass = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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;
    
    
    	autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP);
    	ast_set_flag(c, AST_FLAG_IN_AUTOLOOP);
    
    
    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)) {
    
    		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);
    
    		ast_copy_string(c->exten, "s", sizeof(c->exten));
    
    		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);
    
    			ast_copy_string(c->context, "default", sizeof(c->context));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    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))) {
    
    				ast_copy_string(c->exten, "T", sizeof(c->exten));
    
    				/* 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);
    
    				ast_copy_string(c->exten, "i", sizeof(c->exten));
    
    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 */
    
    					ast_copy_string(c->exten, exten, sizeof(c->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    					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);
    
    							ast_copy_string(c->exten, "i", sizeof(c->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    							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);
    
    							ast_copy_string(c->exten, "t", sizeof(c->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    							c->priority = 1;
    						} else {
    							ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
    							goto out;
    						}
    					}	
    				}
    				if (c->cdr) {
    					if (option_verbose > 2)
    						ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);	
    					ast_cdr_update(c);
    			    }
    			} else {
    
    				status = pbx_builtin_getvar_helper(c, "DIALSTATUS");
    				if (!status)
    					status = "UNKNOWN";
    				if (option_verbose > 2)
    					ast_verbose(VERBOSE_PREFIX_2 "Auto fallthrough, channel '%s' status is '%s'\n", c->name, status);
    				if (!strcasecmp(status, "CONGESTION"))
    					res = pbx_builtin_congestion(c, "10");
    				else if (!strcasecmp(status, "CHANUNAVAIL"))
    					res = pbx_builtin_congestion(c, "10");
    				else if (!strcasecmp(status, "BUSY"))
    					res = pbx_builtin_busy(c, "10");
    				goto out;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    	}
    	if (firstpass) 
    		ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
    out:
    
    	if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
    
    		c->exten[0] = 'h';
    		c->exten[1] = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    		c->priority = 1;
    
    		while(ast_exists_extension(c, c->context, c->exten, c->priority, c->cid.cid_num)) {
    			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 (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);
    				break;
    			}
    			c->priority++;
    		}
    	}
    
    	ast_set2_flag(c, autoloopflag, AST_FLAG_IN_AUTOLOOP);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pbx_destroy(c->pbx);
    	c->pbx = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res != AST_PBX_KEEPALIVE)
    		ast_hangup(c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /* Returns 0 on success, non-zero if call limit was reached */
    static int increase_call_count(const struct ast_channel *c)
    {
    	int failed = 0;
    
    	ast_mutex_lock(&maxcalllock);
    	if (option_maxcalls) {
    		if (countcalls >= option_maxcalls) {
    			ast_log(LOG_NOTICE, "Maximum call limit of %d calls exceeded by '%s'!\n", option_maxcalls, c->name);
    			failed = -1;
    		}
    	}
    
    	if (option_maxload) {
    		getloadavg(&curloadavg, 1);
    		if (curloadavg >= option_maxload) {
    
    Russell Bryant's avatar
    Russell Bryant committed
    			ast_log(LOG_NOTICE, "Maximum loadavg limit of %f load exceeded by '%s' (currently %f)!\n", option_maxload, c->name, curloadavg);
    
    	if (!failed)
    		countcalls++;	
    	ast_mutex_unlock(&maxcalllock);
    
    	return failed;
    }
    
    static void decrease_call_count(void)
    {
    	ast_mutex_lock(&maxcalllock);
    	if (countcalls > 0)
    		countcalls--;
    	ast_mutex_unlock(&maxcalllock);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void *pbx_thread(void *data)
    {
    	/* Oh joyeous kernel, we're a new thread, with nothing to do but
    
    	   answer this channel and get it going.
    	*/
    
    	/* NOTE:
    	   The launcher of this function _MUST_ increment 'countcalls'
    	   before invoking the function; it will be decremented when the
    	   PBX has finished running on the channel
    	 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *c = data;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pthread_exit(NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return NULL;
    
    enum ast_pbx_result ast_pbx_start(struct ast_channel *c)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	pthread_t t;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pthread_attr_t attr;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!c) {
    		ast_log(LOG_WARNING, "Asked to start thread on NULL channel?\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (increase_call_count(c))
    		return AST_PBX_CALL_LIMIT;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Start a new thread, and get something handling this channel. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pthread_attr_init(&attr);
    	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    
    	if (ast_pthread_create(&t, &attr, pbx_thread, c)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Failed to create new channel thread\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    enum ast_pbx_result ast_pbx_run(struct ast_channel *c)
    
    	enum ast_pbx_result res = AST_PBX_SUCCESS;
    
    	if (increase_call_count(c))
    		return AST_PBX_CALL_LIMIT;
    
    	res = __ast_pbx_run(c);
    	decrease_call_count();
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    int ast_active_calls(void)
    {
    	return countcalls;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int pbx_set_autofallthrough(int newval)
    {
    	int oldval;
    	oldval = autofallthrough;
    	if (oldval != newval)
    		autofallthrough = newval;
    	return oldval;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * This function locks contexts list by &conlist, search for the right context
    
    Mark Spencer's avatar
    Mark Spencer committed
     * structure, leave context list locked and call ast_context_remove_include2
     * which removes include, unlock contexts list and return ...
     */
    
    int ast_context_remove_include(const char *context, const char *include, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *c;
    
    	if (ast_lock_contexts()) return -1;
    
    	/* walk contexts and search for the right one ...*/
    	c = ast_walk_contexts(NULL);
    	while (c) {
    		/* we found one ... */
    		if (!strcmp(ast_get_context_name(c), context)) {
    			int ret;
    			/* remove include from this context ... */	
    			ret = ast_context_remove_include2(c, include, registrar);
    
    			ast_unlock_contexts();
    
    			/* ... return results */
    			return ret;
    		}
    		c = ast_walk_contexts(c);
    	}
    
    	/* we can't find the right one context */
    	ast_unlock_contexts();
    	return -1;
    }
    
    /*
     * When we call this function, &conlock lock must be locked, because when
     * we giving *con argument, some process can remove/change this context
     * and after that there can be segfault.
     *
     * This function locks given context, removes include, unlock context and
     * return.
     */
    
    int ast_context_remove_include2(struct ast_context *con, const char *include, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_include *i, *pi = NULL;
    
    
    	if (ast_mutex_lock(&con->lock)) return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* walk includes */
    	i = con->includes;
    	while (i) {
    		/* find our include */
    		if (!strcmp(i->name, include) && 
    
    			(!registrar || !strcmp(i->registrar, registrar))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* remove from list */
    			if (pi)
    				pi->next = i->next;
    			else
    				con->includes = i->next;
    			/* free include and return */
    			free(i);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		pi = i;
    		i = i->next;
    	}
    
    	/* we can't find the right include */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    /*!
     * \note This function locks contexts list by &conlist, search for the rigt context
    
    Mark Spencer's avatar
    Mark Spencer committed
     * structure, leave context list locked and call ast_context_remove_switch2
     * which removes switch, unlock contexts list and return ...
     */
    
    int ast_context_remove_switch(const char *context, const char *sw, const char *data, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_context *c;
    
    	if (ast_lock_contexts()) return -1;
    
    	/* walk contexts and search for the right one ...*/
    	c = ast_walk_contexts(NULL);
    	while (c) {
    		/* we found one ... */
    		if (!strcmp(ast_get_context_name(c), context)) {
    			int ret;
    			/* remove switch from this context ... */	
    			ret = ast_context_remove_switch2(c, sw, data, registrar);
    
    			ast_unlock_contexts();
    
    			/* ... return results */
    			return ret;
    		}
    		c = ast_walk_contexts(c);
    	}
    
    	/* we can't find the right one context */
    	ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    /*!
     * \brief This function locks given context, removes switch, unlock context and
     * return.
     * \note When we call this function, &conlock lock must be locked, because when
    
    Mark Spencer's avatar
    Mark Spencer committed
     * we giving *con argument, some process can remove/change this context
     * and after that there can be segfault.
     *
     */
    
    int ast_context_remove_switch2(struct ast_context *con, const char *sw, const char *data, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_sw *i, *pi = NULL;
    
    
    	if (ast_mutex_lock(&con->lock)) return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* walk switchs */
    	i = con->alts;
    	while (i) {
    		/* find our switch */
    		if (!strcmp(i->name, sw) && !strcmp(i->data, data) && 
    
    			(!registrar || !strcmp(i->registrar, registrar))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* remove from list */
    			if (pi)
    				pi->next = i->next;
    			else
    				con->alts = i->next;
    			/* free switch and return */
    			free(i);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		pi = i;
    		i = i->next;
    	}
    
    	/* we can't find the right switch */
    
     * \note This functions lock contexts list, search for the right context,
    
    Mark Spencer's avatar
    Mark Spencer committed
     * call ast_context_remove_extension2, unlock contexts list and return.
     * In this function we are using
     */
    
    int ast_context_remove_extension(const char *context, const char *extension, int priority, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *c;
    
    	if (ast_lock_contexts()) return -1;
    
    	/* walk contexts ... */
    	c = ast_walk_contexts(NULL);
    	while (c) {
    		/* ... search for the right one ... */
    		if (!strcmp(ast_get_context_name(c), context)) {
    			/* ... remove extension ... */
    			int ret = ast_context_remove_extension2(c, extension, priority,
    				registrar);
    			/* ... unlock contexts list and return */
    			ast_unlock_contexts();
    			return ret;
    		}
    		c = ast_walk_contexts(c);
    	}
    
    	/* we can't find the right context */
    	ast_unlock_contexts();
    	return -1;
    }
    
    
    /*!
     * \brief This functionc locks given context, search for the right extension and
    
    Mark Spencer's avatar
    Mark Spencer committed
     * fires out all peer in this extensions with given priority. If priority
     * is set to 0, all peers are removed. After that, unlock context and
     * return.
    
     * \note When do you want to call this function, make sure that &conlock is locked,
     * because some process can handle with your *con context before you lock
     * it.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    int ast_context_remove_extension2(struct ast_context *con, const char *extension, int priority, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_exten *exten, *prev_exten = NULL;
    
    
    	if (ast_mutex_lock(&con->lock)) return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* go through all extensions in context and search the right one ... */
    	exten = con->root;
    	while (exten) {
    
    		/* look for right extension */
    		if (!strcmp(exten->exten, extension) &&
    
    			(!registrar || !strcmp(exten->registrar, registrar))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			struct ast_exten *peer;
    
    			/* should we free all peers in this extension? (priority == 0)? */
    			if (priority == 0) {
    				/* remove this extension from context list */
    				if (prev_exten)
    					prev_exten->next = exten->next;
    				else
    					con->root = exten->next;
    
    				/* fire out all peers */
    				peer = exten; 
    				while (peer) {
    					exten = peer->peer;
    
    					
    					if (!peer->priority==PRIORITY_HINT) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    					peer->datad(peer->data);
    					free(peer);
    
    					peer = exten;
    				}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return 0;
    			} else {
    				/* remove only extension with exten->priority == priority */
    				struct ast_exten *previous_peer = NULL;
    
    				peer = exten;
    				while (peer) {
    					/* is this our extension? */
    					if (peer->priority == priority &&
    
    						(!registrar || !strcmp(peer->registrar, registrar) )) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* we are first priority extension? */
    						if (!previous_peer) {
    							/* exists previous extension here? */
    							if (prev_exten) {
    								/* yes, so we must change next pointer in
    								 * previous connection to next peer
    								 */
    								if (peer->peer) {
    									prev_exten->next = peer->peer;
    									peer->peer->next = exten->next;
    								} else
    									prev_exten->next = exten->next;
    							} else {
    								/* no previous extension, we are first
    								 * extension, so change con->root ...
    								 */
    								if (peer->peer)
    									con->root = peer->peer;
    								else
    									con->root = exten->next; 
    							}
    						} else {
    							/* we are not first priority in extension */
    							previous_peer->peer = peer->peer;
    						}
    
    						/* now, free whole priority extension */
    
    						if (peer->priority==PRIORITY_HINT)
    
    Mark Spencer's avatar
    Mark Spencer committed
    						peer->datad(peer->data);
    						free(peer);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    						return 0;
    					} else {
    						/* this is not right extension, skip to next peer */
    						previous_peer = peer;
    						peer = peer->peer;
    					}
    				}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return -1;
    			}
    		}
    
    		prev_exten = exten;
    		exten = exten->next;
    	}
    
    	/* we can't find right extension */
    
    /*! \brief Dynamically register a new dial plan application */
    
    int ast_register_application(const char *app, int (*execute)(struct ast_channel *, void *), const char *synopsis, const char *description)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char tmps[80];
    
    	int length;
    	length = sizeof(struct ast_app);
    	length += strlen(app) + 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock application list\n");
    		return -1;
    	}
    	tmp = apps;
    	while(tmp) {
    		if (!strcasecmp(app, tmp->name)) {
    			ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    		tmp = tmp->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (tmp) {
    
    		memset(tmp, 0, length);
    		strcpy(tmp->name, app);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->execute = execute;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->synopsis = synopsis;
    		tmp->description = description;
    
    		/* Store in alphabetical order */
    		cur = apps;
    		prev = NULL;
    		while(cur) {
    			if (strcasecmp(tmp->name, cur->name) < 0)
    				break;
    			prev = cur;
    			cur = cur->next;
    		}
    		if (prev) {
    			tmp->next = prev->next;
    			prev->next = tmp;
    		} else {
    			tmp->next = apps;
    			apps = tmp;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	if (option_verbose > 1)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_verbose( VERBOSE_PREFIX_2 "Registered application '%s'\n", term_color(tmps, tmp->name, COLOR_BRCYAN, 0, sizeof(tmps)));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_register_switch(struct ast_switch *sw)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_switch *tmp, *prev=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock switch lock\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tmp = switches;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	while(tmp) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(tmp->name, sw->name))
    			break;
    		prev = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp = tmp->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (tmp) {	
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
    		return -1;
    	}
    	sw->next = NULL;
    	if (prev) 
    		prev->next = sw;
    	else
    		switches = sw;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_unregister_switch(struct ast_switch *sw)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_switch *tmp, *prev=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock switch lock\n");
    		return;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tmp = switches;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	while(tmp) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (tmp == sw) {
    			if (prev)
    				prev->next = tmp->next;
    			else
    				switches = tmp->next;
    			tmp->next = NULL;
    			break;			
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		prev = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp = tmp->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Help for CLI commands ...
     */
    static char show_application_help[] = 
    "Usage: show application <application> [<application> [<application> [...]]]\n"
    "       Describes a particular application.\n";
    
    
    static char show_functions_help[] =
    
    "Usage: show functions [like <text>]\n"
    "       List builtin functions, optionally only those matching a given string\n";
    
    
    static char show_function_help[] =
    "Usage: show function <function>\n"
    "       Describe a particular dialplan function.\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char show_applications_help[] =
    
    "Usage: show applications [{like|describing} <text>]\n"
    "       List applications which are currently available.\n"
    "       If 'like', <text> will be a substring of the app name\n"
    "       If 'describing', <text> will be a substring of the description\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char show_dialplan_help[] =
    "Usage: show dialplan [exten@][context]\n"
    "       Show dialplan\n";
    
    static char show_switches_help[] = 
    "Usage: show switches\n"
    "       Show registered switches\n";
    
    
    static char show_hints_help[] = 
    "Usage: show hints\n"
    "       Show registered hints\n";
    
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * IMPLEMENTATION OF CLI FUNCTIONS IS IN THE SAME ORDER AS COMMANDS HELPS
     *
     */
    
    /*
    
     * \brief 'show application' CLI command implementation functions ...
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    /*
     * There is a possibility to show informations about more than one
     * application at one time. You can type 'show application Dial Echo' and
     * you will see informations about these two applications ...
     */
    static char *complete_show_application(char *line, char *word,
    	int pos, int state)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_app *a;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int which = 0;