Skip to content
Snippets Groups Projects
pbx.c 162 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*! \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);
    
    	if (e) {
    		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;
    
    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);
    
    /* 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;
    }
    
    
    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);
    
    	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);
    	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);
    
    		/* 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 (;;) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pos = 0;
    		digit = 0;
    
    Russell Bryant's avatar
    Russell Bryant committed
    		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)) {
    				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);
    				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)) {
    
    			/* 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);
    
    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 */
    
    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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						} 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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						} 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)) {
    
    		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);
    
    	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 = 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;
    	if (ast_lock_contexts()) {
    		errno = EBUSY;
    		return NULL;
    	}
    	while ( (c = ast_walk_contexts(c)) ) {
    		if (!strcmp(ast_get_context_name(c), context))
    			return c;
    	}
    	ast_unlock_contexts();
    	errno = ENOENT;
    	return NULL;
    }
    
    
    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;
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (ast_mutex_lock(&con->lock))
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* walk includes */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (i = con->includes; i; pi = i, i = i->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* 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;
    		}
    	}
    
    	/* 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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int ret = -1; /* default error return */
    
    	struct ast_context *c = find_context_locked(context);
    
    	if (c) {
    		/* 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
    {
    	struct ast_sw *i, *pi = NULL;
    
    	if (ast_mutex_lock(&con->lock))
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* walk switchs */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (i = con->alts; i; pi = i, i = i->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcmp(i->name, sw) && !strcmp(i->data, data) && 
    
    			(!registrar || !strcmp(i->registrar, registrar))) {
    
    			/* found, remove from list */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (pi)
    				pi->next = i->next;
    			else
    				con->alts = i->next;
    
    			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;
    
    
    	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;
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (tmp = apps; tmp; tmp = tmp->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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;
    		}
    	}
    
    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;
    	/* Store in alphabetical order */
    	prev = NULL;
    	for (cur = apps; cur; cur = cur->next) {
    		if (strcasecmp(tmp->name, cur->name) < 0)
    			break;
    		prev = cur;
    	}
    	if (prev) {
    		tmp->next = prev->next;
    		prev->next = tmp;
    	} else {
    		tmp->next = apps;
    		apps = tmp;
    	}
    	
    
    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
    {
    
    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;
    	}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (tmp = switches; tmp; prev = tmp, tmp = tmp->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(tmp->name, sw->name))
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    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
    	}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (tmp = switches; tmp; prev = tmp, tmp = tmp->next) {
    
    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
    /*
     * 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";
    
    
    static char show_globals_help[] = 
    "Usage: show globals\n"
    "       Show current global dialplan variables and their values\n";
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* try to lock applications list ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock application list\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    
    	/* return the n-th [partial] matching entry */
    	for (a = apps; a && !ret; a = a->next) {
    		if (!strncasecmp(word, a->name, wordlen) && ++which > state)
    			ret = strdup(a->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (argc < 3) return RESULT_SHOWUSAGE;
    
    	/* try to lock applications list ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock application list\n");
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* ... go through all applications ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (a = apps; a; a = a->next) {
    
    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
    			}
    
    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");
    
    		ast_log(LOG_ERROR, "Unable to lock hints\n");
    		return -1;
    	}
    
    		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 (!switches) {
    		ast_cli(fd, "There are no registered alternative switches\n");
    		return RESULT_SUCCESS;
    	}
    	/* ... we have applications ... */
    	ast_cli(fd, "\n    -= Registered Asterisk Alternative Switches =-\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock switches\n");
    		return -1;
    	}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	for (sw = switches; sw; sw = sw->next)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cli(fd, "%s: %s\n", sw->name, sw->description);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return RESULT_SUCCESS;
    }
    
    /*
     * 'show applications' CLI command implementation functions ...
     */
    static int handle_show_applications(int fd, int argc, char *argv[])
    {
    	struct ast_app *a;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int like = 0, describing = 0;
    
    	int total_match = 0; 	/* Number of matches in like clause */
    	int total_apps = 0; 	/* Number of apps registered */
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* try to lock applications list ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock application list\n");
    		return -1;
    	}
    
    	/* ... have we got at least one application (first)? no? */
    
    	if (!apps) {
    		ast_cli(fd, "There are no registered applications\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    
    	/* show applications like <keyword> */
    	if ((argc == 4) && (!strcmp(argv[2], "like"))) {
    		like = 1;
    	} else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
    		describing = 1;
    	}
    
    	/* show applications describing <keyword1> [<keyword2>] [...] */
    	if ((!like) && (!describing)) {
    		ast_cli(fd, "    -= Registered Asterisk Applications =-\n");
    	} else {
    		ast_cli(fd, "    -= Matching Asterisk Applications =-\n");
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* ... go through all applications ... */
    
    	for (a = apps; a; a = a->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* ... show informations about applications ... */