Skip to content
Snippets Groups Projects
pbx.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock application list\n");
    		return -1;
    	}
    
    	/* ... have we got at least one application (first)? no? */
    
    	if (!apps) {
    		ast_cli(fd, "There are no registered applications\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    
    	/* show applications like <keyword> */
    	if ((argc == 4) && (!strcmp(argv[2], "like"))) {
    		like = 1;
    	} else if ((argc > 3) && (!strcmp(argv[2], "describing"))) {
    		describing = 1;
    	}
    
    	/* show applications describing <keyword1> [<keyword2>] [...] */
    	if ((!like) && (!describing)) {
    		ast_cli(fd, "    -= Registered Asterisk Applications =-\n");
    	} else {
    		ast_cli(fd, "    -= Matching Asterisk Applications =-\n");
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* ... go through all applications ... */
    
    	for (a = apps; a; a = a->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* ... show informations about applications ... */
    
    		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
    	/* ... unlock and return */
    
    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)
    
    	if (pos == 2) {
    		if (ast_strlen_zero(word)) {
    			switch (state) {
    			case 0:
    				return strdup("like");
    			case 1:
    				return strdup("describing");
    			default:
    				return NULL;
    			}
    
    		} else if (! strncasecmp(word, "like", wordlen)) {
    
    			if (state == 0) {
    				return strdup("like");
    			} else {
    				return NULL;
    			}
    
    		} else if (! strncasecmp(word, "describing", wordlen)) {
    
    			if (state == 0) {
    				return strdup("describing");
    			} else {
    				return NULL;
    			}
    		}
    	}
    	return NULL;
    }
    
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* try to lock contexts list ... */
    	if (ast_lock_contexts()) {
    		ast_log(LOG_ERROR, "Unable to lock context list\n");
    		return NULL;
    	}
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    	wordlen = strlen(word);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* ... walk through all contexts ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ( (c = ast_walk_contexts(c)) ) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* ... word matches context name? yes? ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    		if (!strncasecmp(word, ast_get_context_name(c), wordlen)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* ... for serve? ... */
    			if (++which > state) {
    				/* ... yes, serve this context name ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    				ret = strdup(ast_get_context_name(c));
    				break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* ... unlock and return */
    	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;
    };
    
    
    static int show_dialplan_helper(int fd, char *context, char *exten, struct dialplan_counters *dpc, struct ast_include *rinclude, int includecount, 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
    
    	/* try to lock contexts */
    	if (ast_lock_contexts()) {
    
    		ast_log(LOG_WARNING, "Failed to lock contexts list\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* walk all contexts ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ( (c = ast_walk_contexts(c)) ) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* show this context? */
    		if (!context ||
    			!strcmp(ast_get_context_name(c), context)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    			/* try to lock context before walking in ... */
    			if (!ast_lock_context(c)) {
    				struct ast_exten *e;
    				struct ast_include *i;
    				struct ast_ignorepat *ip;
    				struct ast_sw *sw;
    				char buf[256], buf2[256];
    				int context_info_printed = 0;
    
    				/* are we looking for exten too? if yes, we print context
    				 * if we our extension only
    				 */
    				if (!exten) {
    
    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;
    				}
    
    				/* walk extensions ... */
    
    				for (e = ast_walk_context_extensions(c, NULL); e; e = ast_walk_context_extensions(c, e)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					struct ast_exten *p;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    					/* looking for extension? is this our extension? */
    					if (exten &&
    
    						!ast_extension_match(ast_get_extension_name(e), exten))
    
    Mark Spencer's avatar
    Mark Spencer committed
    					{
    						/* we are looking for extension and it's not our
     						 * extension, so skip to next extension */
    						continue;
    					}
    
    
    Mark Spencer's avatar
    Mark Spencer 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 {
    							ast_cli(fd, "[ Context '%s' created by '%s' ]\n",
    								ast_get_context_name(c),
    								ast_get_context_registrar(c));
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    						context_info_printed = 1;
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    					/* write extension name and first peer */	
    					bzero(buf, sizeof(buf));		
    					snprintf(buf, sizeof(buf), "'%s' =>",
    						ast_get_extension_name(e));
    
    
    					prio = ast_get_extension_priority(e);
    					if (prio == PRIORITY_HINT) {
    						snprintf(buf2, sizeof(buf2),
    							"hint: %s",
    							ast_get_extension_app(e));
    					} else {
    						snprintf(buf2, sizeof(buf2),
    							"%d. %s(%s)",
    							prio,
    							ast_get_extension_app(e),
    							(char *)ast_get_extension_app_data(e));
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    					ast_cli(fd, "  %-17s %-45s [%s]\n", buf, buf2,
    						ast_get_extension_registrar(e));
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    					/* walk next extension peers */
    
    					for (p=ast_walk_extension_priorities(e, e); p; p=ast_walk_extension_priorities(e, p)) {
    						dpc->total_prio++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						bzero((void *)buf2, sizeof(buf2));
    
    						bzero((void *)buf, sizeof(buf));
    						if (ast_get_extension_label(p))
    							snprintf(buf, sizeof(buf), "   [%s]", ast_get_extension_label(p));
    
    						prio = ast_get_extension_priority(p);
    						if (prio == PRIORITY_HINT) {
    							snprintf(buf2, sizeof(buf2),
    								"hint: %s",
    								ast_get_extension_app(p));
    						} else {
    							snprintf(buf2, sizeof(buf2),
    								"%d. %s(%s)",
    								prio,
    								ast_get_extension_app(p),
    								(char *)ast_get_extension_app_data(p));
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    						ast_cli(fd,"  %-17s %-45s [%s]\n",
    
    				/* walk included and write info ... */
    				for (i = ast_walk_context_includes(c, NULL); i; i = ast_walk_context_includes(c, i)) {
    					bzero(buf, sizeof(buf));
    					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;
    								}
    							}
    							if (!dupe) {
    								includes[includecount] = (char *)ast_get_include_name(i);
    								show_dialplan_helper(fd, (char *)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);
    							}
    						}
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_cli(fd, "  Include =>        %-45s [%s]\n",
    							buf, ast_get_include_registrar(i));
    					}
    
    Russell Bryant's avatar
    Russell Bryant committed
    				for (ip = ast_walk_context_ignorepats(c, NULL); ip; 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)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_cli(fd, "  Ignore pattern => %-45s [%s]\n",
    
    Mark Spencer's avatar
    Mark Spencer committed
    					}
    
    				}
    				if (!rinclude) {
    					for (sw = ast_walk_context_switches(c, NULL); sw; sw = ast_walk_context_switches(c, sw)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    						snprintf(buf, sizeof(buf), "'%s/%s'",
    							ast_get_switch_name(sw),
    							ast_get_switch_data(sw));
    						ast_cli(fd, "  Alt. Switch =>    %-45s [%s]\n",
    							buf, ast_get_switch_registrar(sw));	
    					}
    				}
    	
    				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
    	ast_unlock_contexts();
    
    
    	if (dpc->total_exten == old_total_exten) {
    		/* Nothing new under the sun */
    		return -1;
    	} else {
    		return 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;
    
    	char *incstack[AST_PBX_MAX_STACK];
    
    	if (argc != 2 && argc != 3) 
    		return RESULT_SHOWUSAGE;
    
    
    	/* we obtain [exten@]context? if yes, split them ... */
    	if (argc == 3) {
    		char *splitter = ast_strdupa(argv[2]);
    		/* is there a '@' character? */
    
    		if (splitter && strchr(argv[2], '@')) {
    
    			/* yes, split into exten & context ... */
    			exten   = strsep(&splitter, "@");
    			context = splitter;
    
    			/* check for length and change to NULL if ast_strlen_zero() */
    
    			if (ast_strlen_zero(exten))
    				exten = NULL;
    			if (ast_strlen_zero(context))
    				context = NULL;
    
    			show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
    
    		} else {
    			/* no '@' char, only context given */
    			context = argv[2];
    
    			if (ast_strlen_zero(context))
    				context = NULL;
    
    			show_dialplan_helper(fd, context, exten, &counters, NULL, 0, incstack);
    
    		show_dialplan_helper(fd, NULL, NULL, &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));
    	}
    	/* ... we have applications ... */
    	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[])
    {
    	if (argc != 4) 
    		return RESULT_SHOWUSAGE;
    
    	pbx_builtin_setvar_helper(NULL, argv[2], argv[3]);
    	ast_cli(fd, "\n    -- Global variables %s set to %s\n", argv[2], argv[3]);
    
    	return RESULT_SUCCESS;
    }
    
    
    
    
    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 },
    
    int ast_unregister_application(const char *app) 
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_app *tmp, *tmpl = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_ERROR, "Unable to lock application list\n");
    		return -1;
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    	for (tmp = apps; tmp; tmp = tmp->next) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strcasecmp(app, tmp->name)) {
    			if (tmpl)
    				tmpl->next = tmp->next;
    			else
    				apps = tmp->next;
    			if (option_verbose > 1)
    				ast_verbose( VERBOSE_PREFIX_2 "Unregistered application '%s'\n", tmp->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		tmpl = tmp;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -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;
    		}
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    	tmp = calloc(1, length);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (tmp) {
    
    		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);
    	} else
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    	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);
    
    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;
    
    	/* preserve all watchers for hints associated with this registrar */
    	AST_LIST_HEAD_INIT(&store);
    
    	AST_LIST_LOCK(&hints);
    	AST_LIST_TRAVERSE(&hints, hint, list) {
    
    		if (hint->callbacks && !strcmp(registrar, hint->exten->parent->registrar)) {
    			length = strlen(hint->exten->exten) + strlen(hint->exten->parent->name) + 2 + sizeof(*this);
    			this = calloc(1, length);
    			if (!this) {
    				ast_log(LOG_WARNING, "Could not allocate memory to preserve hint\n");
    				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) {
    
    Martin Pycko's avatar
    Martin Pycko committed
    		while (tmp) {
    			lasttmp = tmp;
    			tmp = tmp->next;
    		}
    	} else {
    		while (tmp) {
    
    			__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;
    	} 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 */
    
    		AST_LIST_LOCK(&hints);
    		AST_LIST_TRAVERSE(&hints, hint, list) {
    
    			if (hint->exten == exten)
    				break;
    		}
    		if (!exten || !hint) {
    			/* this hint has been removed, notify the watchers */
    			prevcb = NULL;
    			thiscb = this->callbacks;
    			while (thiscb) {
    				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;
    		}
    
    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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct ast_context *c = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (ast_lock_contexts()) {
    		errno = EBUSY;
    		return -1;
    	}
    
    	/* walk contexts ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ( (c = ast_walk_contexts(c)) ) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* ... search for the right one ... */
    		if (!strcmp(ast_get_context_name(c), context)) {
    			int ret = ast_context_add_include2(c, include, registrar);
    			/* ... unlock contexts list and return */
    			ast_unlock_contexts();
    			return ret;
    		}
    	}
    
    	/* we can't find the right context */
    	ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	errno = ENOENT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    /*! \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 */
    	for ( ; s != e; s++) {
    
    		if (s == max)
    			s = 0 ;
    		mask |= (1 << 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));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    
    	/* 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 ? */
    
    Russell Bryant's avatar
    Russell Bryant 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, 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");
    
    	return 1;
    }
    
    int ast_check_timing(struct ast_timing *i)
    {
    	struct tm tm;
    
    	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 */
    
    	int length;
    	char *p;
    	
    	length = sizeof(struct ast_include);
    	length += 2 * (strlen(value) + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* allocate new include structure ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	if (!(new_include = calloc(1, length))) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		errno = ENOMEM;
    		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;
    
    	/* ... try to lock this context ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		free(new_include);
    		errno = EBUSY;
    		return -1;
    	}
    
    	/* ... 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)
    		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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct ast_context *c = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (ast_lock_contexts()) {
    		errno = EBUSY;
    		return -1;
    	}
    
    	/* walk contexts ... */
    
    Russell Bryant's avatar
    Russell Bryant committed
    	while ( (c = ast_walk_contexts(c)) ) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* ... search for the right one ... */
    		if (!strcmp(ast_get_context_name(c), context)) {
    
    			int ret = ast_context_add_switch2(c, sw, data, eval, registrar);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* ... unlock contexts list and return */
    			ast_unlock_contexts();
    			return ret;
    		}
    	}
    
    	/* we can't find the right context */
    	ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	errno = ENOENT;