Skip to content
Snippets Groups Projects
pbx.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * 'show applications' CLI command implementation functions ...
     */
    static int handle_show_applications(int fd, int argc, char *argv[])
    {
    	struct ast_app *a;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int like = 0, describing = 0;
    
    	int total_match = 0; 	/* Number of matches in like clause */
    	int total_apps = 0; 	/* Number of apps registered */
    
    	AST_LIST_LOCK(&apps);
    
    	if (AST_LIST_EMPTY(&apps)) {
    
    		ast_cli(fd, "There are no registered applications\n");
    
    		AST_LIST_UNLOCK(&apps);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    
    	/* show applications like <keyword> */
    	if ((argc == 4) && (!strcmp(argv[2], "like"))) {
    		like = 1;
    	} else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
    		describing = 1;
    	}
    
    	/* show applications describing <keyword1> [<keyword2>] [...] */
    	if ((!like) && (!describing)) {
    		ast_cli(fd, "    -= Registered Asterisk Applications =-\n");
    	} else {
    		ast_cli(fd, "    -= Matching Asterisk Applications =-\n");
    	}
    
    	AST_LIST_TRAVERSE(&apps, a, list) {
    		int printapp = 0;
    
    		total_apps++;
    
    			if (strcasestr(a->name, argv[3])) {
    
    				total_match++;
    
    			}
    		} else if (describing) {
    			if (a->description) {
    				/* Match all words on command line */
    				int i;
    				printapp = 1;
    
    Russell Bryant's avatar
    Russell Bryant committed
    				for (i = 3; i < argc; i++) {
    
    					if (!strcasestr(a->description, argv[i])) {
    
    					} else {
    						total_match++;
    
    					}
    				}
    			}
    		} else {
    			printapp = 1;
    		}
    
    		if (printapp) {
    			ast_cli(fd,"  %20s: %s\n", a->name, a->synopsis ? a->synopsis : "<Synopsis not available>");
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if ((!like) && (!describing)) {
    
    		ast_cli(fd, "    -= %d Applications Registered =-\n",total_apps);
    	} else {
    		ast_cli(fd, "    -= %d Applications Matching =-\n",total_match);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return RESULT_SUCCESS;
    }
    
    
    static char *complete_show_applications(const char *line, const char *word, int pos, int state)
    
    	static char* choices[] = { "like", "describing", NULL };
    
    	return (pos != 2) ? NULL : ast_cli_complete(word, choices, state);
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * 'show dialplan' CLI command implementation functions ...
     */
    
    static char *complete_show_dialplan_context(const char *line, const char *word, int pos,
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int state)
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct ast_context *c = NULL;
    	char *ret = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int which = 0;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int wordlen;
    
    	/* we are do completion of [exten@]context on second position only */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (pos != 2)
    		return NULL;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	wordlen = strlen(word);
    
    
    	/* walk through all contexts and return the n-th match */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ( (c = ast_walk_contexts(c)) ) {
    
    		if (!strncasecmp(word, ast_get_context_name(c), wordlen) && ++which > state) {
    			ret = ast_strdup(ast_get_context_name(c));
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	ast_unlock_contexts();
    
    Russell Bryant's avatar
    Russell Bryant committed
    	return ret;
    
    struct dialplan_counters {
    	int total_context;
    	int total_exten;
    	int total_prio;
    	int context_existence;
    	int extension_existence;
    };
    
    
    /*! \brief helper function to print an extension */
    static void print_ext(struct ast_exten *e, char * buf, int buflen)
    {
    	int prio = ast_get_extension_priority(e);
    	if (prio == PRIORITY_HINT) {
    		snprintf(buf, buflen, "hint: %s",
    			ast_get_extension_app(e));
    	} else {
    		snprintf(buf, buflen, "%d. %s(%s)",
    			prio, ast_get_extension_app(e),
    			(char *)ast_get_extension_app_data(e));
    	}
    }
    
    /* XXX not verified */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    static int show_dialplan_helper(int fd, const char *context, const char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, const char *includes[])
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct ast_context *c = NULL;
    
    Russell Bryant's avatar
    Russell Bryant committed
    	int res = 0, old_total_exten = dpc->total_exten;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* walk all contexts ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ( (c = ast_walk_contexts(c)) ) {
    
    		struct ast_exten *e;
    		struct ast_include *i;
    		struct ast_ignorepat *ip;
    		char buf[256], buf2[256];
    		int context_info_printed = 0;
    
    		if (context && strcmp(ast_get_context_name(c), context))
    			continue;	/* skip this one, name doesn't match */
    
    		dpc->context_existence = 1;
    
    		ast_lock_context(c);
    
    		/* are we looking for exten too? if yes, we print context
    		 * only if we find our extension.
    		 * Otherwise print context even if empty ?
    		 * XXX i am not sure how the rinclude is handled.
    		 * I think it ought to go inside.
    		 */
    		if (!exten) {
    			dpc->total_context++;
    			ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
    				ast_get_context_name(c), ast_get_context_registrar(c));
    			context_info_printed = 1;
    		}
    
    		/* walk extensions ... */
    		e = NULL;
    		while ( (e = ast_walk_context_extensions(c, e)) ) {
    			struct ast_exten *p;
    
    			if (exten && !ast_extension_match(ast_get_extension_name(e), exten))
    				continue;	/* skip, extension match failed */
    
    			dpc->extension_existence = 1;
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			/* may we print context info? */
    
    			if (!context_info_printed) {
    				dpc->total_context++;
    				if (rinclude) { /* TODO Print more info about rinclude */
    					ast_cli(fd, "[ Included context '%s' created by '%s' ]\n",
    						ast_get_context_name(c), ast_get_context_registrar(c));
    				} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
    						ast_get_context_name(c), ast_get_context_registrar(c));
    				}
    
    				context_info_printed = 1;
    			}
    			dpc->total_prio++;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    			/* write extension name and first peer */
    
    			snprintf(buf, sizeof(buf), "'%s' =>", ast_get_extension_name(e));
    
    			print_ext(e, buf2, sizeof(buf2));
    
    			ast_cli(fd, "  %-17s %-45s [%s]\n", buf, buf2,
    				ast_get_extension_registrar(e));
    
    			dpc->total_exten++;
    			/* walk next extension peers */
    			p = e;	/* skip the first one, we already got it */
    			while ( (p = ast_walk_extension_priorities(e, p)) ) {
    				const char *el = ast_get_extension_label(p);
    				dpc->total_prio++;
    				if (el)
    					snprintf(buf, sizeof(buf), "   [%s]", el);
    				else
    					buf[0] = '\0';
    				print_ext(p, buf2, sizeof(buf2));
    
    				ast_cli(fd,"  %-17s %-45s [%s]\n", buf, buf2,
    					ast_get_extension_registrar(p));
    			}
    		}
    
    		/* walk included and write info ... */
    		i = NULL;
    		while ( (i = ast_walk_context_includes(c, i)) ) {
    			snprintf(buf, sizeof(buf), "'%s'", ast_get_include_name(i));
    			if (exten) {
    				/* Check all includes for the requested extension */
    				if (includecount >= AST_PBX_MAX_STACK) {
    					ast_log(LOG_NOTICE, "Maximum include depth exceeded!\n");
    				} else {
    					int dupe=0;
    					int x;
    					for (x=0;x<includecount;x++) {
    						if (!strcasecmp(includes[x], ast_get_include_name(i))) {
    							dupe++;
    							break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					}
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    						includes[includecount] = ast_get_include_name(i);
    
    						show_dialplan_helper(fd, ast_get_include_name(i), exten, dpc, i, includecount + 1, includes);
    					} else {
    						ast_log(LOG_WARNING, "Avoiding circular include of %s within %s\n", ast_get_include_name(i), context);
    
    			} else {
    				ast_cli(fd, "  Include =>        %-45s [%s]\n",
    					buf, ast_get_include_registrar(i));
    			}
    		}
    
    		/* walk ignore patterns and write info ... */
    		ip = NULL;
    		while ( (ip = ast_walk_context_ignorepats(c, ip)) ) {
    			const char *ipname = ast_get_ignorepat_name(ip);
    			char ignorepat[AST_MAX_EXTENSION];
    			snprintf(buf, sizeof(buf), "'%s'", ipname);
    			snprintf(ignorepat, sizeof(ignorepat), "_%s.", ipname);
    			if (!exten || ast_extension_match(ignorepat, exten)) {
    				ast_cli(fd, "  Ignore pattern => %-45s [%s]\n",
    					buf, ast_get_ignorepat_registrar(ip));
    			}
    		}
    		if (!rinclude) {
    			struct ast_sw *sw = NULL;
    			while ( (sw = ast_walk_context_switches(c, sw)) ) {
    				snprintf(buf, sizeof(buf), "'%s/%s'",
    					ast_get_switch_name(sw),
    					ast_get_switch_data(sw));
    				ast_cli(fd, "  Alt. Switch =>    %-45s [%s]\n",
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    					buf, ast_get_switch_registrar(sw));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    
    		ast_unlock_context(c);
    
    		/* if we print something in context, make an empty line */
    		if (context_info_printed)
    			ast_cli(fd, "\r\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_unlock_contexts();
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	return (dpc->total_exten == old_total_exten) ? -1 : res;
    
    }
    
    static int handle_show_dialplan(int fd, int argc, char *argv[])
    {
    	char *exten = NULL, *context = NULL;
    	/* Variables used for different counters */
    	struct dialplan_counters counters;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	const char *incstack[AST_PBX_MAX_STACK];
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (argc != 2 && argc != 3)
    
    
    	/* we obtain [exten@]context? if yes, split them ... */
    	if (argc == 3) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (strchr(argv[2], '@')) {	/* split into exten & context */
    			context = ast_strdupa(argv[2]);
    			exten = strsep(&context, "@");
    			/* change empty strings to NULL */
    
    			if (ast_strlen_zero(exten))
    				exten = NULL;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		} else { /* no '@' char, only context given */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		if (ast_strlen_zero(context))
    			context = NULL;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	/* else Show complete dial plan, context and exten are NULL */
    	show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* check for input failure and throw some error messages */
    
    	if (context && !counters.context_existence) {
    		ast_cli(fd, "There is no existence of '%s' context\n", context);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return RESULT_FAILURE;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (context)
    			ast_cli(fd, "There is no existence of %s@%s extension\n",
    				exten, context);
    		else
    			ast_cli(fd,
    				"There is no existence of '%s' extension in all contexts\n",
    				exten);
    		return RESULT_FAILURE;
    	}
    
    	ast_cli(fd,"-= %d %s (%d %s) in %d %s. =-\n",
    				counters.total_exten, counters.total_exten == 1 ? "extension" : "extensions",
    				counters.total_prio, counters.total_prio == 1 ? "priority" : "priorities",
    				counters.total_context, counters.total_context == 1 ? "context" : "contexts");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* everything ok */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return RESULT_SUCCESS;
    }
    
    
    /*! \brief CLI support for listing global variables in a parseable way */
    static int handle_show_globals(int fd, int argc, char *argv[])
    {
    	int i = 0;
    	struct ast_var_t *newvariable;
    
    
    	AST_LIST_TRAVERSE (&globals, newvariable, entries) {
    		i++;
    		ast_cli(fd, "   %s=%s\n", ast_var_name(newvariable), ast_var_value(newvariable));
    	}
    
    	ast_cli(fd, "\n    -- %d variables\n", i);
    
    	return RESULT_SUCCESS;
    }
    
    /*! \brief  CLI support for setting global variables */
    static int handle_set_global(int fd, int argc, char *argv[])
    {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (argc != 4)
    
    		return RESULT_SHOWUSAGE;
    
    	pbx_builtin_setvar_helper(NULL, argv[2], argv[3]);
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	ast_cli(fd, "\n    -- Global variable %s set to %s\n", argv[2], argv[3]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * CLI entries for upper commands ...
     */
    
    static struct ast_cli_entry pbx_cli[] = {
    	{ { "show", "applications", NULL }, handle_show_applications,
    	  "Shows registered dialplan applications", show_applications_help, complete_show_applications },
    	{ { "show", "functions", NULL }, handle_show_functions,
    	  "Shows registered dialplan functions", show_functions_help },
    	{ { "show" , "function", NULL }, handle_show_function,
    	  "Describe a specific dialplan function", show_function_help, complete_show_function },
    	{ { "show", "application", NULL }, handle_show_application,
    	  "Describe a specific dialplan application", show_application_help, complete_show_application },
    	{ { "show", "dialplan", NULL }, handle_show_dialplan,
    	  "Show dialplan", show_dialplan_help, complete_show_dialplan_context },
    	{ { "show", "switches", NULL },	handle_show_switches,
    	  "Show alternative switches", show_switches_help },
    	{ { "show", "hints", NULL }, handle_show_hints,
    	  "Show dialplan hints", show_hints_help },
    
    	{ { "show", "globals", NULL }, handle_show_globals,
    	  "Show global dialplan variables", show_globals_help },
    	{ { "set", "global", NULL }, handle_set_global,
    	  "Set global dialplan variable", set_global_help },
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    int ast_unregister_application(const char *app)
    
    	struct ast_app *tmp;
    
    	AST_LIST_LOCK(&apps);
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&apps, tmp, list) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(app, tmp->name)) {
    
    			AST_LIST_REMOVE_CURRENT(&apps, list);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (option_verbose > 1)
    				ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
    
    	AST_LIST_TRAVERSE_SAFE_END
    	AST_LIST_UNLOCK(&apps);
    
    	return tmp ? 0 : -1;
    
    struct ast_context *ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_context *tmp, **local_contexts;
    
    	int length;
    	length = sizeof(struct ast_context);
    	length += strlen(name) + 1;
    
    	if (!extcontexts) {
    		local_contexts = &contexts;
    
    	} else
    		local_contexts = extcontexts;
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (tmp = *local_contexts; tmp; tmp = tmp->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(tmp->name, name)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
    
    			if (!extcontexts)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return NULL;
    		}
    	}
    
    		strcpy(tmp->name, name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->root = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->registrar = registrar;
    
    		tmp->next = *local_contexts;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->includes = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->ignorepats = NULL;
    
    		*local_contexts = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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);
    
    	if (!extcontexts)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return tmp;
    }
    
    
    void __ast_context_destroy(struct ast_context *con, const char *registrar);
    
    struct store_hint {
    	char *context;
    	char *exten;
    	struct ast_state_cb *callbacks;
    	int laststate;
    	AST_LIST_ENTRY(store_hint) list;
    	char data[1];
    };
    
    AST_LIST_HEAD(store_hints, store_hint);
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    /* XXX this does not check that multiple contexts are merged */
    
    void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
    {
    
    	struct ast_context *tmp, *lasttmp = NULL;
    
    	struct store_hints store;
    	struct store_hint *this;
    	struct ast_hint *hint;
    	struct ast_exten *exten;
    	int length;
    	struct ast_state_cb *thiscb, *prevcb;
    
    	AST_LIST_HEAD_INIT(&store);
    
    
    	/* it is very important that this function hold the hint list lock _and_ the conlock
    	   during its operation; not only do we need to ensure that the list of contexts
    	   and extensions does not change, but also that no hint callbacks (watchers) are
    	   added or removed during the merge/delete process
    
    	   in addition, the locks _must_ be taken in this order, because there are already
    	   other code paths that use this order
    	*/
    	ast_mutex_lock(&conlock);
    
    
    	/* preserve all watchers for hints associated with this registrar */
    
    		if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
    			length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
    
    				continue;
    			this->callbacks = hint->callbacks;
    			hint->callbacks = NULL;
    			this->laststate = hint->laststate;
    			this->context = this->data;
    			strcpy(this->data, hint->exten->parent->name);
    			this->exten = this->data + strlen(this->context) + 1;
    			strcpy(this->exten, hint->exten->exten);
    			AST_LIST_INSERT_HEAD(&store, this, list);
    		}
    	}
    
    
    	tmp = *extcontexts;
    
    Martin Pycko's avatar
    Martin Pycko committed
    	if (registrar) {
    
    		/* XXX remove previous contexts from same registrar */
    
    		ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
    
    Martin Pycko's avatar
    Martin Pycko committed
    		while (tmp) {
    			lasttmp = tmp;
    			tmp = tmp->next;
    		}
    	} else {
    
    		/* XXX remove contexts with the same name */
    
    Martin Pycko's avatar
    Martin Pycko committed
    		while (tmp) {
    
    			ast_log(LOG_WARNING, "must remove %s  reg %s\n", tmp->name, tmp->registrar);
    
    			__ast_context_destroy(tmp,tmp->registrar);
    
    Martin Pycko's avatar
    Martin Pycko committed
    			lasttmp = tmp;
    			tmp = tmp->next;
    		}
    
    	}
    	if (lasttmp) {
    		lasttmp->next = contexts;
    		contexts = *extcontexts;
    		*extcontexts = NULL;
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	} else
    
    		ast_log(LOG_WARNING, "Requested contexts didn't get merged\n");
    
    
    	/* restore the watchers for hints that can be found; notify those that
    	   cannot be restored
    	*/
    	while ((this = AST_LIST_REMOVE_HEAD(&store, list))) {
    		exten = ast_hint_extension(NULL, this->context, this->exten);
    		/* Find the hint in the list of hints */
    
    			if (hint->exten == exten)
    				break;
    		}
    		if (!exten || !hint) {
    			/* this hint has been removed, notify the watchers */
    			prevcb = NULL;
    			thiscb = this->callbacks;
    			while (thiscb) {
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    				prevcb = thiscb;
    
    				thiscb = thiscb->next;
    				prevcb->callback(this->context, this->exten, AST_EXTENSION_REMOVED, prevcb->data);
    				free(prevcb);
    	    		}
    		} else {
    			thiscb = this->callbacks;
    			while (thiscb->next)
    				thiscb = thiscb->next;
    			thiscb->next = hint->callbacks;
    			hint->callbacks = this->callbacks;
    			hint->laststate = this->laststate;
    		}
    		free(this);
    	}
    
    
    	AST_LIST_UNLOCK(&hints);
    	ast_mutex_unlock(&conlock);
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	return;
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * errno values
     *  EBUSY  - can't lock
    
    Mark Spencer's avatar
    Mark Spencer committed
     *  ENOENT - no existence of context
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    int ast_context_add_include(const char *context, const char *include, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int ret = -1;
    	struct ast_context *c = find_context_locked(context);
    
    	if (c) {
    		ret = ast_context_add_include2(c, include, registrar);
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    /*! \brief Helper for get_range.
     * return the index of the matching entry, starting from 1.
     * If names is not supplied, try numeric values.
     */
    static int lookup_name(const char *s, char *const names[], int max)
    {
    	int i;
    
    	if (names) {
    		for (i = 0; names[i]; i++) {
    			if (!strcasecmp(s, names[i]))
    				return i+1;
    		}
    	} else if (sscanf(s, "%d", &i) == 1 && i >= 1 && i <= max) {
    		return i;
    	}
    	return 0; /* error return */
    }
    
    /*! \brief helper function to return a range up to max (7, 12, 31 respectively).
     * names, if supplied, is an array of names that should be mapped to numbers.
     */
    static unsigned get_range(char *src, int max, char *const names[], const char *msg)
    {
    	int s, e; /* start and ending position */
    	unsigned int mask = 0;
    
    	/* Check for whole range */
    	if (ast_strlen_zero(src) || !strcmp(src, "*")) {
    		s = 0;
    		e = max - 1;
    	} else {
    		/* Get start and ending position */
    		char *c = strchr(src, '-');
    		if (c)
    			*c++ = '\0';
    		/* Find the start */
    		s = lookup_name(src, names, max);
    		if (!s) {
    			ast_log(LOG_WARNING, "Invalid %s '%s', assuming none\n", msg, src);
    			return 0;
    		}
    		s--;
    		if (c) { /* find end of range */
    			e = lookup_name(c, names, max);
    			if (!e) {
    				ast_log(LOG_WARNING, "Invalid end %s '%s', assuming none\n", msg, c);
    				return 0;
    			}
    			e--;
    		} else
    			e = s;
    	}
    	/* Fill the mask. Remember that ranges are cyclic */
    
    	mask = 1 << e;	/* initialize with last element */
    
    	while (s != e) {
    		if (s >= max) {
    			s = 0;
    			mask |= (1 << s);
    		} else {
    			mask |= (1 << s);
    			s++;
    		}
    
    	}
    	return mask;
    }
    
    /*! \brief store a bitmask of valid times, one bit each 2 minute */
    
    static void get_timerange(struct ast_timing *i, char *times)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	char *e;
    	int x;
    	int s1, s2;
    	int e1, e2;
    
    	/*	int cth, ctm; */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    	/* start disabling all times, fill the fields with 0's, as they may contain garbage */
    
    	memset(i->minmask, 0, sizeof(i->minmask));
    
    	/* 2-minutes per bit, since the mask has only 32 bits :( */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Star is all times */
    
    	if (ast_strlen_zero(times) || !strcmp(times, "*")) {
    
    			i->minmask[x] = 0x3fffffff; /* 30 bits */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    	/* Otherwise expect a range */
    	e = strchr(times, '-');
    	if (!e) {
    
    		ast_log(LOG_WARNING, "Time range is not valid. Assuming no restrictions based on time.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    
    	*e++ = '\0';
    	/* XXX why skip non digits ? */
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	while (*e && !isdigit(*e))
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!*e) {
    
    		ast_log(LOG_WARNING, "Invalid time range.  Assuming no restrictions based on time.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    	if (sscanf(times, "%d:%d", &s1, &s2) != 2) {
    
    		ast_log(LOG_WARNING, "%s isn't a time.  Assuming no restrictions based on time.\n", times);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    	if (sscanf(e, "%d:%d", &e1, &e2) != 2) {
    
    		ast_log(LOG_WARNING, "%s isn't a time.  Assuming no restrictions based on time.\n", e);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    
    	/* XXX this needs to be optimized */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	s1 = s1 * 30 + s2/2;
    	if ((s1 < 0) || (s1 >= 24*30)) {
    
    		ast_log(LOG_WARNING, "%s isn't a valid start time. Assuming no time.\n", times);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    	e1 = e1 * 30 + e2/2;
    
    	if ((e1 < 0) || (e1 >= 24*30)) {
    		ast_log(LOG_WARNING, "%s isn't a valid end time. Assuming no time.\n", e);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return;
    	}
    	/* Go through the time and enable each appropriate bit */
    
    	for (x=s1;x != e1;x = (x + 1) % (24 * 30)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		i->minmask[x/30] |= (1 << (x % 30));
    	}
    	/* Do the last one */
    	i->minmask[x/30] |= (1 << (x % 30));
    
    	for (cth=0; cth<24; cth++) {
    
    		/* Initialize masks to blank */
    		i->minmask[cth] = 0;
    
    		for (ctm=0; ctm<30; ctm++) {
    
    			if (
    			/* First hour with more than one hour */
    			      (((cth == s1) && (ctm >= s2)) &&
    			       ((cth < e1)))
    			/* Only one hour */
    			||    (((cth == s1) && (ctm >= s2)) &&
    			       ((cth == e1) && (ctm <= e2)))
    			/* In between first and last hours (more than 2 hours) */
    			||    ((cth > s1) &&
    			       (cth < e1))
    			/* Last hour with more than one hour */
    			||    ((cth > s1) &&
    			       ((cth == e1) && (ctm <= e2)))
    			)
    				i->minmask[cth] |= (1 << (ctm / 2));
    		}
    	}
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* All done */
    
    	return;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static char *days[] =
    {
    	"sun",
    	"mon",
    	"tue",
    	"wed",
    	"thu",
    	"fri",
    	"sat",
    
    Mark Spencer's avatar
    Mark Spencer committed
    };
    
    static char *months[] =
    {
    	"jan",
    	"feb",
    	"mar",
    	"apr",
    	"may",
    	"jun",
    	"jul",
    	"aug",
    	"sep",
    	"oct",
    	"nov",
    	"dec",
    
    int ast_build_timing(struct ast_timing *i, const char *info_in)
    
    	char info_save[256];
    	char *info;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Check for empty just in case */
    
    	if (ast_strlen_zero(info_in))
    		return 0;
    	/* make a copy just in case we were passed a static string */
    
    	ast_copy_string(info_save, info_in, sizeof(info_save));
    
    	info = info_save;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Assume everything except time */
    
    	i->monthmask = 0xfff;	/* 12 bits */
    	i->daymask = 0x7fffffffU; /* 31 bits */
    	i->dowmask = 0x7f; /* 7 bits */
    	/* on each call, use strsep() to move info to the next argument */
    	get_timerange(i, strsep(&info, "|"));
    	if (info)
    		i->dowmask = get_range(strsep(&info, "|"), 7, days, "day of week");
    	if (info)
    		i->daymask = get_range(strsep(&info, "|"), 31, NULL, "day");
    	if (info)
    		i->monthmask = get_range(strsep(&info, "|"), 12, months, "month");
    
    int ast_check_timing(const struct ast_timing *i)
    
    	time_t t = time(NULL);
    
    
    	localtime_r(&t,&tm);
    
    	/* If it's not the right month, return */
    
    	if (!(i->monthmask & (1 << tm.tm_mon)))
    
    		return 0;
    
    	/* If it's not that time of the month.... */
    	/* Warning, tm_mday has range 1..31! */
    	if (!(i->daymask & (1 << (tm.tm_mday-1))))
    		return 0;
    
    	/* If it's not the right day of the week */
    	if (!(i->dowmask & (1 << tm.tm_wday)))
    		return 0;
    
    	/* Sanity check the hour just to be safe */
    	if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
    		ast_log(LOG_WARNING, "Insane time...\n");
    		return 0;
    	}
    
    	/* Now the tough part, we calculate if it fits
    	   in the right time based on min/hour */
    	if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
    		return 0;
    
    	/* If we got this far, then we're good */
    	return 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * errno values
     *  ENOMEM - out of memory
     *  EBUSY  - can't lock
     *  EEXIST - already included
     *  EINVAL - there is no existence of context for inclusion
     */
    
    int ast_context_add_include2(struct ast_context *con, const char *value,
    	const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_include *new_include;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_include *i, *il = NULL; /* include, include_last */
    
    	length = sizeof(struct ast_include);
    	length += 2 * (strlen(value) + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* allocate new include structure ... */
    
    	if (!(new_include = ast_calloc(1, length)))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	/* Fill in this structure. Use 'p' for assignments, as the fields
    	 * in the structure are 'const char *'
    	 */
    
    	p = new_include->stuff;
    	new_include->name = p;
    
    	p += strlen(value) + 1;
    	new_include->rname = p;
    
    	strcpy(p, value);
    	/* Strip off timing info, and process if it is there */
    	if ( (c = strchr(p, '|')) ) {
    		*c++ = '\0';
    	        new_include->hastime = ast_build_timing(&(new_include->timing), c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	new_include->next      = NULL;
    	new_include->registrar = registrar;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* ... go to last include and check if context is already included too... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (i = con->includes; i; i = i->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(i->name, new_include->name)) {
    			free(new_include);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			errno = EEXIST;
    			return -1;
    		}
    		il = i;
    	}
    
    	/* ... include new context into context list, unlock, return */
    	if (il)
    		il->next = new_include;
    	else
    		con->includes = new_include;
    	if (option_verbose > 2)
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con));
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 0;
    }
    
    /*
     * errno values
     *  EBUSY  - can't lock
    
    Mark Spencer's avatar
    Mark Spencer committed
     *  ENOENT - no existence of context
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int ret = -1;
    	struct ast_context *c = find_context_locked(context);
    
    	if (c) { /* found, add switch to this context */
    		ret = ast_context_add_switch2(c, sw, data, eval, registrar);
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    /*
     * errno values
     *  ENOMEM - out of memory
     *  EBUSY  - can't lock
     *  EEXIST - already included
     *  EINVAL - there is no existence of context for inclusion
     */
    
    int ast_context_add_switch2(struct ast_context *con, const char *value,
    
    	const char *data, int eval, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_sw *new_sw;
    
    	length = sizeof(struct ast_sw);
    	length += strlen(value) + 1;
    	if (data)
    		length += strlen(data);
    	length++;
    
    	if (eval) {
    		/* Create buffer for evaluation of variables */
    		length += SWITCH_DATA_LENGTH;
    		length++;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* allocate new sw structure ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* ... fill in this structure ... */
    
    	p = new_sw->stuff;
    	new_sw->name = p;
    	strcpy(new_sw->name, value);
    	p += strlen(value) + 1;
    	new_sw->data = p;
    
    		strcpy(new_sw->data, data);
    
    		strcpy(new_sw->data, "");
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    	if (eval)
    
    		new_sw->tmpdata = p;
    	new_sw->eval	  = eval;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	new_sw->registrar = registrar;
    
    	/* ... try to lock this context ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* ... go to last sw and check if context is already swd too... */
    
    	AST_LIST_TRAVERSE(&con->alts, i, list) {
    
    		if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			free(new_sw);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			errno = EEXIST;
    			return -1;
    		}
    	}
    
    	/* ... sw new context into context list, unlock, return */
    
    	AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_verbose > 2)
    
    		ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con));
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 0;
    }
    
    /*
     * EBUSY  - can't lock
    
    Mark Spencer's avatar
    Mark Spencer committed
     * ENOENT - there is not context existence
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	int ret = -1;
    	struct ast_context *c = find_context_locked(context);
    
    	if (c) {
    		ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
    		ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_ignorepat *ip, *ipl = NULL;