Skip to content
Snippets Groups Projects
pbx_config.c 42.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * Asterisk -- An open source telephony toolkit.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Copyright (C) 1999 - 2006, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Mark Spencer <markster@digium.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * This program is free software, distributed under the terms of
    
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    
     * \brief Populate and remember extensions from static config file
    
    #include <sys/types.h>
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <ctype.h>
    #include <errno.h>
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    #include "asterisk/pbx.h"
    #include "asterisk/config.h"
    
    #include "asterisk/module.h"
    #include "asterisk/logger.h"
    #include "asterisk/cli.h"
    #include "asterisk/callerid.h"
    
    #ifdef __AST_DEBUG_MALLOC
    static void FREE(void *ptr)
    {
    	free(ptr);
    }
    #else
    #define FREE free
    #endif
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *config = "extensions.conf";
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *registrar = "pbx_config";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int static_config = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int write_protect_config = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int autofallthrough_config = 0;
    
    static int clearglobalvars_config = 0;
    
    AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
    
    static struct ast_context *local_contexts = NULL;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Help for commands provided by this module ...
     */
    static char context_dont_include_help[] =
    
    "Usage: dont include <context> in <context>\n"
    "       Remove an included context from another context.\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char context_remove_extension_help[] =
    "Usage: remove extension exten@context [priority]\n"
    
    "       Remove an extension from a given context. If a priority\n"
    "       is given, only that specific priority from the given extension\n"
    "       will be removed.\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char context_add_include_help[] =
    
    "Usage: include <context> in <context>\n"
    "       Include a context in another context.\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char save_dialplan_help[] =
    "Usage: save dialplan [/path/to/extension/file]\n"
    "       Save dialplan created by pbx_config module.\n"
    "\n"
    "Example: save dialplan                 (/etc/asterisk/extensions.conf)\n"
    "         save dialplan /home/markster  (/home/markster/extensions.conf)\n";
    
    static char context_add_extension_help[] =
    "Usage: add extension <exten>,<priority>,<app>,<app-data> into <context>\n"
    "       [replace]\n\n"
    "       This command will add new extension into <context>. If there is an\n"
    "       existence of extension with the same priority and last 'replace'\n"
    "       arguments is given here we simply replace this extension.\n"
    "\n"
    "Example: add extension 6123,1,Dial,IAX/216.207.245.56/6123 into local\n"
    "         Now, you can dial 6123 and talk to Markster :)\n";
    
    static char context_add_ignorepat_help[] =
    "Usage: add ignorepat <pattern> into <context>\n"
    
    Russell Bryant's avatar
    Russell Bryant committed
    "       This command adds a new ignore pattern into context <context>\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "\n"
    "Example: add ignorepat _3XX into local\n";
    
    static char context_remove_ignorepat_help[] =
    "Usage: remove ignorepat <pattern> from <context>\n"
    
    "       This command removes an ignore pattern from context <context>\n"
    
    Mark Spencer's avatar
    Mark Spencer committed
    "\n"
    "Example: remove ignorepat _3XX from local\n";
    
    
    static char reload_extensions_help[] =
    "Usage: reload extensions.conf without reloading any other modules\n"
    
    "       This command does not delete global variables unless\n"
    "       clearglobalvars is set to yes in extensions.conf\n"
    
    Martin Pycko's avatar
    Martin Pycko committed
    "Example: extensions reload\n";
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Implementation of functions provided by this module
     */
    
    
    Mark Spencer's avatar
    Mark Spencer committed
     * REMOVE INCLUDE command stuff
     */
    static int handle_context_dont_include(int fd, int argc, char *argv[])
    {
    
    	if (argc != 5)
    		return RESULT_SHOWUSAGE;
    
    	if (strcmp(argv[3], "in"))
    		return RESULT_SHOWUSAGE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (!ast_context_remove_include(argv[4], argv[2], registrar)) {
    		ast_cli(fd, "We are not including '%s' in '%s' now\n",
    			argv[2], argv[4]);
    		return RESULT_SUCCESS;
    	}
    
    	ast_cli(fd, "Failed to remove '%s' include from '%s' context\n",
    		argv[2], argv[4]);
    	return RESULT_FAILURE;
    }
    
    
    /*! \brief return true if 'name' is included by context c */
    static int lookup_ci(struct ast_context *c, const char *name)
    {
    	struct ast_include *i = NULL;
    
    	if (ast_lock_context(c))	/* error, skip */
    		return 0;
    	while ( (i = ast_walk_context_includes(c, i)) )
    		if (!strcmp(name, ast_get_include_name(i)))
    			break;
    	ast_unlock_context(c);
    	return i ? -1 /* success */ : 0;
    }
    
    /*! \brief return true if 'name' is in the ignorepats for context c */
    static int lookup_c_ip(struct ast_context *c, const char *name)
    {
    	struct ast_ignorepat *ip = NULL;
    
    	if (ast_lock_context(c))	/* error, skip */
    		return 0;
    	while ( (ip = ast_walk_context_ignorepats(c, ip)) )
    		if (!strcmp(name, ast_get_ignorepat_name(ip)))
    			break;
    	ast_unlock_context(c);
    	return ip ? -1 /* success */ : 0;
    }
    
    /*! \brief moves to the n-th word in the string, or empty string if none */
    static const char *skip_words(const char *p, int n)
    {
    	int in_blank = 0;
    	for (;n && *p; p++) {
    		if (isblank(*p) /* XXX order is important */ && !in_blank) {
    			n--;	/* one word is gone */
    			in_blank = 1;
    		} else if (/* !is_blank(*p), we know already, && */ in_blank) {
    			in_blank = 0;
    		}
    	}
    	return p;
    }
    
    /*! \brief match the first 'len' chars of word. len==0 always succeeds */
    static int partial_match(const char *s, const char *word, int len)
    {
    	return (len == 0 || !strncmp(s, word, len));
    }
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    /*! \brief split extension\@context in two parts, return -1 on error.
    
     * The return string is malloc'ed and pointed by *ext
     */
    static int split_ec(const char *src, char **ext, char ** const ctx)
    {
    	char *c, *e = ast_strdup(src); /* now src is not used anymore */
    
    	if (e == NULL)
    		return -1;	/* malloc error */
    	/* now, parse values from 'exten@context' */
    	*ext = e;
    	c = strchr(e, '@');
    	if (c == NULL)	/* no context part */
    		*ctx = "";	/* it is not overwritten, anyways */
    	else {	/* found context, check for duplicity ... */
    		*c++ = '\0';
    		*ctx = c;
    		if (strchr(c, '@')) { /* two @, not allowed */
    			free(e);
    			return -1;
    		}
    	} 
    	return 0;
    }
    
    /* _X_ is the string we need to complete */
    
    static char *complete_context_dont_include(const char *line, const char *word,
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int pos, int state)
    {
    	int which = 0;
    
    	char *res = NULL;
    	int len = strlen(word); /* how many bytes to match */
    	struct ast_context *c = NULL;
    
    	if (pos == 2) {		/* "dont include _X_" */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast_lock_contexts()) {
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    			return NULL;
    		}
    
    		/* walk contexts and their includes, return the n-th match */
    		while (!res && (c = ast_walk_contexts(c))) {
    			struct ast_include *i = NULL;
    
    			if (ast_lock_context(c))	/* error ? skip this one */
    
    Mark Spencer's avatar
    Mark Spencer committed
    				continue;
    
    
    			while ( !res && (i = ast_walk_context_includes(c, i)) ) {
    				const char *i_name = ast_get_include_name(i);
    				struct ast_context *nc = NULL;
    				int already_served = 0;
    
    				if (!partial_match(i_name, word, len))
    					continue;	/* not matched */
    
    				/* check if this include is already served or not */
    
    				/* go through all contexts again till we reach actual
    				 * context or already_served = 1
    				 */
    				while ( (nc = ast_walk_contexts(nc)) && nc != c && !already_served)
    					already_served = lookup_ci(nc, i_name);
    
    				if (!already_served && ++which > state)
    					res = strdup(i_name);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_unlock_context(c);
    		}
    
    		ast_unlock_contexts();
    
    		return res;
    	} else if (pos == 3) { /* "dont include CTX _X_" */
    		/*
    		 * complete as 'in', but only if previous context is really
    		 * included somewhere
    		 */
    		char *context, *dupline;
    		const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    
    		context = dupline = strdup(s);
    		if (!dupline) {
    			ast_log(LOG_ERROR, "Out of free memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    		if (ast_lock_contexts()) {
    
    			ast_log(LOG_ERROR, "Failed to lock contexts list\n");
    			free(context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    
    		/* go through all contexts and check if is included ... */
    
    		while (!res && (c = ast_walk_contexts(c)))
    			if (lookup_ci(c, context)) /* context is really included, complete "in" command */
    				res = strdup("in");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_unlock_contexts();
    
    		if (!res)
    			ast_log(LOG_WARNING, "%s not included anywhere\n", context);
    		free(context);
    		return res;
    	} else if (pos == 4) { /* "dont include CTX in _X_" */
    		/*
    		 * Context from which we removing include ... 
    		 */
    		char *context, *dupline, *in;
    		const char *s = skip_words(line, 2); /* skip 'dont' 'include' */
    		context = dupline = strdup(s);
    		if (!dupline) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_ERROR, "Out of free memory\n");
    			return NULL;
    		}
    
    
    		strsep(&dupline, " "); /* skip context */
    
    		/* third word must be 'in' */
    		in = strsep(&dupline, " ");
    		if (!in || strcmp(in, "in")) {
    			free(context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    
    		if (ast_lock_contexts()) {
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    
    		/* walk through all contexts ... */
    
    		c = NULL;
    		while ( !res && (c = ast_walk_contexts(c))) {
    			const char *c_name = ast_get_context_name(c);
    			if (!partial_match(c_name, word, len))	/* not a good target */
    				continue;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* walk through all includes and check if it is our context */	
    
    			if (lookup_ci(c, context) && ++which > state)
    				res = strdup(c_name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
     * REMOVE EXTENSION command stuff
     */
    static int handle_context_remove_extension(int fd, int argc, char *argv[])
    {
    	int removing_priority = 0;
    	char *exten, *context;
    
    	int ret = RESULT_FAILURE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (argc != 4 && argc != 3) return RESULT_SHOWUSAGE;
    
    	/*
    	 * Priority input checking ...
    	 */
    	if (argc == 4) {
    		char *c = argv[3];
    
    		/* check for digits in whole parameter for right priority ...
    		 * why? because atoi (strtol) returns 0 if any characters in
    		 * string and whole extension will be removed, it's not good
    		 */
    
    		if (!strcmp("hint", c))
    			removing_priority = PRIORITY_HINT;
    		else {
    			while (*c && isdigit(*c))
    				c++;
    			if (*c) { /* non-digit in string */
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_cli(fd, "Invalid priority '%s'\n", argv[3]);
    				return RESULT_FAILURE;
    			}
    
    			removing_priority = atoi(argv[3]);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    		if (removing_priority == 0) {
    			ast_cli(fd, "If you want to remove whole extension, please " \
    				"omit priority argument\n");
    			return RESULT_FAILURE;
    		}
    	}
    
    
    	/* XXX original overwrote argv[2] */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/*
    	 * Format exten@context checking ...
    	 */
    
    	if (split_ec(argv[2], &exten, &context))
    		return RESULT_FAILURE; /* XXX malloc failure */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((!strlen(exten)) || (!(strlen(context)))) {
    
    		ast_cli(fd, "Missing extension or context name in second argument '%s'\n",
    			argv[2]);
    		free(exten);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_FAILURE;
    	}
    
    	if (!ast_context_remove_extension(context, exten, removing_priority, registrar)) {
    		if (!removing_priority)
    			ast_cli(fd, "Whole extension %s@%s removed\n",
    				exten, context);
    		else
    			ast_cli(fd, "Extension %s@%s with priority %d removed\n",
    				exten, context, removing_priority);
    			
    
    		ret = RESULT_SUCCESS;
    	} else {
    		ast_cli(fd, "Failed to remove extension %s@%s\n", exten, context);
    		ret = RESULT_FAILURE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    #define BROKEN_READLINE 1
    
    #ifdef BROKEN_READLINE
    /*
     * There is one funny thing, when you have word like 300@ and you hit
     * <tab>, you arguments will like as your word is '300 ', so it '@'
     * characters acts sometimes as word delimiter and sometimes as a part
     * of word
     *
     * This fix function, allocates new word variable and store here every
     * time xxx@yyy always as one word and correct pos is set too
     *
     * It's ugly, I know, but I'm waiting for Mark suggestion if upper is
     * bug or feature ...
     */
    
    static int fix_complete_args(const char *line, char **word, int *pos)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char *_line, *_strsep_line, *_previous_word = NULL, *_word = NULL;
    	int words = 0;
    
    	_line = strdup(line);
    
    	_strsep_line = _line;
    	while (_strsep_line) {
    		_previous_word = _word;
    		_word = strsep(&_strsep_line, " ");
    
    		if (_word && strlen(_word)) words++;
    	}
    
    
    	if (_word || _previous_word) {
    		if (_word) {
    			if (!strlen(_word)) words++;
    			*word = strdup(_word);
    		} else
    			*word = strdup(_previous_word);
    		*pos = words - 1;
    		free(_line);
    		return 0;
    	}
    
    	free(_line);
    	return -1;
    }
    #endif /* BROKEN_READLINE */
    
    
    static char *complete_context_remove_extension(const char *line, const char *word, int pos,
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int state)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int which = 0;
    
    #ifdef BROKEN_READLINE
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/*
    	 * Fix arguments, *word is a new allocated structure, REMEMBER to
    	 * free *word when you want to return from this function ...
    	 */
    
    	if (fix_complete_args(line, &word2, &pos)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Out of free memory\n");
    		return NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    
    	if (pos == 2) { /* 'remove extension _X_' (exten@context ... */
    		struct ast_context *c = NULL;
    		char *context = NULL, *exten = NULL;
    		int le = 0;	/* length of extension */
    		int lc = 0;	/* length of context */
    
    		lc = split_ec(word, &exten, &context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef BROKEN_READLINE
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    		if (lc)	/* error */
    			return NULL;
    		le = strlen(exten);
    		lc = strlen(context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    		if (ast_lock_contexts()) {
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		/* find our context ... */
    
    		while ( (c = ast_walk_contexts(c)) ) {	/* match our context if any */
    			struct ast_exten *e = NULL;
    			/* XXX locking ? */
    			if (!partial_match(ast_get_context_name(c), context, lc))
    				continue;	/* context not matched */
    			while ( (e = ast_walk_context_extensions(c, e)) ) { /* try to complete extensions ... */
    				if ( partial_match(ast_get_extension_name(e), exten, le) && ++which > state) { /* n-th match */
    					/* If there is an extension then return exten@context. XXX otherwise ? */
    					if (exten)
    						asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c));
    					break;
    
    			if (e)	/* got a match */
    				break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		ast_unlock_contexts();
    
    	error2:
    		if (exten)
    			free(exten);
    	} else if (pos == 3) { /* 'remove extension EXT _X_' (priority) */
    		char *exten = NULL, *context, *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		struct ast_context *c;
    
    		int le, lc, len;
    		const char *s = skip_words(line, 2); /* skip 'remove' 'extension' */
    		int i = split_ec(s, &exten, &context);	/* parse ext@context */
    
    		if (i)	/* error */
    			goto error3;
    		if ( (p = strchr(exten, ' ')) ) /* remove space after extension */
    			*p = '\0';
    		if ( (p = strchr(context, ' ')) ) /* remove space after context */
    			*p = '\0';
    		le = strlen(exten);
    		lc = strlen(context);
    		len = strlen(word);
    		if (le == 0 || lc == 0)
    			goto error3;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    		if (ast_lock_contexts()) {
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		/* walk contexts */
    
    		c = NULL;
    		while ( (c = ast_walk_contexts(c)) ) {
    			/* XXX locking on c ? */
    			struct ast_exten *e;
    			if (strcmp(ast_get_context_name(c), context) != 0)
    				continue;
    			/* got it, we must match here */
    			e = NULL;
    			while ( (e = ast_walk_context_extensions(c, e)) ) {
    				struct ast_exten *priority;
    				char buffer[10];
    
    				if (strcmp(ast_get_extension_name(e), exten) != 0)
    					continue;
    				/* XXX lock e ? */
    				priority = NULL;
    				while ( !ret && (priority = ast_walk_extension_priorities(e, priority)) ) {
    					snprintf(buffer, sizeof(buffer), "%u", ast_get_extension_priority(priority));
    					if (partial_match(buffer, word, len) && ++which > state) /* n-th match */
    						ret = strdup(buffer);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		ast_unlock_contexts();
    
    	error3:
    		if (exten)
    			free(exten);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef BROKEN_READLINE
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
     * Include context ...
     */
    static int handle_context_add_include(int fd, int argc, char *argv[])
    {
    
    	if (argc != 5) /* include context CTX in CTX */
    		return RESULT_SHOWUSAGE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* third arg must be 'in' ... */
    
    	if (strcmp(argv[3], "in") && strcmp(argv[3], "into")) /* XXX why both ? */
    		return RESULT_SHOWUSAGE;
    
    	if (ast_context_add_include(argv[4], argv[2], registrar)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		switch (errno) {
    
    		case ENOMEM:
    			ast_cli(fd, "Out of memory for context addition\n");
    			break;
    
    		case EBUSY:
    			ast_cli(fd, "Failed to lock context(s) list, please try again later\n");
    			break;
    
    		case EEXIST:
    			ast_cli(fd, "Context '%s' already included in '%s' context\n",
    				argv[2], argv[4]);
    			break;
    
    		case ENOENT:
    		case EINVAL:
    			ast_cli(fd, "There is no existence of context '%s'\n",
    				errno == ENOENT ? argv[4] : argv[2]);
    			break;
    
    		default:
    			ast_cli(fd, "Failed to include '%s' in '%s' context\n",
    				argv[2], argv[4]);
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		return RESULT_FAILURE;
    	}
    
    	/* show some info ... */
    	ast_cli(fd, "Context '%s' included in '%s' context\n",
    
    		argv[2], argv[4]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return RESULT_SUCCESS;
    }
    
    
    static char *complete_context_add_include(const char *line, const char *word, int pos,
    
    Mark Spencer's avatar
    Mark Spencer committed
        int state)
    {
    	struct ast_context *c;
    	int which = 0;
    
    	char *ret = NULL;
    	int len = strlen(word);
    
    	if (pos == 2) {		/* 'include context _X_' (context) ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast_lock_contexts()) {
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    			return NULL;
    		}
    
    		for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
    			if (partial_match(ast_get_context_name(c), word, len) && ++which > state)
    				ret = strdup(ast_get_context_name(c));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_unlock_contexts();
    
    		return ret;
    	} else if (pos == 3) { /* include context CTX _X_ */
    		/* complete  as 'in' if context exists or we are unable to check */
    		char *context, *dupline;
    		struct ast_context *c;
    		const char *s = skip_words(line, 2);	/* should not fail */
    
    		if (state != 0)	/* only once */
    			return NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    		/* parse context from line ... */
    
    		context = dupline = strdup(s);
    		if (!context) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_ERROR, "Out of free memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		/* check for context existence ... */
    		if (ast_lock_contexts()) {
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    			/* our fault, we can't check, so complete 'in' ... */
    			ret = strdup("in");
    		} else {
    			for (c = NULL; !ret && (c = ast_walk_contexts(c)); )
    				if (!strcmp(context, ast_get_context_name(c)))
    					ret = strdup("in"); /* found */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_unlock_contexts();
    
    		}
    		free(context);
    		return ret;
    	} else if (pos == 4) { /* 'include context CTX in _X_' (dst context) */
    		char *context, *dupline, *in;
    		const char *s = skip_words(line, 2); /* should not fail */
    		context = dupline = strdup(s);
    		if (!dupline) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_ERROR, "Out of free memory\n");
    			return NULL;
    		}
    
    		strsep(&dupline, " "); /* skip context */
    		in = strsep(&dupline, " ");
    		/* error if missing context or third word is not 'in' */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strlen(context) || strcmp(in, "in")) {
    
    			ast_log(LOG_ERROR, "bad context %s or missing in %s\n",
    				context, in);
    			goto error3;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		if (ast_lock_contexts()) {
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    
    		for (c = NULL; (c = ast_walk_contexts(c)); )
    			if (!strcmp(context, ast_get_context_name(c)))
    				break;
    		if (c) { /* first context exists, go on... */
    			/* go through all contexts ... */
    			for (c = NULL; !ret && (c = ast_walk_contexts(c)); ) {
    				if (!strcmp(context, ast_get_context_name(c)))
    					continue; /* skip ourselves */
    				if (partial_match(ast_get_context_name(c), word, len) &&
    						!lookup_ci(c, context) /* not included yet */ &&
    						++which > state)
    					ret = strdup(ast_get_context_name(c));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    		} else {
    			ast_log(LOG_ERROR, "context %s not found\n", context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		ast_unlock_contexts();
    
    	error3:
    		free(context);
    		return ret;
    
    /*!
     * \brief 'save dialplan' CLI command implementation functions ...
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    static int handle_save_dialplan(int fd, int argc, char *argv[])
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_context *c;
    
    	struct ast_config *cfg;
    	struct ast_variable *v;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int incomplete = 0; /* incomplete config write? */
    	FILE *output;
    
    
    	const char *base, *slash, *file;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (! (static_config && !write_protect_config)) {
    		ast_cli(fd,
    			"I can't save dialplan now, see '%s' example file.\n",
    			config);
    		return RESULT_FAILURE;
    	}
    
    
    	if (argc != 2 && argc != 3)
    		return RESULT_SHOWUSAGE;
    
    	if (ast_mutex_lock(&save_dialplan_lock)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cli(fd,
    			"Failed to lock dialplan saving (another proccess saving?)\n");
    		return RESULT_FAILURE;
    	}
    
    	/* XXX the code here is quite loose, a pathname with .conf in it
    	 * is assumed to be a complete pathname
    	 */
    	if (argc == 3) {	/* have config path. Look for *.conf */
    		base = argv[2];
    		if (!strstr(argv[2], ".conf")) { /*no, this is assumed to be a pathname */
    			/* if filename ends with '/', do not add one */
    			slash = (*(argv[2] + strlen(argv[2]) -1) == '/') ? "/" : "";
    			file = config;	/* default: 'extensions.conf' */
    		} else {	/* yes, complete file name */
    			slash = "";
    			file = "";
    		}
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* no config file, default one */
    
    		base = ast_config_AST_CONFIG_DIR;
    		slash = "/";
    		file = config;
    	}
    	snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
    
    	cfg = ast_config_load("extensions.conf");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* try to lock contexts list */
    	if (ast_lock_contexts()) {
    		ast_cli(fd, "Failed to lock contexts list\n");
    
    		ast_mutex_unlock(&save_dialplan_lock);
    
    		ast_config_destroy(cfg);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_FAILURE;
    	}
    
    	/* create new file ... */
    	if (!(output = fopen(filename, "wt"))) {
    		ast_cli(fd, "Failed to create file '%s'\n",
    			filename);
    		ast_unlock_contexts();
    
    		ast_mutex_unlock(&save_dialplan_lock);
    
    		ast_config_destroy(cfg);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_FAILURE;
    	}
    
    	/* fireout general info */
    	fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\n\n",
    		static_config ? "yes" : "no",
    		write_protect_config ? "yes" : "no");
    
    
    	if ((v = ast_variable_browse(cfg, "globals"))) {
    		fprintf(output, "[globals]\n");
    		while(v) {
    			fprintf(output, "%s => %s\n", v->name, v->value);
    			v = v->next;
    		}
    		fprintf(output, "\n");
    	}
    
    
    	ast_config_destroy(cfg);
    
    #define PUT_CTX_HDR	do { \
    	if (!context_header_written) {	\
    		fprintf(output, "[%s]\n", ast_get_context_name(c));	\
    		context_header_written = 1;	\
    	}	\
    	} while (0)
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* walk all contexts */
    
    	for (c = NULL; (c = ast_walk_contexts(c)); ) {
    		int context_header_written = 0;
    		struct ast_exten *e, *last_written_e = NULL;
    		struct ast_include *i;
    		struct ast_ignorepat *ip;
    		struct ast_sw *sw;
    
    		/* try to lock context and fireout all info */	
    		if (ast_lock_context(c)) { /* lock failure */
    			incomplete = 1;
    			continue;
    		}
    		/* registered by this module? */
    		/* XXX do we need this ? */
    		if (!strcmp(ast_get_context_registrar(c), registrar)) {
    			fprintf(output, "[%s]\n", ast_get_context_name(c));
    			context_header_written = 1;
    		}
    
    		/* walk extensions ... */
    		for (e = NULL; (e = ast_walk_context_extensions(c, e)); ) {
    			struct ast_exten *p = NULL;
    
    			/* fireout priorities */
    			while ( (p = ast_walk_extension_priorities(e, p)) ) {
    				if (strcmp(ast_get_extension_registrar(p), registrar) != 0) /* not this source */
    					continue;
    		
    				/* make empty line between different extensions */	
    				if (last_written_e != NULL &&
    					    strcmp(ast_get_extension_name(last_written_e),
    						    ast_get_extension_name(p)))
    					fprintf(output, "\n");
    				last_written_e = p;
    
    				PUT_CTX_HDR;
    
    				if (ast_get_extension_priority(p)==PRIORITY_HINT) { /* easy */
    					fprintf(output, "exten => %s,hint,%s\n",
    						    ast_get_extension_name(p),
    						    ast_get_extension_app(p));
    				} else { /* copy and replace '|' with ',' */
    					const char *sep, *cid;
    					char *tempdata = strdup(ast_get_extension_app_data(p));
    					char *s;
    
    					if (!tempdata) { /* XXX error duplicating string ? */
    						incomplete = 1;
    						continue;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					}
    
    					for (s = tempdata; *s; s++)
    						if (*s == '|')
    							*s = ',';
    					if (ast_get_extension_matchcid(p)) {
    						sep = "/";
    						cid = ast_get_extension_cidmatch(p);
    					} else {
    						sep = cid = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    					}
    
    					fprintf(output, "exten => %s%s%s,%d,%s(%s)\n",
    					    ast_get_extension_name(p), sep, cid,
    					    ast_get_extension_priority(p),
    					    ast_get_extension_app(p), tempdata);
    					free(tempdata);
    
    		/* written any extensions? ok, write space between exten & inc */
    		if (last_written_e)
    			fprintf(output, "\n");
    
    		/* walk through includes */
    		for (i = NULL; (i = ast_walk_context_includes(c, i)) ; ) {
    			if (strcmp(ast_get_include_registrar(i), registrar) != 0)
    				continue; /* not mine */
    			PUT_CTX_HDR;
    			fprintf(output, "include => %s\n", ast_get_include_name(i));
    		}
    		if (ast_walk_context_includes(c, NULL))
    			fprintf(output, "\n");
    
    		/* walk through switches */
    		for (sw = NULL; (sw = ast_walk_context_switches(c, sw)) ; ) {
    			if (strcmp(ast_get_switch_registrar(sw), registrar) != 0)
    				continue; /* not mine */
    			PUT_CTX_HDR;
    			fprintf(output, "switch => %s/%s\n",
    				    ast_get_switch_name(sw), ast_get_switch_data(sw));
    		}
    
    		if (ast_walk_context_switches(c, NULL))
    			fprintf(output, "\n");
    
    		/* fireout ignorepats ... */
    		for (ip = NULL; (ip = ast_walk_context_ignorepats(c, ip)); ) {
    			if (strcmp(ast_get_ignorepat_registrar(ip), registrar) != 0)
    				continue; /* not mine */
    			PUT_CTX_HDR;
    			fprintf(output, "ignorepat => %s\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_get_ignorepat_name(ip));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}	
    
    	ast_unlock_contexts();
    
    	ast_mutex_unlock(&save_dialplan_lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	fclose(output);
    
    	if (incomplete) {
    		ast_cli(fd, "Saved dialplan is incomplete\n");
    		return RESULT_FAILURE;
    	}
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    	ast_cli(fd, "Dialplan successfully saved into '%s'\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    		filename);
    	return RESULT_SUCCESS;
    }
    
    
    /*!
     * \brief ADD EXTENSION command stuff
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    static int handle_context_add_extension(int fd, int argc, char *argv[])
    {
    	char *whole_exten;
    	char *exten, *prior;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *cidmatch, *app, *app_data;
    
    	char *start, *end;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* check for arguments at first */
    
    	if (argc != 5 && argc != 6)
    		return RESULT_SHOWUSAGE;
    	if (strcmp(argv[3], "into"))
    		return RESULT_SHOWUSAGE;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (argc == 6) if (strcmp(argv[5], "replace")) return RESULT_SHOWUSAGE;
    
    
    	/* XXX overwrite argv[2] */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	whole_exten = argv[2];
    
    	exten 	= strsep(&whole_exten,",");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (strchr(exten, '/')) {
    		cidmatch = exten;
    		strsep(&cidmatch,"/");
    	} else {
    		cidmatch = NULL;
    	}
    	prior       = strsep(&whole_exten,",");
    
    		if (!strcmp(prior, "hint")) {
    
    			iprior = PRIORITY_HINT;
    		} else {
    
    				ast_cli(fd, "'%s' is not a valid priority\n", prior);
    				prior = NULL;
    			}
    
    	if (app && (start = strchr(app, '(')) && (end = strrchr(app, ')'))) {
    
    		*start = *end = '\0';
    		app_data = start + 1;
    
    		ast_process_quotes_and_slashes(app_data, ',', '|');
    
    	} else {
    		if (app) {
    			app_data = strchr(app, ',');
    			if (app_data) {
    				*app_data = '\0';
    				app_data++;
    			}
    		} else	
    			app_data = NULL;
    	}
    
    	if (!exten || !prior || !app || (!app_data && iprior != PRIORITY_HINT))
    		return RESULT_SHOWUSAGE;
    
    James Golovich's avatar
    James Golovich committed
    		app_data="";
    
    	if (ast_add_extension(argv[4], argc == 6 ? 1 : 0, exten, iprior, NULL, cidmatch, app,
    
    Mark Spencer's avatar
    Mark Spencer committed
    		(void *)strdup(app_data), free, registrar)) {
    		switch (errno) {
    
    		case ENOMEM:
    			ast_cli(fd, "Out of free memory\n");