Skip to content
Snippets Groups Projects
pbx.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*! \brief  ast_add_hint: Add hint to hint list, check initial extension state */
    
    static int ast_add_hint(struct ast_exten *e)
    {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (!e)
    
    		return -1;
    
    	/* Search if hint exists, do nothing */
    
    	AST_LIST_TRAVERSE(&hints, hint, list) {
    		if (hint->exten == e) {
    			AST_LIST_UNLOCK(&hints);
    
    			if (option_debug > 1)
    				ast_log(LOG_DEBUG, "HINTS: Not re-adding existing hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
    
    	if (option_debug > 1)
    		ast_log(LOG_DEBUG, "HINTS: Adding hint %s: %s\n", ast_get_extension_name(e), ast_get_extension_app(e));
    
    
    	if (!(hint = ast_calloc(1, sizeof(*hint)))) {
    
    		return -1;
    
    	/* Initialize and insert new item at the top */
    
    	hint->exten = e;
    	hint->laststate = ast_extension_state2(e);
    	AST_LIST_INSERT_HEAD(&hints, hint, list);
    
    /*! \brief  ast_change_hint: Change hint for an extension */
    
    static int ast_change_hint(struct ast_exten *oe, struct ast_exten *ne)
    
    	AST_LIST_LOCK(&hints);
    	AST_LIST_TRAVERSE(&hints, hint, list) {
    		if (hint->exten == oe) {
    	    		hint->exten = ne;
    			res = 0;
    			break;
    
    /*! \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_state_cb *cblist, *cbprev;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (!e)
    
    		return -1;
    
    	AST_LIST_LOCK(&hints);
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&hints, hint, list) {
    		if (hint->exten == e) {
    
    			cbprev = NULL;
    
    			while (cblist) {
    				/* Notify with -1 and remove all callbacks */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				cbprev = cblist;
    
    				cblist = cblist->next;
    
    				cbprev->callback(hint->exten->parent->name, hint->exten->exten, AST_EXTENSION_DEACTIVATED, cbprev->data);
    
    				free(cbprev);
    	    		}
    
    	    		hint->callbacks = NULL;
    			AST_LIST_REMOVE_CURRENT(&hints, list);
    	    		free(hint);
    	   		res = 0;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			break;
    
    	AST_LIST_TRAVERSE_SAFE_END
    	AST_LIST_UNLOCK(&hints);
    
    /*! \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)
    
    	struct ast_exten *e = ast_hint_extension(c, context, exten);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (hint)
    
    			ast_copy_string(hint, ast_get_extension_app(e), hintsize);
    
    		if (name) {
    
    			const char *tmp = ast_get_extension_app_data(e);
    
    				ast_copy_string(name, tmp, namesize);
    
    Russell Bryant's avatar
    Russell Bryant committed
    		return -1;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	return 0;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    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, E_MATCH);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    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, E_FINDLABEL);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    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, E_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, E_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, E_MATCHMORE);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    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, E_SPAWN);
    
    /* helper function to set extension and priority */
    static void set_ext_pri(struct ast_channel *c, const char *exten, int pri)
    {
    	ast_copy_string(c->exten, exten, sizeof(c->exten));
    	c->priority = pri;
    }
    
    
    /*!
     * \brief collect digits from the channel into the buffer,
     * return -1 on error, 0 on timeout or done.
     */
    static int collect_digits(struct ast_channel *c, int waittime, char *buf, int buflen, int pos)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    
    	buf[pos] = '\0';	/* make sure it is properly terminated */
    	while (ast_matchmore_extension(c, c->context, buf, 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;
    		} else {
    			if (!digit)	/* No entry */
    				break;
    			if (digit < 0)	/* Error, maybe a  hangup */
    				return -1;
    			if (pos < buflen - 1) {	/* XXX maybe error otherwise ? */
    				buf[pos++] = digit;
    				buf[pos] = '\0';
    			}
    			waittime = c->pbx->dtimeout;
    		}
    	}
    	return 0;
    }
    
    static int __ast_pbx_run(struct ast_channel *c)
    {
    	int found = 0;	/* set if we find at least one match */
    	int res = 0;
    
    	int error = 0;		/* set an error conditions */
    
    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);
    
    		/* XXX and now what ? */
    		free(c->pbx);
    	}
    
    	if (!(c->pbx = ast_calloc(1, sizeof(*c->pbx))))
    
    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
    	/* Set reasonable defaults */
    	c->pbx->rtimeout = 10;
    	c->pbx->dtimeout = 5;
    
    
    	autoloopflag = ast_test_flag(c, AST_FLAG_IN_AUTOLOOP);	/* save value to restore at the end */
    
    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);
    
    		/* XXX the original code used the existing priority in the call to
    		 * ast_exists_extension(), and reset it to 1 afterwards.
    		 * I believe the correct thing is to set it to 1 immediately.
    		 */
    		set_ext_pri(c, "s", 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);
    
    			ast_copy_string(c->context, "default", sizeof(c->context));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (c->cdr && ast_tvzero(c->cdr->start))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cdr_start(c->cdr);
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (;;) {
    
    		char dst_exten[256];	/* buffer to accumulate digits */
    		int pos = 0;		/* XXX should check bounds */
    		int digit = 0;
    
    		/* loop on priorities in this context/exten */
    
    Russell Bryant's avatar
    Russell Bryant committed
    		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 (strchr("0123456789ABCDEF*#", 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
    					pos = 0;
    
    					dst_exten[pos++] = digit = res;
    					dst_exten[pos] = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    				}
    
    				if (res == AST_PBX_KEEPALIVE) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					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
    					break;
    
    				}
    				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);
    				if (c->_softhangup == AST_SOFTHANGUP_ASYNCGOTO) {
    					c->_softhangup =0;
    				} else if (c->_softhangup == AST_SOFTHANGUP_TIMEOUT) {
    					/* atimeout, nothing bad */
    				} else {
    					if (c->cdr)
    
    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)) {
    				set_ext_pri(c, "T", 0); /* 0 will become 1 with the c->priority++; at the end */
    
    				/* 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->_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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			c->priority++;
    
    		} /* end while  - from here on we can use 'break' to go out */
    		if (error)
    			break;
    
    		/* XXX we get here on non-existing extension or a keypress or hangup ? */
    
    
    		if (!ast_exists_extension(c, c->context, c->exten, 1, c->cid.cid_num)) {
    
    			/* If there is no match at priority 1, it is not a valid extension anymore.
    			 * Try to continue at "i", 1 or exit if the latter does not exist.
    			 */
    
    			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
    			} 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);
    
    				error = 1; /* we know what to do with it */
    				break;
    
    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;
    
    		} else {	/* keypress received, get more digits for a full extension */
    			int 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;
    
    			if (!waittime) {
    				const char *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");
    
    				error = 1; /* XXX disable message */
    				break;	/* exit from the 'for' loop */
    			}
    
    			if (collect_digits(c, waittime, dst_exten, sizeof(dst_exten), pos))
    				break;
    			if (ast_exists_extension(c, c->context, dst_exten, 1, c->cid.cid_num)) /* Prepare the next cycle */
    				set_ext_pri(c, dst_exten, 1);
    			else {
    				/* No such extension */
    				if (!ast_strlen_zero(dst_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", dst_exten, c->context, c->name);
    						pbx_builtin_setvar_helper(c, "INVALID_EXTEN", dst_exten);
    						set_ext_pri(c, "i", 1);
    					} else {
    						ast_log(LOG_WARNING, "Invalid extension '%s', but no rule 'i' in context '%s'\n", dst_exten, c->context);
    						found = 1; /* XXX disable message */
    						break;
    					}
    				} else {
    					/* 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);
    						set_ext_pri(c, "t", 1);
    					} else {
    						ast_log(LOG_WARNING, "Timeout, but no rule 't' in context '%s'\n", c->context);
    						found = 1; /* XXX disable message */
    						break;
    					}
    				}
    			}
    			if (c->cdr) {
    				if (option_verbose > 2)
    					ast_verbose(VERBOSE_PREFIX_2 "CDR updated on %s\n",c->name);
    				ast_cdr_update(c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    	if (!found && !error)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Don't know what to do with '%s'\n", c->name);
    
    	if ((res != AST_PBX_KEEPALIVE) && ast_exists_extension(c, c->context, "h", 1, c->cid.cid_num)) {
    
    		if (c->cdr && ast_opt_end_cdr_before_h_exten)
    			ast_cdr_end(c->cdr);
    
    		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);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		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);
    }
    
    
    static void destroy_exten(struct ast_exten *e)
    {
    	if (e->priority == PRIORITY_HINT)
    		ast_remove_hint(e);
    
    	if (e->datad)
    		e->datad(e->data);
    	free(e);
    }
    
    
    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 = autofallthrough;
    	autofallthrough = newval;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return oldval;
    }
    
    
    /* lookup for a context with a given name,
     * return with conlock held if found, NULL if not found
     */
    static struct ast_context *find_context_locked(const char *context)
    {
    	struct ast_context *c = NULL;
    
    	while ( (c = ast_walk_contexts(c)) ) {
    		if (!strcmp(ast_get_context_name(c), context))
    			return c;
    	}
    	ast_unlock_contexts();
    
    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
    {
    
    	int ret = -1;
    	struct ast_context *c = find_context_locked(context);
    
    	if (c) {
    		/* found, remove include from this context ... */
    		ret = ast_context_remove_include2(c, include, registrar);
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*
     * 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;
    
    	ast_mutex_lock(&con->lock);
    
    	/* find our include */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (i = con->includes; i; pi = i, i = i->next) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		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);
    
    /*!
     * \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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int ret = -1; /* default error return */
    
    	struct ast_context *c = find_context_locked(context);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		/* remove switch from this context ... */
    
    		ret = ast_context_remove_switch2(c, sw, data, registrar);
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    	return ret;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*!
     * \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
    {
    
    	/* walk switches */
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&con->alts, i, list) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (!strcmp(i->name, sw) && !strcmp(i->data, data) &&
    
    			(!registrar || !strcmp(i->registrar, registrar))) {
    
    			/* found, remove from list */
    
    			AST_LIST_REMOVE_CURRENT(&con->alts, list);
    
    			free(i); /* free switch and return */
    			ret = 0;
    			break;
    
     * \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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int ret = -1; /* default error return */
    
    	struct ast_context *c = find_context_locked(context);
    
    	if (c) { /* ... remove extension ... */
    		ret = ast_context_remove_extension2(c, extension, priority, registrar);
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    	return ret;
    
    /*!
     * \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;
    
    	struct ast_exten *peer;
    
    	/* scan the extension list to find matching extension-registrar */
    	for (exten = con->root; exten; prev_exten = exten, exten = exten->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcmp(exten->exten, extension) &&
    
    			(!registrar || !strcmp(exten->registrar, registrar)))
    			break;
    	}
    	if (!exten) {
    		/* we can't find right extension */
    		ast_mutex_unlock(&con->lock);
    		return -1;
    	}
    
    	/* 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 */
    		while ( (peer = exten) ) {
    			exten = peer->peer; /* prepare for next entry */
    			destroy_exten(peer);
    		}
    	} else {
    		/* scan the priority list to remove extension with exten->priority == priority */
    		struct ast_exten *previous_peer = NULL;
    
    		for (peer = exten; peer; previous_peer = peer, peer = peer->peer) {
    			if (peer->priority == priority &&
    					(!registrar || !strcmp(peer->registrar, registrar) ))
    				break; /* found our priority */
    		}
    		if (!peer) { /* not found */
    			ast_mutex_unlock(&con->lock);
    			return -1;
    		}
    		/* we are first priority extension? */
    		if (!previous_peer) {
    			/*
    			 * We are first in the priority chain, so must update the extension chain.
    			 * The next node is either the next priority or the next extension
    			 */
    			struct ast_exten *next_node = peer->peer ? peer->peer : peer->next;
    
    			if (!prev_exten)	/* change the root... */
    				con->root = next_node;
    			else
    				prev_exten->next = next_node; /* unlink */
    			if (peer->peer)	/* XXX update the new head of the pri list */
    				peer->peer->next = peer->next;
    		} else { /* easy, we are not first priority in extension */
    			previous_peer->peer = peer->peer;
    
    		/* now, free whole priority extension */
    		destroy_exten(peer);
    		/* XXX should we return -1 ? */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    /*! \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
    {
    
    	struct ast_app *tmp, *cur = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char tmps[80];
    
    
    	AST_LIST_LOCK(&apps);
    	AST_LIST_TRAVERSE(&apps, tmp, list) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(app, tmp->name)) {
    			ast_log(LOG_WARNING, "Already have an application '%s'\n", app);
    
    			AST_LIST_UNLOCK(&apps);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    	}
    
    	length = sizeof(*tmp) + strlen(app) + 1;
    
    		AST_LIST_UNLOCK(&apps);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    
    	strcpy(tmp->name, app);
    	tmp->execute = execute;
    	tmp->synopsis = synopsis;
    	tmp->description = description;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	/* Store in alphabetical order */
    
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, cur, list) {
    		if (strcasecmp(tmp->name, cur->name) < 0) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			AST_LIST_INSERT_BEFORE_CURRENT(&apps, tmp, list);
    
    Russell Bryant's avatar
    Russell Bryant committed
    			break;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	}
    
    	AST_LIST_TRAVERSE_SAFE_END
    
    		AST_LIST_INSERT_TAIL(&apps, tmp, list);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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;
    }
    
    
    /*
     * Append to the list. We don't have a tail pointer because we need
     * to scan the list anyways to check for duplicates during insertion.
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_register_switch(struct ast_switch *sw)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_switch *tmp;
    
    	AST_LIST_LOCK(&switches);
    	AST_LIST_TRAVERSE(&switches, tmp, list) {
    		if (!strcasecmp(tmp->name, sw->name)) {
    			AST_LIST_UNLOCK(&switches);
    			ast_log(LOG_WARNING, "Switch '%s' already found\n", sw->name);
    			return -1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	AST_LIST_INSERT_TAIL(&switches, sw, list);
    	AST_LIST_UNLOCK(&switches);
    
    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
    {
    
    	AST_LIST_LOCK(&switches);
    	AST_LIST_REMOVE(&switches, sw, list);
    	AST_LIST_UNLOCK(&switches);
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Help for CLI commands ...
     */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static char show_application_help[] =
    
    Mark Spencer's avatar
    Mark Spencer committed
    "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";
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static char show_switches_help[] =
    
    Mark Spencer's avatar
    Mark Spencer committed
    "Usage: show switches\n"
    "       Show registered switches\n";
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static char show_hints_help[] =
    
    "Usage: show hints\n"
    "       Show registered hints\n";
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static char show_globals_help[] =
    
    "Usage: show globals\n"
    "       Show current global dialplan variables and their values\n";
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static char set_global_help[] =
    
    "Usage: set global <name> <value>\n"
    "       Set global dialplan variable <name> to <value>\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(const char *line, const 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;
    
    	/* return the n-th [partial] matching entry */
    
    	AST_LIST_LOCK(&apps);
    	AST_LIST_TRAVERSE(&apps, a, list) {
    
    		if (!strncasecmp(word, a->name, wordlen) && ++which > state) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	AST_LIST_UNLOCK(&apps);
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	return ret;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int handle_show_application(int fd, int argc, char *argv[])
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_app *a;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int app, no_registered_app = 1;
    
    	if (argc < 3)
    		return RESULT_SHOWUSAGE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* ... go through all applications ... */
    
    	AST_LIST_LOCK(&apps);
    	AST_LIST_TRAVERSE(&apps, a, list) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* ... compare this application name with all arguments given
    		 * to 'show application' command ... */
    		for (app = 2; app < argc; app++) {
    			if (!strcasecmp(a->name, argv[app])) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* 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;
    				int synopsis_size, description_size;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    				no_registered_app = 0;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (a->synopsis)
    					synopsis_size = strlen(a->synopsis) + 23;
    				else
    					synopsis_size = strlen("Not available") + 23;
    				synopsis = alloca(synopsis_size);
    
    				if (a->description)
    					description_size = strlen(a->description) + 23;
    				else
    					description_size = strlen("Not available") + 23;
    				description = alloca(description_size);
    
    				if (synopsis && description) {
    					snprintf(info, 64 + AST_MAX_APP, "\n  -= Info about application '%s' =- \n\n", a->name);
    					term_color(infotitle, info, COLOR_MAGENTA, 0, 64 + AST_MAX_APP + 22);
    
    					term_color(syntitle, "[Synopsis]\n", COLOR_MAGENTA, 0, 40);
    					term_color(destitle, "[Description]\n", COLOR_MAGENTA, 0, 40);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					term_color(synopsis,
    									a->synopsis ? a->synopsis : "Not available",
    									COLOR_CYAN, 0, synopsis_size);
    					term_color(description,
    									a->description ? a->description : "Not available",
    									COLOR_CYAN, 0, description_size);
    
    					ast_cli(fd,"%s%s%s\n\n%s%s\n", infotitle, syntitle, synopsis, destitle, description);
    				} else {
    					/* ... one of our applications, show info ...*/
    					ast_cli(fd,"\n  -= Info about application '%s' =- \n\n"
    
    						"[Synopsis]\n  %s\n\n"
    						"[Description]\n%s\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    						a->name,
    						a->synopsis ? a->synopsis : "Not available",
    						a->description ? a->description : "Not available");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    	AST_LIST_UNLOCK(&apps);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* we found at least one app? no? */
    	if (no_registered_app) {
    		ast_cli(fd, "Your application(s) is (are) not registered\n");
    		return RESULT_FAILURE;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return RESULT_SUCCESS;
    }
    
    
    /*! \brief  handle_show_hints: CLI support for listing registred dial plan hints */
    
    static int handle_show_hints(int fd, int argc, char *argv[])
    {
    	struct ast_hint *hint;
    	int num = 0;
    
    		ast_cli(fd, "There are no registered dialplan hints\n");
    		return RESULT_SUCCESS;
    	}
    	/* ... we have hints ... */
    	ast_cli(fd, "\n    -= Registered Asterisk Dial Plan Hints =-\n");
    
    		watchers = 0;
    		for (watcher = hint->callbacks; watcher; watcher = watcher->next)
    			watchers++;
    
    		ast_cli(fd, "   %20s@%-20.20s: %-20.20s  State:%-15.15s Watchers %2d\n",
    
    			ast_get_extension_name(hint->exten),
    			ast_get_context_name(ast_get_extension_context(hint->exten)),
    			ast_get_extension_app(hint->exten),
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			ast_extension_state2str(hint->laststate), watchers);
    
    		num++;
    	}
    	ast_cli(fd, "----------------\n");
    
    	ast_cli(fd, "- %d hints registered\n", num);
    
    	return RESULT_SUCCESS;
    }
    
    
    /*! \brief  handle_show_switches: CLI support for listing registred dial plan switches */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int handle_show_switches(int fd, int argc, char *argv[])
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_switch *sw;
    
    	if (AST_LIST_EMPTY(&switches)) {
    		AST_LIST_UNLOCK(&switches);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cli(fd, "There are no registered alternative switches\n");
    		return RESULT_SUCCESS;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_cli(fd, "\n    -= Registered Asterisk Alternative Switches =-\n");
    
    	AST_LIST_TRAVERSE(&switches, sw, list)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cli(fd, "%s: %s\n", sw->name, sw->description);