Skip to content
Snippets Groups Projects
pbx_config.c 60.1 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
    
    /*** MODULEINFO
    	<support_level>core</support_level>
     ***/
    
    
    /*** DOCUMENTATION
    	<manager name="DialplanExtensionAdd" language="en_US">
    		<synopsis>
    			Add an extension to the dialplan
    		</synopsis>
    		<syntax>
    			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
    			<parameter name="Context" required="true">
    				<para>Context where the extension will be created. The context will
    				be created if it does not already exist.</para>
    			</parameter>
    			<parameter name="Extension" required="true">
    				<para>Name of the extension that will be created (may include callerid match by separating
    				with '/')</para>
    			</parameter>
    			<parameter name="Priority" required="true">
    				<para>Priority being added to this extension. Must be either <literal>hint</literal> or a
    				numerical value.</para>
    			</parameter>
    			<parameter name="Application" required="true">
    				<para>The application to use for this extension at the requested priority</para>
    			</parameter>
    			<parameter name="ApplicationData" required="false">
    				<para>Arguments to the application.</para>
    			</parameter>
    			<parameter name="Replace" required="false">
    				<para>If set to 'yes', '1', 'true' or any of the other values we evaluate as true, then
    				if an extension already exists at the requested context, extension, and priority it will
    				be overwritten. Otherwise, the existing extension will remain and the action will fail.
    				</para>
    			</parameter>
    		</syntax>
    	</manager>
    	<manager name="DialplanExtensionRemove" language="en_US">
    		<synopsis>
    			Remove an extension from the dialplan
    		</synopsis>
    		<syntax>
    			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
    			<parameter name="Context" required="true">
    				<para>Context of the extension being removed</para>
    			</parameter>
    			<parameter name="Extension" required="true">
    				<para>Name of the extension being removed (may include callerid match by separating with '/')</para>
    			</parameter>
    			<parameter name="Priority" required="false">
    				<para>If provided, only remove this priority from the extension instead of all
    				priorities in the extension.</para>
    			</parameter>
    		</syntax>
    	</manager>
     ***/
    
    
    ASTERISK_REGISTER_FILE()
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    #include <ctype.h>
    
    
    #include "asterisk/paths.h"	/* ast_config_AST_CONFIG_DIR */
    
    #include "asterisk/pbx.h"
    #include "asterisk/config.h"
    #include "asterisk/module.h"
    #include "asterisk/logger.h"
    #include "asterisk/cli.h"
    
    #include "asterisk/channel.h"	/* AST_MAX_EXTENSION */
    
    static const char config[] = "extensions.conf";
    static const char registrar[] = "pbx_config";
    
    static char userscontext[AST_MAX_EXTENSION] = "default";
    
    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;
    
    static int autofallthrough_config = 1;
    
    static int clearglobalvars_config = 0;
    
    static int extenpatternmatchnew_config = 0;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    static char *overrideswitch_config = NULL;
    
    AST_MUTEX_DEFINE_STATIC(save_dialplan_lock);
    
    static struct ast_context *local_contexts = NULL;
    
    Steve Murphy's avatar
    Steve Murphy committed
    static struct ast_hashtab *local_table = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * Prototypes for our completion functions
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    static char *complete_dialplan_remove_include(struct ast_cli_args *);
    static char *complete_dialplan_add_include(struct ast_cli_args *);
    static char *complete_dialplan_remove_ignorepat(struct ast_cli_args *);
    static char *complete_dialplan_add_ignorepat(struct ast_cli_args *);
    static char *complete_dialplan_remove_extension(struct ast_cli_args *);
    static char *complete_dialplan_add_extension(struct ast_cli_args *);
    
    static char *complete_dialplan_remove_context(struct ast_cli_args *);
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Implementation of functions provided by this module
     */
    
    
    /*!
     * * REMOVE context command stuff
     */
    
    static char *handle_cli_dialplan_remove_context(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    {
    	struct ast_context *con;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "dialplan remove context";
    		e->usage =
    			"Usage: dialplan remove context <context>\n"
    			"       Removes all extensions from a specified context.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return complete_dialplan_remove_context(a);
    	}
    
    	if (a->argc != 4) {
    		return CLI_SHOWUSAGE;
    	}
    
    	con = ast_context_find(a->argv[3]);
    
    	if (!con) {
    		ast_cli(a->fd, "There is no such context as '%s'\n",
                            a->argv[3]);
                    return CLI_SUCCESS;
    	} else {
    		ast_context_destroy(con, registrar);
    		ast_cli(a->fd, "Removing context '%s'\n",
    			a->argv[3]);
    		return CLI_SUCCESS;
    	}
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
     * REMOVE INCLUDE command stuff
     */
    
    static char *handle_cli_dialplan_remove_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "dialplan remove include";
    		e->usage =
    			"Usage: dialplan remove include <context> from <context>\n"
    			"       Remove an included context from another context.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return complete_dialplan_remove_include(a);
    	}
    
    	if (a->argc != 6 || strcmp(a->argv[4], "from"))
    
    	if (!ast_context_remove_include(a->argv[5], a->argv[3], registrar)) {
    		ast_cli(a->fd, "We are not including '%s' into '%s' now\n",
    			a->argv[3], a->argv[5]);
    		return CLI_SUCCESS;
    
    	ast_cli(a->fd, "Failed to remove '%s' include from '%s' context\n",
    		a->argv[3], a->argv[5]);
    	return CLI_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_rdlock_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_rdlock_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 ** const cid)
    
    	char *i, *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 */
    
    	}
    	if (cid && (i = strchr(e, '/'))) {
    		*i++ = '\0';
    		*cid = i;
    	} else if (cid) {
    		/* Signal none detected */
    		*cid = NULL;
    	}
    
    	return 0;
    }
    
    /* _X_ is the string we need to complete */
    
    static char *complete_dialplan_remove_include(struct ast_cli_args *a)
    
    {
    	int which = 0;
    	char *res = NULL;
    
    	int len = strlen(a->word); /* how many bytes to match */
    
    	struct ast_context *c = NULL;
    
    
    	if (a->pos == 3) {		/* "dialplan remove include _X_" */
    
    			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_rdlock_context(c))	/* error ? skip this one */
    
    				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, a->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 > a->n)
    
    			}
    			ast_unlock_context(c);
    		}
    
    		ast_unlock_contexts();
    		return res;
    
    	} else if (a->pos == 4) { /* "dialplan remove include CTX _X_" */
    
    		/*
    		 * complete as 'from', but only if previous context is really
    		 * included somewhere
    		 */
    		char *context, *dupline;
    
    		const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */
    
    		context = dupline = ast_strdup(s);
    
    		if (!dupline) {
    			ast_log(LOG_ERROR, "Out of free memory\n");
    			return NULL;
    		}
    		strsep(&dupline, " ");
    
    
    			ast_log(LOG_ERROR, "Failed to lock contexts list\n");
    
    			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 "from" command */
    
    		ast_unlock_contexts();
    		if (!res)
    			ast_log(LOG_WARNING, "%s not included anywhere\n", context);
    
    	} else if (a->pos == 5) { /* "dialplan remove include CTX from _X_" */
    
    		/*
    		 * Context from which we removing include ... 
    		 */
    		char *context, *dupline, *from;
    
    		const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'include' */
    
    		context = dupline = ast_strdup(s);
    
    		if (!dupline) {
    			ast_log(LOG_ERROR, "Out of free memory\n");
    			return NULL;
    		}
    
    		strsep(&dupline, " "); /* skip context */
    
    		/* fourth word must be 'from' */
    		from = strsep(&dupline, " ");
    		if (!from || strcmp(from, "from")) {
    
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    
    			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, a->word, len))	/* not a good target */
    
    				continue;
    			/* walk through all includes and check if it is our context */	
    
    			if (lookup_ci(c, context) && ++which > a->n)
    
    		}
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
     * REMOVE EXTENSION command stuff
     */
    
    static char *handle_cli_dialplan_remove_extension(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    {
    	int removing_priority = 0;
    
    	char *exten, *context, *cid;
    
    	char *ret = CLI_FAILURE;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "dialplan remove extension";
    		e->usage =
    
    			"Usage: dialplan remove extension exten[/cid]@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";
    		return NULL;
    	case CLI_GENERATE:
    		return complete_dialplan_remove_extension(a);
    	}
    
    	if (a->argc != 5 && a->argc != 4)
    		return CLI_SHOWUSAGE;
    
    
    	/*
    	 * Priority input checking ...
    	 */
    
    		const char *c = a->argv[4];
    
    
    		/* 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 */
    
    				ast_cli(a->fd, "Invalid priority '%s'\n", a->argv[4]);
    				return CLI_FAILURE;
    
    			removing_priority = atoi(a->argv[4]);
    
    		}
    
    		if (removing_priority == 0) {
    
    			ast_cli(a->fd, "If you want to remove whole extension, please " \
    
    				"omit priority argument\n");
    
    		}
    	}
    
    	/* XXX original overwrote argv[3] */
    	/*
    	 * Format exten@context checking ...
    	 */
    
    	if (split_ec(a->argv[3], &exten, &context, &cid))
    
    		return CLI_FAILURE; /* XXX malloc failure */
    
    	if ((!strlen(exten)) || (!(strlen(context)))) {
    
    		ast_cli(a->fd, "Missing extension or context name in third argument '%s'\n",
    			a->argv[3]);
    
    	if (!ast_context_remove_extension_callerid(context, exten, removing_priority,
    			/* Do NOT substitute S_OR; it is NOT the same thing */
    			cid ? cid : (removing_priority ? "" : NULL), cid ? 1 : 0, registrar)) {
    
    		if (!removing_priority)
    
    			ast_cli(a->fd, "Whole extension %s@%s removed\n",
    
    				exten, context);
    		else
    
    			ast_cli(a->fd, "Extension %s@%s with priority %d removed\n",
    
    				exten, context, removing_priority);
    			
    
    		if (cid) {
    			ast_cli(a->fd, "Failed to remove extension %s/%s@%s\n", exten, cid, context);
    		} else {
    			ast_cli(a->fd, "Failed to remove extension %s@%s\n", exten, context);
    		}
    
    static int manager_dialplan_extension_remove(struct mansession *s, const struct message *m)
    {
    	const char *context = astman_get_header(m, "Context");
    	const char *extension = astman_get_header(m, "Extension");
    	const char *priority = astman_get_header(m, "Priority");
    
    	int ipriority;
    	char *exten;
    	char *cidmatch = NULL;
    
    	if (ast_strlen_zero(context) || ast_strlen_zero(extension)) {
    		astman_send_error(s, m, "Context and Extension must be provided "
    			"for DialplanExtensionRemove");
    		return 0;
    	}
    
    	exten = ast_strdupa(extension);
    
    	if (strchr(exten, '/')) {
    		cidmatch = exten;
    		strsep(&cidmatch, "/");
    	}
    
    	if (ast_strlen_zero(priority)) {
    		ipriority = 0;
    	} else if (!strcmp("hint", priority)) {
    		ipriority = PRIORITY_HINT;
    	} else if ((sscanf(priority, "%30d", &ipriority) != 1) || ipriority <= 0) {
    		astman_send_error(s, m, "The priority specified was invalid.");
    		return 0;
    	}
    
    	if (!ast_context_remove_extension_callerid(context, exten, ipriority,
    			/* Do not substitute S_OR; it is not the same thing */
    			!ast_strlen_zero(cidmatch) ? cidmatch : (ipriority ? "" : NULL),
    			!ast_strlen_zero(cidmatch) ? 1 : 0, registrar)) {
    		if (ipriority) {
    			astman_send_ack(s, m, "Removed the requested priority from the extension");
    		} else {
    			astman_send_ack(s, m, "Removed the requested extension");
    		}
    	} else {
    		astman_send_error(s, m, "Failed to remove requested extension");
    	}
    
    	return 0;
    }
    
    
    static char *complete_dialplan_remove_extension(struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int which = 0;
    
    
    	if (a->pos == 3) { /* 'dialplan remove extension _X_' (exten@context ... */
    
    		struct ast_context *c = NULL;
    
    		char *context = NULL, *exten = NULL, *cid = NULL;
    
    		int le = 0;	/* length of extension */
    		int lc = 0;	/* length of context */
    
    		int lcid = 0; /* length of cid */
    
    		lc = split_ec(a->word, &exten, &context, &cid);
    		if (lc)	{ /* error */
    
    		le = strlen(exten);
    		lc = strlen(context);
    
    		lcid = cid ? strlen(cid) : -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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 ( !strchr(a->word, '/') ||
    						(!strchr(a->word, '@') && partial_match(ast_get_extension_cidmatch(e), cid, lcid)) ||
    						(strchr(a->word, '@') && !strcmp(ast_get_extension_cidmatch(e), cid))) {
    					if ( ((strchr(a->word, '/') || strchr(a->word, '@')) && !strcmp(ast_get_extension_name(e), exten)) ||
    						 (!strchr(a->word, '/') && !strchr(a->word, '@') && partial_match(ast_get_extension_name(e), exten, le))) { /* n-th match */
    						if (++which > a->n) {
    							/* If there is an extension then return exten@context. */
    							if (ast_get_extension_matchcid(e) && (!strchr(a->word, '@') || strchr(a->word, '/'))) {
    
    								if (ast_asprintf(&ret, "%s/%s@%s", ast_get_extension_name(e), ast_get_extension_cidmatch(e), ast_get_context_name(c)) < 0) {
    
    								break;
    							} else if (!ast_get_extension_matchcid(e) && !strchr(a->word, '/')) {
    
    								if (ast_asprintf(&ret, "%s@%s", ast_get_extension_name(e), ast_get_context_name(c)) < 0) {
    
    			if (e)	/* got a match */
    				break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		ast_unlock_contexts();
    
    	} else if (a->pos == 4) { /* 'dialplan remove extension EXT _X_' (priority) */
    
    		char *exten = NULL, *context, *cid, *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		struct ast_context *c;
    
    		const char *s = skip_words(a->line, 3); /* skip 'dialplan' 'remove' 'extension' */
    
    		int i = split_ec(s, &exten, &context, &cid);	/* 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);
    
    		if (le == 0 || lc == 0)
    			goto error3;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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 (cid && strcmp(ast_get_extension_cidmatch(e), cid) != 0) {
    					continue;
    				}
    
    				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), "%d", ast_get_extension_priority(priority));
    
    					if (partial_match(buffer, a->word, len) && ++which > a->n) /* n-th match */
    
    Mark Spencer's avatar
    Mark Spencer committed
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		ast_unlock_contexts();
    
    /*!
     * Include context ...
     */
    
    static char *handle_cli_dialplan_add_include(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "dialplan add include";
    		e->usage =
    			"Usage: dialplan add include <context> into <context>\n"
    			"       Include a context in another context.\n";
    		return NULL;
    	case CLI_GENERATE:
    		return complete_dialplan_add_include(a);
    	}
    
    	if (a->argc != 6) /* dialplan add include CTX in CTX */
    		return CLI_SHOWUSAGE;
    
    	/* fifth arg must be 'into' ... */
    
    	if (strcmp(a->argv[4], "into"))
    		return CLI_SHOWUSAGE;
    
    	into_context = a->argv[5];
    
    	if (!ast_context_find(into_context)) {
    		ast_cli(a->fd, "Context '%s' did not exist prior to add include - the context will be created.\n", into_context);
    	}
    
    	if (!ast_context_find_or_create(NULL, NULL, into_context, registrar)) {
    		ast_cli(a->fd, "ast_context_find_or_create() failed\n");
    		ast_cli(a->fd, "Failed to include '%s' in '%s' context\n",a->argv[3], a->argv[5]);
    		return CLI_FAILURE;
    	}
    
    
    	if (ast_context_add_include(a->argv[5], a->argv[3], registrar)) {
    
    		switch (errno) {
    		case ENOMEM:
    
    			ast_cli(a->fd, "Out of memory for context addition\n");
    
    			ast_cli(a->fd, "Failed to lock context(s) list, please try again later\n");
    
    			ast_cli(a->fd, "Context '%s' already included in '%s' context\n",
    				a->argv[3], a->argv[5]);
    
    			break;
    
    		case ENOENT:
    		case EINVAL:
    
    			ast_cli(a->fd, "There is no existence of context '%s'\n",
    				errno == ENOENT ? a->argv[5] : a->argv[3]);
    
    			ast_cli(a->fd, "Failed to include '%s' in '%s' context\n",
    				a->argv[3], a->argv[5]);
    
    
    	/* show some info ... */
    
    	ast_cli(a->fd, "Context '%s' included in '%s' context\n",
    		a->argv[3], a->argv[5]);
    
    static char *complete_dialplan_add_include(struct ast_cli_args *a)
    
    {
    	struct ast_context *c;
    	int which = 0;
    	char *ret = NULL;
    
    	if (a->pos == 3) {		/* 'dialplan add include _X_' (context) ... */
    
    			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), a->word, len) && ++which > a->n)
    
    				ret = ast_strdup(ast_get_context_name(c));
    
    		ast_unlock_contexts();
    		return ret;
    
    	} else if (a->pos == 4) { /* dialplan add include CTX _X_ */
    
    		/* always complete  as 'into' */
    
    		return (a->n == 0) ? ast_strdup("into") : NULL;
    
    	} else if (a->pos == 5) { /* 'dialplan add include CTX into _X_' (dst context) */
    
    		char *context, *dupline, *into;
    
    		const char *s = skip_words(a->line, 3); /* should not fail */
    
    		context = dupline = ast_strdup(s);
    
    		if (!dupline) {
    			ast_log(LOG_ERROR, "Out of free memory\n");
    			return NULL;
    		}
    
    		strsep(&dupline, " "); /* skip context */
    		into = strsep(&dupline, " ");
    		/* error if missing context or fifth word is not 'into' */
    		if (!strlen(context) || strcmp(into, "into")) {
    			ast_log(LOG_ERROR, "bad context %s or missing into %s\n",
    				context, into);
    			goto error3;
    		}
    
    
    			ast_log(LOG_ERROR, "Failed to lock context list\n");
    			goto error3;
    		}
    
    
    		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), a->word, len) &&
    					!lookup_ci(c, context) /* not included yet */ &&
    					++which > a->n) {
    
    				ret = ast_strdup(ast_get_context_name(c));
    
    			}
    		}
    		ast_unlock_contexts();
    	error3:
    
    /*!
     * \brief 'save dialplan' CLI command implementation functions ...
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    static char *handle_cli_dialplan_save(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	char filename[256], overrideswitch[256] = "";
    
    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;
    
    	struct ast_flags config_flags = { 0 };
    
    	const char *base, *slash;
    
    	switch (cmd) {
    	case CLI_INIT:
    		e->command = "dialplan save";
    		e->usage =
    			"Usage: dialplan save [/path/to/extension/file]\n"
    			"       Save dialplan created by pbx_config module.\n"
    			"\n"
    			"Example: dialplan save                 (/etc/asterisk/extensions.conf)\n"
    			"         dialplan save /home/markster  (/home/markster/extensions.conf)\n";
    		return NULL;
    	case CLI_GENERATE:
    		return NULL;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (! (static_config && !write_protect_config)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"I can't save dialplan now, see '%s' example file.\n",
    			config);
    
    	if (a->argc != 2 && a->argc != 3)
    		return CLI_SHOWUSAGE;
    
    	if (ast_mutex_lock(&save_dialplan_lock)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Failed to lock dialplan saving (another proccess saving?)\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* XXX the code here is quite loose, a pathname with .conf in it
    	 * is assumed to be a complete pathname
    	 */
    
    	if (a->argc == 3) {	/* have config path. Look for *.conf */
    		base = a->argv[2];
    		if (!strstr(a->argv[2], ".conf")) { /*no, this is assumed to be a pathname */
    
    			/* if filename ends with '/', do not add one */
    
    			slash = (*(a->argv[2] + strlen(a->argv[2]) -1) == '/') ? "/" : "";
    
    		} else {	/* yes, complete file name */
    			slash = "";
    		}
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* no config file, default one */
    
    		base = ast_config_AST_CONFIG_DIR;
    		slash = "/";
    	}
    	snprintf(filename, sizeof(filename), "%s%s%s", base, slash, config);
    
    	cfg = ast_config_load("extensions.conf", config_flags);
    
    	if (!cfg) {
    		ast_cli(a->fd, "Failed to load extensions.conf\n");
    		ast_mutex_unlock(&save_dialplan_lock);
    		return CLI_FAILURE;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* try to lock contexts list */
    
    		ast_cli(a->fd, "Failed to lock contexts list\n");
    
    		ast_mutex_unlock(&save_dialplan_lock);
    
    		ast_config_destroy(cfg);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* create new file ... */
    	if (!(output = fopen(filename, "wt"))) {
    
    		ast_cli(a->fd, "Failed to create file '%s'\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    			filename);
    		ast_unlock_contexts();
    
    		ast_mutex_unlock(&save_dialplan_lock);
    
    		ast_config_destroy(cfg);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* fireout general info */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	if (overrideswitch_config) {
    		snprintf(overrideswitch, sizeof(overrideswitch), "overrideswitch=%s\n", overrideswitch_config);
    	}
    	fprintf(output, "[general]\nstatic=%s\nwriteprotect=%s\nautofallthrough=%s\nclearglobalvars=%s\n%sextenpatternmatchnew=%s\n\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    		static_config ? "yes" : "no",
    
    		write_protect_config ? "yes" : "no",
                    autofallthrough_config ? "yes" : "no",
    
    				clearglobalvars_config ? "yes" : "no",
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    				overrideswitch_config ? overrideswitch : "",
    
    				extenpatternmatchnew_config ? "yes" : "no");
    
    	if ((v = ast_variable_browse(cfg, "globals"))) {
    		fprintf(output, "[globals]\n");
    		while(v) {
    
    			int escaped_len = 2 * strlen(v->value) + 1;
    			char escaped[escaped_len];
    
    			ast_escape_semicolons(v->value, escaped, escaped_len);
    			fprintf(output, "%s => %s\n", v->name, escaped);
    
    	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 *ext, *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_rdlock_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 (ext = NULL; (ext = ast_walk_context_extensions(c, ext)); ) {
    
    			struct ast_exten *p = NULL;
    
    			/* fireout priorities */
    
    			while ( (p = ast_walk_extension_priorities(ext, 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;
    
    				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));
    
    					const char *el = ast_get_extension_label(p);
    
    					char label[128] = "";
    
    					char *appdata = ast_get_extension_app_data(p);
    
    
    					int escaped_len = (!ast_strlen_zero(appdata)) ? 2 * strlen(appdata) + 1 : 1;
    					char escaped[escaped_len];
    
    					if (ast_get_extension_matchcid(p)) {
    						sep = "/";
    						cid = ast_get_extension_cidmatch(p);
    
    					}
    
    					if (el && (snprintf(label, sizeof(label), "(%s)", el) != (strlen(el) + 2))) {
    
    						incomplete = 1;	/* error encountered or label > 125 chars */
    
    					}
    
    					if (!ast_strlen_zero(appdata)) {
    						ast_escape_semicolons(appdata, escaped, escaped_len);
    					} else {
    
    					fprintf(output, "exten => %s%s%s,%d%s,%s(%s)\n",
    
    					    ast_get_extension_name(p), (ast_strlen_zero(sep) ? "" : sep), (ast_strlen_zero(cid) ? "" : cid),
    
    					    ast_get_extension_priority(p), label,
    
    		/* 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");