Skip to content
Snippets Groups Projects
extconf.c 181 KiB
Newer Older
  • Learn to ignore specific revisions
  • {
    	return pbx_find_extension(NULL, bypass, q, context, exten, priority, label, callerid, action);
    }
    
    
    static struct ast_context *contexts;
    AST_RWLOCK_DEFINE_STATIC(conlock); 		/*!< Lock for the ast_context list */
    
    static const char *ast_get_context_name(struct ast_context *con);
    
    static const char *ast_get_context_name(struct ast_context *con)
    {
    	return con ? con->name : NULL;
    }
    
    /*
     * errno values
     *  ENOMEM - out of memory
     *  EBUSY  - can't lock
     *  EEXIST - already included
     *  EINVAL - there is no existence of context for inclusion
     */
    static int ast_context_add_include2(struct ast_context *con, const char *value,
    							 const char *registrar);
    
    static int ast_context_add_include2(struct ast_context *con, const char *value,
    									const char *registrar)
    {
    	struct ast_include *new_include;
    	char *c;
    	struct ast_include *i, *il = NULL; /* include, include_last */
    	int length;
    	char *p;
    
    	length = sizeof(struct ast_include);
    	length += 2 * (strlen(value) + 1);
    
    	/* allocate new include structure ... */
    	if (!(new_include = ast_calloc(1, length)))
    		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;
    	strcpy(p, value);
    	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);
    
    	}
    	new_include->next      = NULL;
    	new_include->registrar = registrar;
    
    
    	/* ... go to last include and check if context is already included too... */
    	for (i = con->includes; i; i = i->next) {
    		if (!strcasecmp(i->name, new_include->name)) {
    			free(new_include);
    			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));
    
    	return 0;
    }
    
    int localized_context_add_include2(struct ast_context *con, const char *value,
    							 const char *registrar);
    int localized_context_add_include2(struct ast_context *con, const char *value,
    								   const char *registrar)
    {
    	return  ast_context_add_include2(con, value, registrar);
    }
    
    
    
    static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
    
    static int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
    {
    	struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
    	int length;
    	length = sizeof(struct ast_ignorepat);
    	length += strlen(value) + 1;
    	if (!(ignorepat = ast_calloc(1, length)))
    		return -1;
    	/* The cast to char * is because we need to write the initial value.
    	 * The field is not supposed to be modified otherwise
    	 */
    	strcpy((char *)ignorepat->pattern, value);
    	ignorepat->next = NULL;
    	ignorepat->registrar = registrar;
    	for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
    		ignorepatl = ignorepatc;
    		if (!strcasecmp(ignorepatc->pattern, value)) {
    			/* Already there */
    			errno = EEXIST;
    			return -1;
    		}
    	}
    	if (ignorepatl)
    		ignorepatl->next = ignorepat;
    	else
    		con->ignorepats = ignorepat;
    	return 0;
    
    }
    
    int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar);
    
    int localized_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
    {
    	return ast_context_add_ignorepat2(con, value, registrar);
    }
    
    
    /*
     * Lock context list functions ...
     */
    
    static int ast_wrlock_contexts(void)
    {
    	return ast_rwlock_wrlock(&conlock);
    }
    
    static int ast_unlock_contexts(void)
    {
    	return ast_rwlock_unlock(&conlock);
    }
    
    static int ast_wrlock_context(struct ast_context *con)
    {
    	return ast_rwlock_wrlock(&con->lock);
    }
    
    static int ast_unlock_context(struct ast_context *con)
    {
    	return ast_rwlock_unlock(&con->lock);
    }
    
    /*
     * errno values
     *  ENOMEM - out of memory
     *  EBUSY  - can't lock
     *  EEXIST - already included
     *  EINVAL - there is no existence of context for inclusion
     */
    static int ast_context_add_switch2(struct ast_context *con, const char *value,
    							const char *data, int eval, const char *registrar);
    
    static int ast_context_add_switch2(struct ast_context *con, const char *value,
    	const char *data, int eval, const char *registrar)
    {
    	struct ast_sw *new_sw;
    	struct ast_sw *i;
    	int length;
    	char *p;
    
    	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++;
    	}
    
    	/* allocate new sw structure ... */
    	if (!(new_sw = ast_calloc(1, length)))
    		return -1;
    	/* ... 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;
    	if (data) {
    		strcpy(new_sw->data, data);
    		p += strlen(data) + 1;
    	} else {
    		strcpy(new_sw->data, "");
    		p++;
    	}
    	if (eval)
    		new_sw->tmpdata = p;
    	new_sw->eval	  = eval;
    	new_sw->registrar = registrar;
    
    	/* ... 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)) {
    			free(new_sw);
    			errno = EEXIST;
    			return -1;
    		}
    	}
    
    	/* ... sw new context into context list, unlock, return */
    	AST_LIST_INSERT_TAIL(&con->alts, new_sw, list);
    
    	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));
    
    	return 0;
    }
    
    int localized_context_add_switch2(struct ast_context *con, const char *value,
    										 const char *data, int eval, const char *registrar);
    
    int localized_context_add_switch2(struct ast_context *con, const char *value,
    										 const char *data, int eval, const char *registrar)
    {
    	return ast_context_add_switch2(con, value, data, eval, registrar);
    }
    
    static struct ast_context *__ast_context_create(struct ast_context **extcontexts, const char *name, const char *registrar, int existsokay)
    {
    
    	struct ast_context *tmp, **loc_contexts;
    
    	int length = sizeof(struct ast_context) + strlen(name) + 1;
    
    	if (!extcontexts) {
    		ast_wrlock_contexts();
    
    		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);
    				count -= length;
    				cp2 += length;
    
    			}
    		} 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);