Skip to content
Snippets Groups Projects
pbx.c 44.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		context = NULL;
    		exten = NULL;
    	}
    	ast_pthread_mutex_lock(&conlock);
    	con = contexts;
    	while(con) {
    		if (!context || (!strcasecmp(context, con->name))) {
    			ast_cli(fd, "\n [ Context '%s' created by '%s']\n", con->name, con->registrar);
    			eroot = con->root;
    			while(eroot) {
    				if (!exten || (!strcasecmp(exten, eroot->exten))) {
    					memset(tmp, ' ', sizeof(tmp));
    					snprintf(tmp, sizeof(tmp), "  '%s' => ", eroot->exten);
    					spaces = strlen(tmp);
    					tmp[spaces] = ' ';
    					if (spaces < 19)
    						spaces = 19;
    					snprintf(tmp2, sizeof(tmp2), "%d. %s(%s)", eroot->priority, eroot->app, (char *)eroot->data);
    					snprintf(tmp + spaces, sizeof(tmp) - spaces,     "%-45s [%s]\n", 
    							tmp2, eroot->registrar);
    					ast_cli(fd, tmp);
    					memset(tmp, ' ', spaces);
    					e = eroot->peer;
    					while(e) {
    						snprintf(tmp2, sizeof(tmp2), "%d. %s(%s)", e->priority, e->app, (char *)e->data);
    						snprintf(tmp + spaces, sizeof(tmp) - spaces,     "%-45s [%s]\n", 
    							tmp2, e->registrar);
    						ast_cli(fd, tmp);
    						e = e->peer;
    					}
    				}
    				eroot = eroot->next;
    			}
    			inc = con->includes;
    			while(inc) {
    				snprintf(tmp, sizeof(tmp), "   Include =>    '%s'", inc->name);
    				ast_cli(fd, "%s [%s]\n", tmp, inc->registrar);
    				inc = inc->next;
    			}
    		}
    		con = con->next;
    	}
    	ast_pthread_mutex_unlock(&conlock);
    	return RESULT_SUCCESS;
    }
    
    static struct ast_cli_entry showapps = { { "show", "applications", NULL }, 
    	handle_show_applications, "Shows registered applications", apps_help };
    
    static struct ast_cli_entry showdialplan = { { "show", "dialplan", NULL }, 
    	handle_dialplan, "Displays all or part of dialplan", dialplan_help, complete_context };
    
    static struct ast_cli_entry showapp = { { "show", "application", NULL }, 
    	handle_show_application, "Describe a specific application", app_help, complete_app };
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_unregister_application(char *app) {
    	struct ast_app *tmp, *tmpl = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ast_pthread_mutex_lock(&applock)) {
    
    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)) {
    			if (tmpl)
    				tmpl->next = tmp->next;
    			else
    				apps = tmp->next;
    			if (option_verbose > 1)
    				ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_pthread_mutex_unlock(&applock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		tmpl = tmp;
    		tmp = tmp->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&applock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_context *ast_context_create(char *name, char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *tmp;
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&conlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tmp = contexts;
    	while(tmp) {
    		if (!strcasecmp(tmp->name, name)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_pthread_mutex_unlock(&conlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
    			return NULL;
    		}
    		tmp = tmp->next;
    	}
    	tmp = malloc(sizeof(struct ast_context));
    	if (tmp) {
    		pthread_mutex_init(&tmp->lock, NULL);
    		strncpy(tmp->name, name, sizeof(tmp->name));
    		tmp->root = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->registrar = registrar;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->next = contexts;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->includes = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		contexts = tmp;
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
    		else if (option_verbose > 2)
    			ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
    	} else
    		ast_log(LOG_WARNING, "Out of memory\n");
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&conlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return tmp;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_context_add_include2(struct ast_context *con, char *value, char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_include *inc, *incc, *incl = NULL;
    	inc = malloc(sizeof(struct ast_include));
    	if (!inc) {
    		ast_log(LOG_WARNING, "Out of memory\n");
    		return -1;
    	}
    	strncpy(inc->name, value, sizeof(inc->name));
    	inc->next = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	inc->registrar = registrar;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pthread_mutex_lock(&con->lock);
    	incc = con->includes;
    	while(incc) {
    		incl = incc;
    		if (!strcasecmp(incc->name, value)) {
    			/* Already there */
    			pthread_mutex_unlock(&con->lock);
    			return 0;
    		}
    		incc = incc->next;
    	}
    	if (incl) 
    		incl->next = inc;
    	else
    		con->includes = inc;
    	pthread_mutex_unlock(&con->lock);
    	return 0;
    	
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_add_extension2(struct ast_context *con,
    					  int replace, char *extension, int priority,
    
    Mark Spencer's avatar
    Mark Spencer committed
    					  char *application, void *data, void (*datad)(void *),
    					  char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    #define LOG { 	if (option_debug) \
    		ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
    	else if (option_verbose > 2) \
    		ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n", tmp->exten, tmp->priority, con->name); \
    		}
    
    	/*
    	 * This is a fairly complex routine.  Different extensions are kept
    	 * in order by the extension number.  Then, extensions of different
    	 * priorities (same extension) are kept in a list, according to the
    	 * peer pointer.
    	 */
    	struct ast_exten *tmp, *e, *el = NULL, *ep = NULL;
    	int res;
    	/* Be optimistic:  Build the extension structure first */
    	tmp = malloc(sizeof(struct ast_exten));
    	if (tmp) {
    		strncpy(tmp->exten, extension, sizeof(tmp->exten));
    		tmp->priority = priority;
    		strncpy(tmp->app, application, sizeof(tmp->app));
    		tmp->data = data;
    		tmp->datad = datad;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->registrar = registrar;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->peer = NULL;
    		tmp->next =  NULL;
    	} else {
    		ast_log(LOG_WARNING, "Out of memory\n");
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ast_pthread_mutex_lock(&con->lock)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		free(tmp);
    		/* And properly destroy the data */
    		datad(data);
    		ast_log(LOG_WARNING, "Failed to lock context '%s'\n", con->name);
    		return -1;
    	}
    	e = con->root;
    	while(e) {
    		res= strcasecmp(e->exten, extension);
    		if (res == 0) {
    			/* We have an exact match, now we find where we are
    			   and be sure there's no duplicates */
    			while(e) {
    				if (e->priority == tmp->priority) {
    					/* Can't have something exactly the same.  Is this a
    					   replacement?  If so, replace, otherwise, bonk. */
    					if (replace) {
    						if (ep) {
    							/* We're in the peer list, insert ourselves */
    							ep->peer = tmp;
    							tmp->peer = e->peer;
    						} else if (el) {
    							/* We're the first extension. Take over e's functions */
    							el->next = tmp;
    							tmp->next = e->next;
    							tmp->peer = e->peer;
    						} else {
    							/* We're the very first extension.  */
    							con->root = tmp;
    							tmp->next = e->next;
    							tmp->peer = e->peer;
    						}
    						/* Destroy the old one */
    						e->datad(e->data);
    						free(e);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_pthread_mutex_unlock(&con->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* And immediately return success. */
    						LOG;
    						return 0;
    					} else {
    						ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
    						tmp->datad(tmp->data);
    						free(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_pthread_mutex_unlock(&con->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						return -1;
    					}
    				} else if (e->priority > tmp->priority) {
    					/* Slip ourselves in just before e */
    					if (ep) {
    						/* Easy enough, we're just in the peer list */
    						ep->peer = tmp;
    						tmp->peer = e;
    					} else if (el) {
    						/* We're the first extension in this peer list */
    						el->next = tmp;
    						tmp->next = e->next;
    						e->next = NULL;
    						tmp->peer = e;
    					} else {
    						/* We're the very first extension altogether */
    						tmp->next = con->root;
    						/* Con->root must always exist or we couldn't get here */
    						tmp->peer = con->root->peer;
    						con->root = tmp;
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_pthread_mutex_unlock(&con->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					/* And immediately return success. */
    					LOG;
    					return 0;
    				}
    				ep = e;
    				e = e->peer;
    			}
    			/* If we make it here, then it's time for us to go at the very end.
    			   ep *must* be defined or we couldn't have gotten here. */
    			ep->peer = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_pthread_mutex_unlock(&con->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* And immediately return success. */
    			LOG;
    			return 0;
    				
    		} else if (res > 0) {
    			/* Insert ourselves just before 'e'.  We're the first extension of
    			   this kind */
    			tmp->next = e;
    			if (el) {
    				/* We're in the list somewhere */
    				el->next = tmp;
    			} else {
    				/* We're at the top of the list */
    				con->root = tmp;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_pthread_mutex_unlock(&con->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* And immediately return success. */
    			LOG;
    			return 0;
    		}			
    			
    		el = e;
    		e = e->next;
    	}
    	/* If we fall all the way through to here, then we need to be on the end. */
    	if (el)
    		el->next = tmp;
    	else
    		con->root = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&con->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	LOG;
    	return 0;	
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_context_destroy(struct ast_context *con, char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *tmp, *tmpl=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_include *tmpi, *tmpil= NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&conlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tmp = contexts;
    	while(tmp) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (((tmp == con) || !con) &&
    		    (!registrar || !strcasecmp(registrar, tmp->registrar))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Okay, let's lock the structure to be sure nobody else
    			   is searching through it. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (ast_pthread_mutex_lock(&tmp->lock)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Unable to lock context lock\n");
    				return;
    			}
    			if (tmpl)
    				tmpl->next = tmp->next;
    			else
    				contexts = tmp->next;
    			/* Okay, now we're safe to let it go -- in a sense, we were
    			   ready to let it go as soon as we locked it. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_pthread_mutex_unlock(&tmp->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			for (tmpi = tmp->includes; tmpi; ) {
    				/* Free includes */
    				tmpil = tmpi;
    				tmpi = tmpi->next;
    				free(tmpil);
    				tmpil = tmpi;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			free(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!con) {
    				/* Might need to get another one -- restart */
    				tmp = contexts;
    				tmpl = NULL;
    				tmpil = NULL;
    				continue;
    			}
    			ast_pthread_mutex_unlock(&conlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return;
    		}
    		tmpl = tmp;
    		tmp = tmp->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&conlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void wait_for_hangup(struct ast_channel *chan)
    {
    	int res;
    	struct ast_frame *f;
    	do {
    		res = ast_waitfor(chan, -1);
    		if (res < 0)
    			return;
    		f = ast_read(chan);
    		if (f)
    			ast_frfree(f);
    	} while(f);
    }
    
    static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
    {
    	ast_indicate(chan, AST_CONTROL_RINGING);
    	return 0;
    }
    
    static int pbx_builtin_busy(struct ast_channel *chan, void *data)
    {
    	ast_indicate(chan, AST_CONTROL_BUSY);		
    	wait_for_hangup(chan);
    	return -1;
    }
    
    static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
    {
    	ast_indicate(chan, AST_CONTROL_CONGESTION);
    	wait_for_hangup(chan);
    	return -1;
    }
    
    static int pbx_builtin_answer(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	if (chan->state != AST_STATE_RING) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Ignoring answer request since line is not ringing\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	} else
    		return ast_answer(chan);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_setlanguage(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Copy the language as specified */
    	strncpy(chan->language, (char *)data, sizeof(chan->language));
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_hangup(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Just return non-zero and it will hang up */
    	return -1;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_stripmsd(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char newexten[AST_MAX_EXTENSION] = "";
    	if (!data || !atoi(data)) {
    		ast_log(LOG_DEBUG, "Ignoring, since number of digits to strip is 0\n");
    		return 0;
    	}
    	if (strlen(chan->exten) > atoi(data)) {
    		strncpy(newexten, chan->exten + atoi(data), sizeof(newexten));
    	}
    	strncpy(chan->exten, newexten, sizeof(chan->exten));
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_prefix(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char newexten[AST_MAX_EXTENSION] = "";
    	if (!data || !strlen(data)) {
    		ast_log(LOG_DEBUG, "Ignoring, since there is no prefix to add\n");
    		return 0;
    	}
    	snprintf(newexten, sizeof(newexten), "%s%s", (char *)data, chan->exten);
    	strncpy(chan->exten, newexten, sizeof(chan->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "Prepended prefix, new extension is %s\n", chan->exten);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_wait(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Wait for "n" seconds */
    	if (data && atoi((char *)data))
    		sleep(atoi((char *)data));
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_background(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Answer if need be */
    	if (chan->state != AST_STATE_UP)
    		if (ast_answer(chan))
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop anything playing */
    	ast_stopstream(chan);
    	/* Stream a file */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = ast_streamfile(chan, (char *)data, chan->language);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_rtimeout(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Set the timeout for how long to wait between digits */
    	chan->pbx->rtimeout = atoi((char *)data);
    	if (option_verbose > 2)
    		ast_verbose( VERBOSE_PREFIX_3 "Set Response Timeout to %d\n", chan->pbx->rtimeout);
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_dtimeout(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	/* Set the timeout for how long to wait between digits */
    	chan->pbx->dtimeout = atoi((char *)data);
    	if (option_verbose > 2)
    		ast_verbose( VERBOSE_PREFIX_3 "Set Digit Timeout to %d\n", chan->pbx->dtimeout);
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int pbx_builtin_goto(struct ast_channel *chan, void *data)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char *s;
    	char *exten, *pri, *context;
    	if (!data) {
    		ast_log(LOG_WARNING, "Goto requires an argument (optional context|optional extension|priority)\n");
    		return -1;
    	}
    	s = strdup((void *) data);
    	context = strtok(s, "|");
    	exten = strtok(NULL, "|");
    	if (!exten) {
    		/* Only a priority in this one */
    		pri = context;
    		exten = NULL;
    		context = NULL;
    	} else {
    		pri = strtok(NULL, "|");
    		if (!pri) {
    			/* Only an extension and priority in this one */
    			pri = exten;
    			exten = context;
    			context = NULL;
    		}
    	}
    	if (atoi(pri) < 0) {
    		ast_log(LOG_WARNING, "Priority '%s' must be a number > 0\n", pri);
    		free(s);
    		return -1;
    	}
    	/* At this point we have a priority and maybe an extension and a context */
    	chan->priority = atoi(pri) - 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (exten && strcasecmp(exten, "BYEXTENSION"))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		strncpy(chan->exten, exten, sizeof(chan->exten));
    	if (context)
    		strncpy(chan->context, context, sizeof(chan->context));
    	if (option_verbose > 2)
    		ast_verbose( VERBOSE_PREFIX_3 "Goto (%s,%s,%d)\n", chan->context,chan->exten, chan->priority+1);
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    int load_pbx(void)
    {
    	int x;
    	/* Initialize the PBX */
    	if (option_verbose) {
    		ast_verbose( "Asterisk PBX Core Initializing\n");
    		ast_verbose( "Registering builtin applications:\n");
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_cli_register(&showapps);
    	ast_cli_register(&showapp);
    	ast_cli_register(&showdialplan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=0;x<sizeof(builtins) / sizeof(struct pbx_builtin); x++) {
    		if (option_verbose)
    			ast_verbose( VERBOSE_PREFIX_1 "[%s]\n", builtins[x].name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast_register_application(builtins[x].name, builtins[x].execute, builtins[x].synopsis, builtins[x].description)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_ERROR, "Unable to register builtin application '%s'\n", builtins[x].name);
    			return -1;
    		}
    	}
    	return 0;
    }