Skip to content
Snippets Groups Projects
extconf.c 172 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		loc_contexts = &contexts;
    
    		loc_contexts = extcontexts;
    
    	for (tmp = *loc_contexts; tmp; tmp = tmp->next) {
    
    		if (!strcasecmp(tmp->name, name)) {
    			if (!existsokay) {
    				ast_log(LOG_WARNING, "Tried to register context '%s', already in use\n", name);
    				tmp = NULL;
    			}
    			if (!extcontexts)
    				ast_unlock_contexts();
    			return tmp;
    		}
    	}
    	if ((tmp = ast_calloc(1, length))) {
    		ast_rwlock_init(&tmp->lock);
    		ast_mutex_init(&tmp->macrolock);
    		strcpy(tmp->name, name);
    		tmp->root = NULL;
    		tmp->registrar = registrar;
    
    		tmp->next = *loc_contexts;
    
    		tmp->includes = NULL;
    		tmp->ignorepats = NULL;
    
    		*loc_contexts = tmp;
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Registered context '%s'\n", tmp->name);
    
    		if (option_verbose > 2)
    			ast_verbose( VERBOSE_PREFIX_3 "Registered extension context '%s'\n", tmp->name);
    	}
    
    	if (!extcontexts)
    		ast_unlock_contexts();
    	return tmp;
    }
    
    /*! \brief
     * Main interface to add extensions to the list for out context.
     *
     * We sort extensions in order of matching preference, so that we can
     * stop the search as soon as we find a suitable match.
     * This ordering also takes care of wildcards such as '.' (meaning
     * "one or more of any character") and '!' (which is 'earlymatch',
     * meaning "zero or more of any character" but also impacts the
     * return value from CANMATCH and EARLYMATCH.
     *
     * The extension match rules defined in the devmeeting 2006.05.05 are
     * quite simple: WE SELECT THE LONGEST MATCH.
     * In detail, "longest" means the number of matched characters in
     * the extension. In case of ties (e.g. _XXX and 333) in the length
     * of a pattern, we give priority to entries with the smallest cardinality
     * (e.g, [5-9] comes before [2-8] before the former has only 5 elements,
     * while the latter has 7, etc.
     * In case of same cardinality, the first element in the range counts.
     * If we still have a tie, any final '!' will make this as a possibly
     * less specific pattern.
     *
     * EBUSY - can't lock
     * EEXIST - extension with the same priority exist and no replace is set
     *
     */
    static int ast_add_extension2(struct ast_context *con,
    	int replace, const char *extension, int priority, const char *label, const char *callerid,
    	const char *application, void *data, void (*datad)(void *),
    	const char *registrar)
    {
    	/*
    	 * Sort extensions (or patterns) according to the rules indicated above.
    	 * These are implemented by the function ext_cmp()).
    	 * All priorities for the same ext/pattern/cid are kept in a list,
    	 * using the 'peer' field  as a link field..
    	 */
    	struct ast_exten *tmp, *e, *el = NULL;
    	int res;
    	int length;
    	char *p;
    
    	/* if we are adding a hint, and there are global variables, and the hint
    	   contains variable references, then expand them --- NOT In this situation!!!
    	*/
    
    	length = sizeof(struct ast_exten);
    	length += strlen(extension) + 1;
    	length += strlen(application) + 1;
    	if (label)
    		length += strlen(label) + 1;
    	if (callerid)
    		length += strlen(callerid) + 1;
    	else
    		length ++;	/* just the '\0' */
    
    	/* Be optimistic:  Build the extension structure first */
    	if (datad == NULL)
    		datad = null_datad;
    	if (!(tmp = ast_calloc(1, length)))
    		return -1;
    
    	/* use p as dst in assignments, as the fields are const char * */
    	p = tmp->stuff;
    	if (label) {
    		tmp->label = p;
    		strcpy(p, label);
    		p += strlen(label) + 1;
    	}
    	tmp->exten = p;
    	p += ext_strncpy(p, extension, strlen(extension) + 1) + 1;
    	tmp->priority = priority;
    	tmp->cidmatch = p;	/* but use p for assignments below */
    	if (callerid) {
    		p += ext_strncpy(p, callerid, strlen(callerid) + 1) + 1;
    		tmp->matchcid = 1;
    	} else {
    		*p++ = '\0';
    		tmp->matchcid = 0;
    	}
    	tmp->app = p;
    	strcpy(p, application);
    	tmp->parent = con;
    	tmp->data = data;
    	tmp->datad = datad;
    	tmp->registrar = registrar;
    
    	res = 0; /* some compilers will think it is uninitialized otherwise */
    	for (e = con->root; e; el = e, e = e->next) {   /* scan the extension list */
    		res = ext_cmp(e->exten, extension);
    		if (res == 0) { /* extension match, now look at cidmatch */
    			if (!e->matchcid && !tmp->matchcid)
    				res = 0;
    			else if (tmp->matchcid && !e->matchcid)
    				res = 1;
    			else if (e->matchcid && !tmp->matchcid)
    				res = -1;
    			else
    				res = strcasecmp(e->cidmatch, tmp->cidmatch);
    		}
    		if (res >= 0)
    			break;
    	}
    	if (e && res == 0) { /* exact match, insert in the pri chain */
    		res = add_pri(con, tmp, el, e, replace);
    		if (res < 0) {
    			errno = EEXIST;	/* XXX do we care ? */
    			return 0; /* XXX should we return -1 maybe ? */
    		}
    	} else {
    		/*
    		 * not an exact match, this is the first entry with this pattern,
    		 * so insert in the main list right before 'e' (if any)
    		 */
    		tmp->next = e;
    		if (el)
    			el->next = tmp;
    		else
    			con->root = tmp;
    		if (tmp->priority == PRIORITY_HINT)
    			ast_add_hint(tmp);
    	}
    	if (option_debug) {
    		if (tmp->matchcid) {
    
    			ast_log(LOG_DEBUG, "Added extension '%s' priority %d (CID match '%s') to %s\n",
    
    				tmp->exten, tmp->priority, tmp->cidmatch, con->name);
    		} else {
    
    			ast_log(LOG_DEBUG, "Added extension '%s' priority %d to %s\n",
    
    				tmp->exten, tmp->priority, con->name);
    		}
    	}
    	if (option_verbose > 2) {
    		if (tmp->matchcid) {
    			ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d (CID match '%s')to %s\n",
    				tmp->exten, tmp->priority, tmp->cidmatch, con->name);
    		} else {
    			ast_verbose( VERBOSE_PREFIX_3 "Added extension '%s' priority %d to %s\n",
    				tmp->exten, tmp->priority, con->name);
    		}
    	}
    	return 0;
    }
    
    int localized_add_extension2(struct ast_context *con,
    							 int replace, const char *extension, int priority, const char *label, const char *callerid,
    							 const char *application, void *data, void (*datad)(void *),
    							 const char *registrar);
    
    int localized_add_extension2(struct ast_context *con,
    							 int replace, const char *extension, int priority, const char *label, const char *callerid,
    							 const char *application, void *data, void (*datad)(void *),
    							 const char *registrar)
    {
    	return ast_add_extension2(con, replace, extension, priority, label, callerid, application, data, datad, registrar);
    }
    
    
    
    /*! \brief The return value depends on the action:
     *
     * E_MATCH, E_CANMATCH, E_MATCHMORE require a real match,
     *	and return 0 on failure, -1 on match;
     * E_FINDLABEL maps the label to a priority, and returns
     *	the priority on success, ... XXX
     * E_SPAWN, spawn an application,
     *	and return 0 on success, -1 on failure.
     */
    static int pbx_extension_helper(struct ast_channel *c, struct ast_context *con,
    								const char *context, const char *exten, int priority,
    								const char *label, const char *callerid, enum ext_match_t action)
    {
    	struct ast_exten *e;
    	int res;
    	struct pbx_find_info q = { .stacklen = 0 }; /* the rest is reset in pbx_find_extension */
    
    	int matching_action = (action == E_MATCH || action == E_CANMATCH || action == E_MATCHMORE);
    
    	e = pbx_find_extension(NULL, con, &q, context, exten, priority, label, callerid, action);
    	if (e) {
    		if (matching_action) {
    			return -1;	/* success, we found it */
    		} else if (action == E_FINDLABEL) { /* map the label to a priority */
    			res = e->priority;
    			return res;	/* the priority we were looking for */
    		} else {	/* spawn */
    
    			/* NOT!!!!! */
    			return 0;
    		}
    	} else if (q.swo) {	/* not found here, but in another switch */
    		if (matching_action)
    			return -1;
    		else {
    			if (!q.swo->exec) {
    				ast_log(LOG_WARNING, "No execution engine for switch %s\n", q.swo->name);
    				res = -1;
    			}
    			return q.swo->exec(c, q.foundcontext ? q.foundcontext : context, exten, priority, callerid, q.data);
    		}
    	} else {	/* not found anywhere, see what happened */
    		switch (q.status) {
    		case STATUS_NO_CONTEXT:
    			if (!matching_action)
    				ast_log(LOG_NOTICE, "Cannot find extension context '%s'\n", context);
    			break;
    		case STATUS_NO_EXTENSION:
    			if (!matching_action)
    				ast_log(LOG_NOTICE, "Cannot find extension '%s' in context '%s'\n", exten, context);
    			break;
    		case STATUS_NO_PRIORITY:
    			if (!matching_action)
    				ast_log(LOG_NOTICE, "No such priority %d in extension '%s' in context '%s'\n", priority, exten, context);
    			break;
    		case STATUS_NO_LABEL:
    			if (context)
    				ast_log(LOG_NOTICE, "No such label '%s' in extension '%s' in context '%s'\n", label, exten, context);
    			break;
    		default:
    
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Shouldn't happen!\n");
    
    		}
    
    		return (matching_action) ? 0 : -1;
    	}
    }
    
    static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid);
    
    static int ast_findlabel_extension2(struct ast_channel *c, struct ast_context *con, const char *exten, const char *label, const char *callerid)
    {
    	return pbx_extension_helper(c, con, NULL, exten, 0, label, callerid, E_FINDLABEL);
    }
    
    
    static struct ast_context *ast_context_find_or_create(struct ast_context **extcontexts, void *tab, const char *name, const char *registrar)
    
    {
    	return __ast_context_create(extcontexts, name, registrar, 1);
    }
    
    
    struct ast_context *localized_context_find_or_create(struct ast_context **extcontexts, void *tab, const char *name, const char *registrar);
    struct ast_context *localized_context_find_or_create(struct ast_context **extcontexts, void *tab, const char *name, const char *registrar)
    
    	return __ast_context_create(extcontexts, name, registrar, 1);
    
    }
    
    
    /* chopped this one off at the knees */
    static int ast_func_read(struct ast_channel *chan, const char *function, char *workspace, size_t len)
    {
    	ast_log(LOG_ERROR, "Function %s not registered\n", function);
    	return -1;
    }
    
    /*! \brief extract offset:length from variable name.
     * Returns 1 if there is a offset:length part, which is
     * trimmed off (values go into variables)
     */
    static int parse_variable_name(char *var, int *offset, int *length, int *isfunc)
    {
    	int parens=0;
    
    	*offset = 0;
    	*length = INT_MAX;
    	*isfunc = 0;
    	for (; *var; var++) {
    		if (*var == '(') {
    			(*isfunc)++;
    			parens++;
    		} else if (*var == ')') {
    			parens--;
    		} else if (*var == ':' && parens == 0) {
    			*var++ = '\0';
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    			sscanf(var, "%30d:%30d", offset, length);
    
    			return 1; /* offset:length valid */
    		}
    	}
    	return 0;
    }
    
    static const char *ast_var_value(const struct ast_var_t *var)
    {
    	return (var ? var->value : NULL);
    }
    
    /*! \brief takes a substring. It is ok to call with value == workspace.
     *
     * offset < 0 means start from the end of the string and set the beginning
     *   to be that many characters back.
     * length is the length of the substring.  A value less than 0 means to leave
     * that many off the end.
     * Always return a copy in workspace.
     */
    static char *substring(const char *value, int offset, int length, char *workspace, size_t workspace_len)
    {
    	char *ret = workspace;
    	int lr;	/* length of the input string after the copy */
    
    	ast_copy_string(workspace, value, workspace_len); /* always make a copy */
    
    	lr = strlen(ret); /* compute length after copy, so we never go out of the workspace */
    
    	/* Quick check if no need to do anything */
    	if (offset == 0 && length >= lr)	/* take the whole string */
    		return ret;
    
    	if (offset < 0)	{	/* translate negative offset into positive ones */
    		offset = lr + offset;
    		if (offset < 0) /* If the negative offset was greater than the length of the string, just start at the beginning */
    			offset = 0;
    	}
    
    	/* too large offset result in empty string so we know what to return */
    	if (offset >= lr)
    		return ret + lr;	/* the final '\0' */
    
    	ret += offset;		/* move to the start position */
    	if (length >= 0 && length < lr - offset)	/* truncate if necessary */
    		ret[length] = '\0';
    	else if (length < 0) {
    		if (lr > offset - length) /* After we remove from the front and from the rear, is there anything left? */
    			ret[lr + length - offset] = '\0';
    		else
    			ret[0] = '\0';
    	}
    
    	return ret;
    }
    
    /*! \brief  Support for Asterisk built-in variables in the dialplan
    \note	See also
    	- \ref AstVar	Channel variables
    	- \ref AstCauses The HANGUPCAUSE variable
     */
    static void pbx_retrieve_variable(struct ast_channel *c, const char *var, char **ret, char *workspace, int workspacelen, struct varshead *headp)
    {
    	const char not_found = '\0';
    	char *tmpvar;
    	const char *s;	/* the result */
    	int offset, length;
    	int i, need_substring;
    	struct varshead *places[2] = { headp, &globals };	/* list of places where we may look */
    
    	/*
    	 * Make a copy of var because parse_variable_name() modifies the string.
    	 * Then if called directly, we might need to run substring() on the result;
    	 * remember this for later in 'need_substring', 'offset' and 'length'
    	 */
    	tmpvar = ast_strdupa(var);	/* parse_variable_name modifies the string */
    	need_substring = parse_variable_name(tmpvar, &offset, &length, &i /* ignored */);
    
    	/*
    	 * Look first into predefined variables, then into variable lists.
    	 * Variable 's' points to the result, according to the following rules:
    	 * s == &not_found (set at the beginning) means that we did not find a
    	 *	matching variable and need to look into more places.
    	 * If s != &not_found, s is a valid result string as follows:
    	 * s = NULL if the variable does not have a value;
    	 *	you typically do this when looking for an unset predefined variable.
    	 * s = workspace if the result has been assembled there;
    	 *	typically done when the result is built e.g. with an snprintf(),
    	 *	so we don't need to do an additional copy.
    	 * s != workspace in case we have a string, that needs to be copied
    	 *	(the ast_copy_string is done once for all at the end).
    	 *	Typically done when the result is already available in some string.
    	 */
    	s = &not_found;	/* default value */
    	if (s == &not_found) { /* look for more */
    		if (!strcmp(var, "EPOCH")) {
    			snprintf(workspace, workspacelen, "%u",(int)time(NULL));
    		}
    
    		s = workspace;
    	}
    	/* if not found, look into chanvars or global vars */
    	for (i = 0; s == &not_found && i < (sizeof(places) / sizeof(places[0])); i++) {
    		struct ast_var_t *variables;
    		if (!places[i])
    			continue;
    		if (places[i] == &globals)
    			ast_rwlock_rdlock(&globalslock);
    		AST_LIST_TRAVERSE(places[i], variables, entries) {
    			if (strcasecmp(ast_var_name(variables), var)==0) {
    				s = ast_var_value(variables);
    				break;
    			}
    		}
    		if (places[i] == &globals)
    			ast_rwlock_unlock(&globalslock);
    	}
    	if (s == &not_found || s == NULL)
    		*ret = NULL;
    	else {
    		if (s != workspace)
    			ast_copy_string(workspace, s, workspacelen);
    		*ret = workspace;
    		if (need_substring)
    			*ret = substring(*ret, offset, length, workspace, workspacelen);
    	}
    }
    
    static void pbx_substitute_variables_helper_full(struct ast_channel *c, struct varshead *headp, const char *cp1, char *cp2, int count)
    {
    	/* Substitutes variables into cp2, based on string cp1, and assuming cp2 to be
    	   zero-filled */
    	char *cp4;
    	const char *tmp, *whereweare;
    	int length, offset, offset2, isfunction;
    	char *workspace = NULL;
    	char *ltmp = NULL, *var = NULL;
    	char *nextvar, *nextexp, *nextthing;
    	char *vars, *vare;
    	int pos, brackets, needsub, len;
    
    
    	*cp2 = 0; /* just in case there's nothing to do */
    
    	whereweare=tmp=cp1;
    	while (!ast_strlen_zero(whereweare) && count) {
    		/* Assume we're copying the whole remaining string */
    		pos = strlen(whereweare);
    		nextvar = NULL;
    		nextexp = NULL;
    		nextthing = strchr(whereweare, '$');
    		if (nextthing) {
    			switch (nextthing[1]) {
    			case '{':
    				nextvar = nextthing;
    				pos = nextvar - whereweare;
    				break;
    			case '[':
    				nextexp = nextthing;
    				pos = nextexp - whereweare;
    				break;
    			}
    		}
    
    		if (pos) {
    			/* Can't copy more than 'count' bytes */
    			if (pos > count)
    				pos = count;
    
    			/* Copy that many bytes */
    			memcpy(cp2, whereweare, pos);
    
    			count -= pos;
    			cp2 += pos;
    			whereweare += pos;
    
    		}
    
    		if (nextvar) {
    			/* We have a variable.  Find the start and end, and determine
    			   if we are going to have to recursively call ourselves on the
    			   contents */
    			vars = vare = nextvar + 2;
    			brackets = 1;
    			needsub = 0;
    
    			/* Find the end of it */
    			while (brackets && *vare) {
    				if ((vare[0] == '$') && (vare[1] == '{')) {
    					needsub++;
    				} else if (vare[0] == '{') {
    					brackets++;
    				} else if (vare[0] == '}') {
    					brackets--;
    				} else if ((vare[0] == '$') && (vare[1] == '['))
    					needsub++;
    				vare++;
    			}
    			if (brackets)
    				ast_log(LOG_NOTICE, "Error in extension logic (missing '}' in '%s')\n", cp1);
    			len = vare - vars - 1;
    
    			/* Skip totally over variable string */
    			whereweare += (len + 3);
    
    			if (!var)
    
    				var = alloca(VAR_BUF_SIZE);
    
    
    			/* Store variable name (and truncate) */
    			ast_copy_string(var, vars, len + 1);
    
    			/* Substitute if necessary */
    			if (needsub) {
    				if (!ltmp)
    
    					ltmp = alloca(VAR_BUF_SIZE);
    
    
    				memset(ltmp, 0, VAR_BUF_SIZE);
    				pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
    				vars = ltmp;
    			} else {
    				vars = var;
    			}
    
    			if (!workspace)
    
    				workspace = alloca(VAR_BUF_SIZE);
    
    
    			workspace[0] = '\0';
    
    			parse_variable_name(vars, &offset, &offset2, &isfunction);
    			if (isfunction) {
    				/* Evaluate function */
    				cp4 = ast_func_read(c, vars, workspace, VAR_BUF_SIZE) ? NULL : workspace;
    
    				if (option_debug)
    					ast_log(LOG_DEBUG, "Function result is '%s'\n", cp4 ? cp4 : "(null)");
    
    			} else {
    				/* Retrieve variable value */
    				pbx_retrieve_variable(c, vars, &cp4, workspace, VAR_BUF_SIZE, headp);
    			}
    			if (cp4) {
    				cp4 = substring(cp4, offset, offset2, workspace, VAR_BUF_SIZE);
    
    				length = strlen(cp4);
    				if (length > count)
    					length = count;
    				memcpy(cp2, cp4, length);
    				count -= length;
    				cp2 += length;
    
    			}
    		} else if (nextexp) {
    			/* We have an expression.  Find the start and end, and determine
    			   if we are going to have to recursively call ourselves on the
    			   contents */
    			vars = vare = nextexp + 2;
    			brackets = 1;
    			needsub = 0;
    
    			/* Find the end of it */
    			while (brackets && *vare) {
    				if ((vare[0] == '$') && (vare[1] == '[')) {
    					needsub++;
    					brackets++;
    					vare++;
    				} else if (vare[0] == '[') {
    					brackets++;
    				} else if (vare[0] == ']') {
    					brackets--;
    				} else if ((vare[0] == '$') && (vare[1] == '{')) {
    					needsub++;
    					vare++;
    				}
    				vare++;
    			}
    			if (brackets)
    				ast_log(LOG_NOTICE, "Error in extension logic (missing ']')\n");
    			len = vare - vars - 1;
    
    			/* Skip totally over expression */
    			whereweare += (len + 3);
    
    			if (!var)
    
    				var = alloca(VAR_BUF_SIZE);
    
    
    			/* Store variable name (and truncate) */
    			ast_copy_string(var, vars, len + 1);
    
    			/* Substitute if necessary */
    			if (needsub) {
    				if (!ltmp)
    
    					ltmp = alloca(VAR_BUF_SIZE);
    
    
    				memset(ltmp, 0, VAR_BUF_SIZE);
    				pbx_substitute_variables_helper_full(c, headp, var, ltmp, VAR_BUF_SIZE - 1);
    				vars = ltmp;
    			} else {
    				vars = var;
    			}
    
    			length = ast_expr(vars, cp2, count, NULL);
    
    			if (length) {
    
    				if (option_debug)
    					ast_log(LOG_DEBUG, "Expression result is '%s'\n", cp2);
    
    			}
    		} else
    			break;
    	}
    }
    
    static void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
    {
    	pbx_substitute_variables_helper_full(c, NULL, cp1, cp2, count);
    }
    
    
    static int pbx_load_config(const char *config_file);
    
    static int pbx_load_config(const char *config_file)
    {
    	struct ast_config *cfg;
    	char *end;
    	char *label;
    	char realvalue[256];
    	int lastpri = -2;
    	struct ast_context *con;
    	struct ast_variable *v;
    	const char *cxt;
    	const char *aft;
    
    	cfg = localized_config_load(config_file);
    	if (!cfg)
    		return 0;
    
    	/* Use existing config to populate the PBX table */
    	static_config = ast_true(ast_variable_retrieve(cfg, "general", "static"));
    	write_protect_config = ast_true(ast_variable_retrieve(cfg, "general", "writeprotect"));
    	if ((aft = ast_variable_retrieve(cfg, "general", "autofallthrough")))
    		autofallthrough_config = ast_true(aft);
    	clearglobalvars_config = ast_true(ast_variable_retrieve(cfg, "general", "clearglobalvars"));
    
    
    	if ((cxt = ast_variable_retrieve(cfg, "general", "userscontext")))
    
    		ast_copy_string(userscontext, cxt, sizeof(userscontext));
    	else
    		ast_copy_string(userscontext, "default", sizeof(userscontext));
    
    	for (v = ast_variable_browse(cfg, "globals"); v; v = v->next) {
    		memset(realvalue, 0, sizeof(realvalue));
    		pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
    		pbx_builtin_setvar_helper(NULL, v->name, realvalue);
    	}
    	for (cxt = NULL; (cxt = ast_category_browse(cfg, cxt)); ) {
    		/* All categories but "general" or "globals" are considered contexts */
    		if (!strcasecmp(cxt, "general") || !strcasecmp(cxt, "globals"))
    			continue;
    
    		con=ast_context_find_or_create(&local_contexts,NULL,cxt, global_registrar);
    
    		if (con == NULL)
    			continue;
    
    		for (v = ast_variable_browse(cfg, cxt); v; v = v->next) {
    			if (!strcasecmp(v->name, "exten")) {
    				char *tc = ast_strdup(v->value);
    				if (tc) {
    					int ipri = -2;
    					char realext[256]="";
    					char *plus, *firstp, *firstc;
    					char *pri, *appl, *data, *cidmatch;
    					char *stringp = tc;
    					char *ext = strsep(&stringp, ",");
    					if (!ext)
    						ext="";
    					pbx_substitute_variables_helper(NULL, ext, realext, sizeof(realext) - 1);
    					cidmatch = strchr(realext, '/');
    					if (cidmatch) {
    						*cidmatch++ = '\0';
    						ast_shrink_phone_number(cidmatch);
    					}
    					pri = strsep(&stringp, ",");
    					if (!pri)
    						pri="";
    					label = strchr(pri, '(');
    					if (label) {
    						*label++ = '\0';
    						end = strchr(label, ')');
    						if (end)
    							*end = '\0';
    						else
    							ast_log(LOG_WARNING, "Label missing trailing ')' at line %d\n", v->lineno);
    					}
    					plus = strchr(pri, '+');
    					if (plus)
    						*plus++ = '\0';
    					if (!strcmp(pri,"hint"))
    						ipri=PRIORITY_HINT;
    					else if (!strcmp(pri, "next") || !strcmp(pri, "n")) {
    						if (lastpri > -2)
    							ipri = lastpri + 1;
    						else
    							ast_log(LOG_WARNING, "Can't use 'next' priority on the first entry!\n");
    					} else if (!strcmp(pri, "same") || !strcmp(pri, "s")) {
    						if (lastpri > -2)
    							ipri = lastpri;
    						else
    							ast_log(LOG_WARNING, "Can't use 'same' priority on the first entry!\n");
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    					} else if (sscanf(pri, "%30d", &ipri) != 1 &&
    
    					    (ipri = ast_findlabel_extension2(NULL, con, realext, pri, cidmatch)) < 1) {
    						ast_log(LOG_WARNING, "Invalid priority/label '%s' at line %d\n", pri, v->lineno);
    						ipri = 0;
    					}
    					appl = S_OR(stringp, "");
    					/* Find the first occurrence of either '(' or ',' */
    					firstc = strchr(appl, ',');
    					firstp = strchr(appl, '(');
    					if (firstc && (!firstp || firstc < firstp)) {
    						/* comma found, no parenthesis */
    						/* or both found, but comma found first */
    						appl = strsep(&stringp, ",");
    						data = stringp;
    					} else if (!firstc && !firstp) {
    						/* Neither found */
    						data = "";
    					} else {
    						/* Final remaining case is parenthesis found first */
    						appl = strsep(&stringp, "(");
    						data = stringp;
    						end = strrchr(data, ')');
    						if ((end = strrchr(data, ')'))) {
    							*end = '\0';
    						} else {
    							ast_log(LOG_WARNING, "No closing parenthesis found? '%s(%s'\n", appl, data);
    						}
    						ast_process_quotes_and_slashes(data, ',', '|');
    					}
    
    					if (!data)
    						data="";
    					appl = ast_skip_blanks(appl);
    					if (ipri) {
    						if (plus)
    							ipri += atoi(plus);
    						lastpri = ipri;
    						if (!ast_opt_dont_warn && !strcmp(realext, "_."))
    							ast_log(LOG_WARNING, "The use of '_.' for an extension is strongly discouraged and can have unexpected behavior.  Please use '_X.' instead at line %d\n", v->lineno);
    
    						if (ast_add_extension2(con, 0, realext, ipri, label, cidmatch, appl, strdup(data), ast_free_ptr, global_registrar)) {
    
    							ast_log(LOG_WARNING, "Unable to register extension at line %d\n", v->lineno);
    						}
    					}
    					free(tc);
    				}
    			} else if (!strcasecmp(v->name, "include")) {
    				memset(realvalue, 0, sizeof(realvalue));
    				pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
    
    				if (ast_context_add_include2(con, realvalue, global_registrar))
    
    					ast_log(LOG_WARNING, "Unable to include context '%s' in context '%s'\n", v->value, cxt);
    			} else if (!strcasecmp(v->name, "ignorepat")) {
    				memset(realvalue, 0, sizeof(realvalue));
    				pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
    
    				if (ast_context_add_ignorepat2(con, realvalue, global_registrar))
    
    					ast_log(LOG_WARNING, "Unable to include ignorepat '%s' in context '%s'\n", v->value, cxt);
    			} else if (!strcasecmp(v->name, "switch") || !strcasecmp(v->name, "lswitch") || !strcasecmp(v->name, "eswitch")) {
    				char *stringp= realvalue;
    				char *appl, *data;
    
    				memset(realvalue, 0, sizeof(realvalue));
    				if (!strcasecmp(v->name, "switch"))
    					pbx_substitute_variables_helper(NULL, v->value, realvalue, sizeof(realvalue) - 1);
    				else
    					ast_copy_string(realvalue, v->value, sizeof(realvalue));
    				appl = strsep(&stringp, "/");
    				data = strsep(&stringp, ""); /* XXX what for ? */
    				if (!data)
    					data = "";
    
    				if (ast_context_add_switch2(con, appl, data, !strcasecmp(v->name, "eswitch"), global_registrar))
    
    					ast_log(LOG_WARNING, "Unable to include switch '%s' in context '%s'\n", v->value, cxt);
    			} else {
    				ast_log(LOG_WARNING, "==!!== Unknown directive: %s at line %d -- IGNORING!!!\n", v->name, v->lineno);
    			}
    		}
    	}
    	ast_config_destroy(cfg);
    	return 1;
    }
    
    static void __ast_context_destroy(struct ast_context *con, const char *registrar)
    {
    	struct ast_context *tmp, *tmpl=NULL;
    	struct ast_include *tmpi;
    	struct ast_sw *sw;
    	struct ast_exten *e, *el, *en;
    	struct ast_ignorepat *ipi;
    
    	for (tmp = contexts; tmp; ) {
    		struct ast_context *next;	/* next starting point */
    		for (; tmp; tmpl = tmp, tmp = tmp->next) {
    
    			if (option_debug)
    				ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
    
    			if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
    			     (!con || !strcasecmp(tmp->name, con->name)) )
    				break;	/* found it */
    		}
    		if (!tmp)	/* not found, we are done */
    			break;
    		ast_wrlock_context(tmp);
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
    
    		next = tmp->next;
    		if (tmpl)
    			tmpl->next = next;
    		else
    			contexts = next;
    		/* Okay, now we're safe to let it go -- in a sense, we were
    		   ready to let it go as soon as we locked it. */
    		ast_unlock_context(tmp);
    		for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
    			struct ast_include *tmpil = tmpi;
    			tmpi = tmpi->next;
    			free(tmpil);
    		}
    		for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
    			struct ast_ignorepat *ipl = ipi;
    			ipi = ipi->next;
    			free(ipl);
    		}
    		while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
    			free(sw);
    		for (e = tmp->root; e;) {
    			for (en = e->peer; en;) {
    				el = en;
    				en = en->peer;
    				destroy_exten(el);
    			}
    			el = e;
    			e = e->next;
    			destroy_exten(el);
    		}
    		ast_rwlock_destroy(&tmp->lock);
    		free(tmp);
    		/* if we have a specific match, we are done, otherwise continue */
    		tmp = con ? NULL : next;
    	}
    }
    
    void localized_context_destroy(struct ast_context *con, const char *registrar);
    
    void localized_context_destroy(struct ast_context *con, const char *registrar)
    {
    	ast_wrlock_contexts();
    	__ast_context_destroy(con,registrar);
    	ast_unlock_contexts();
    }
    
    
    static void ast_merge_contexts_and_delete(struct ast_context **extcontexts, const char *registrar)
    {
    	struct ast_context *tmp, *lasttmp = NULL;
    
    	/* 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_wrlock_contexts();
    
    	tmp = *extcontexts;
    	if (registrar) {
    		/* XXX remove previous contexts from same registrar */
    
    		if (option_debug)
    			ast_log(LOG_DEBUG, "must remove any reg %s\n", registrar);
    
    		__ast_context_destroy(NULL,registrar);
    		while (tmp) {
    			lasttmp = tmp;
    			tmp = tmp->next;
    		}
    	} else {
    		/* XXX remove contexts with the same name */
    		while (tmp) {
    			ast_log(LOG_WARNING, "must remove %s  reg %s\n", tmp->name, tmp->registrar);
    			__ast_context_destroy(tmp,tmp->registrar);
    			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");
    
    	ast_unlock_contexts();
    
    	return;
    }
    
    
    void localized_merge_contexts_and_delete(struct ast_context **extcontexts, void *tab, const char *registrar)
    
    {
    	ast_merge_contexts_and_delete(extcontexts, registrar);
    }
    
    static int ast_context_verify_includes(struct ast_context *con)
    {
    	struct ast_include *inc = NULL;
    	int res = 0;
    
    	while ( (inc = ast_walk_context_includes(con, inc)) )
    		if (!ast_context_find(inc->rname)) {
    			res = -1;
    			if (strcasecmp(inc->rname,"parkedcalls")!=0)
    				ast_log(LOG_WARNING, "Context '%s' tries to include the nonexistent context '%s'\n",
    						ast_get_context_name(con), inc->rname);
    		}
    	return res;
    }
    
    int localized_context_verify_includes(struct ast_context *con);
    
    int localized_context_verify_includes(struct ast_context *con)
    {
    	return ast_context_verify_includes(con);
    }
    
    int localized_pbx_load_module(void);
    
    int localized_pbx_load_module(void)
    {
    	struct ast_context *con;
    
    
    	if(!pbx_load_config(config_filename))
    
    		return -1 /* AST_MODULE_LOAD_DECLINE*/;
    
    	/* pbx_load_users(); */ /* does this affect the dialplan? */
    
    
    	ast_merge_contexts_and_delete(&local_contexts, global_registrar);
    
    
    	for (con = NULL; (con = ast_walk_contexts(con));)
    		ast_context_verify_includes(con);
    
    	printf("=== Loading extensions.conf ===\n");
    	con = 0;
    	while ((con = ast_walk_contexts(con)) ) {
    		printf("Context: %s\n", con->name);
    	}
    	printf("=========\n");
    
    /* For platforms which don't have pthread_rwlock_timedrdlock() */
    struct timeval ast_tvnow(void);
    
    struct timeval ast_tvnow(void)
    {
    	struct timeval t;
    	gettimeofday(&t, NULL);
    	return t;
    }