Skip to content
Snippets Groups Projects
extconf.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • int ast_check_timing(const struct ast_timing *i)
    
    	/* sorry, but this feature will NOT be available
    	   in the standalone version */
    	return 0;
    
    }
    
    #ifdef NOT_ANYMORE
    static struct ast_switch *pbx_findswitch(const char *sw)
    {
    	struct ast_switch *asw;
    
    	AST_RWLIST_TRAVERSE(&switches, asw, list) {
    		if (!strcasecmp(asw->name, sw))
    			break;
    	}
    
    	return asw;
    }
    #endif
    
    
    static struct ast_context *ast_walk_contexts(struct ast_context *con);
    
    static struct ast_context *ast_walk_contexts(struct ast_context *con)
    {
    	return con ? con->next : contexts;
    }
    
    struct ast_context *localized_walk_contexts(struct ast_context *con);
    struct ast_context *localized_walk_contexts(struct ast_context *con)
    {
    	return ast_walk_contexts(con);
    }
    
    
    
    static struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
    											  struct ast_exten *exten);
    
    static struct ast_exten *ast_walk_context_extensions(struct ast_context *con,
    	struct ast_exten *exten)
    {
    	if (!exten)
    		return con ? con->root : NULL;
    	else
    		return exten->next;
    }
    
    struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
    													struct ast_exten *exten);
    struct ast_exten *localized_walk_context_extensions(struct ast_context *con,
    													struct ast_exten *exten)
    {
    	return ast_walk_context_extensions(con,exten);
    }
    
    
    static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
    												struct ast_exten *priority);
    
    static struct ast_exten *ast_walk_extension_priorities(struct ast_exten *exten,
    	struct ast_exten *priority)
    {
    	return priority ? priority->peer : exten;
    }
    
    struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
    													  struct ast_exten *priority);
    struct ast_exten *localized_walk_extension_priorities(struct ast_exten *exten,
    													  struct ast_exten *priority)
    {
    	return ast_walk_extension_priorities(exten, priority);
    }
    
    
    
    static struct ast_include *ast_walk_context_includes(struct ast_context *con,
    											  struct ast_include *inc);
    
    static struct ast_include *ast_walk_context_includes(struct ast_context *con,
    	struct ast_include *inc)
    {
    	if (!inc)
    		return con ? con->includes : NULL;
    	else
    		return inc->next;
    }
    
    
    int ast_context_includes_count(struct ast_context *con);
    int ast_context_includes_count(struct ast_context *con)
    {
    	int c = 0;
    	struct ast_include *inc = NULL;
    
    	while ((inc = ast_walk_context_includes(con, inc))) {
    		c++;
    	}
    
    	return c;
    }
    
    
    struct ast_include *localized_walk_context_includes(struct ast_context *con,
    													struct ast_include *inc);
    struct ast_include *localized_walk_context_includes(struct ast_context *con,
    													struct ast_include *inc)
    {
    	return ast_walk_context_includes(con, inc);
    }
    
    
    static struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
    	struct ast_ignorepat *ip);
    
    static struct ast_ignorepat *ast_walk_context_ignorepats(struct ast_context *con,
    	struct ast_ignorepat *ip)
    {
    	if (!ip)
    		return con ? con->ignorepats : NULL;
    	else
    		return ip->next;
    }
    
    int ast_context_ignorepats_count(struct ast_context *con);
    int ast_context_ignorepats_count(struct ast_context *con)
    {
    	int c = 0;
    	struct ast_ignorepat *ip = NULL;
    
    	while ((ip = ast_walk_context_ignorepats(con, ip))) {
    		c++;
    	}
    
    	return c;
    }
    
    
    
    static struct ast_sw *ast_walk_context_switches(struct ast_context *con,
    													 struct ast_sw *sw);
    
    static struct ast_sw *ast_walk_context_switches(struct ast_context *con,
    													 struct ast_sw *sw)
    {
    	if (!sw)
    		return con ? AST_LIST_FIRST(&con->alts) : NULL;
    	else
    		return AST_LIST_NEXT(sw, list);
    }
    
    struct ast_sw *localized_walk_context_switches(struct ast_context *con,
    													struct ast_sw *sw);
    struct ast_sw *localized_walk_context_switches(struct ast_context *con,
    													struct ast_sw *sw)
    {
    	return ast_walk_context_switches(con, sw);
    }
    
    
    int ast_context_switches_count(struct ast_context *con);
    int ast_context_switches_count(struct ast_context *con)
    {
    	int c = 0;
    	struct ast_sw *sw = NULL;
    
    	while ((sw = ast_walk_context_switches(con, sw))) {
    		c++;
    	}
    
    	return c;
    }
    
    
    
    static struct ast_context *ast_context_find(const char *name);
    
    static struct ast_context *ast_context_find(const char *name)
    {
    	struct ast_context *tmp = NULL;
    	while ( (tmp = ast_walk_contexts(tmp)) ) {
    		if (!name || !strcasecmp(name, tmp->name))
    			break;
    	}
    	return tmp;
    }
    
    /*
     * Internal function for ast_extension_{match|close}
     * return 0 on no-match, 1 on match, 2 on early match.
     * mode is as follows:
     *	E_MATCH		success only on exact match
     *	E_MATCHMORE	success only on partial match (i.e. leftover digits in pattern)
     *	E_CANMATCH	either of the above.
     */
    
    static int _extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
    {
    	mode &= E_MATCH_MASK;	/* only consider the relevant bits */
    
    	if ( (mode == E_MATCH) && (pattern[0] == '_') && (strcasecmp(pattern,data)==0) ) /* note: if this test is left out, then _x. will not match _x. !!! */
    		return 1;
    
    	if (pattern[0] != '_') { /* not a pattern, try exact or partial match */
    		int ld = strlen(data), lp = strlen(pattern);
    
    		if (lp < ld)		/* pattern too short, cannot match */
    			return 0;
    		/* depending on the mode, accept full or partial match or both */
    		if (mode == E_MATCH)
    			return !strcmp(pattern, data); /* 1 on match, 0 on fail */
    		if (ld == 0 || !strncasecmp(pattern, data, ld)) /* partial or full match */
    			return (mode == E_MATCHMORE) ? lp > ld : 1; /* XXX should consider '!' and '/' ? */
    		else
    			return 0;
    	}
    	pattern++; /* skip leading _ */
    	/*
    	 * XXX below we stop at '/' which is a separator for the CID info. However we should
    	 * not store '/' in the pattern at all. When we insure it, we can remove the checks.
    	 */
    	while (*data && *pattern && *pattern != '/') {
    		const char *end;
    
    		if (*data == '-') { /* skip '-' in data (just a separator) */
    			data++;
    			continue;
    		}
    		switch (toupper(*pattern)) {
    		case '[':	/* a range */
    			end = strchr(pattern+1, ']'); /* XXX should deal with escapes ? */
    			if (end == NULL) {
    				ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");
    				return 0;	/* unconditional failure */
    			}
    			for (pattern++; pattern != end; pattern++) {
    				if (pattern+2 < end && pattern[1] == '-') { /* this is a range */
    					if (*data >= pattern[0] && *data <= pattern[2])
    						break;	/* match found */
    					else {
    						pattern += 2; /* skip a total of 3 chars */
    						continue;
    					}
    				} else if (*data == pattern[0])
    					break;	/* match found */
    			}
    			if (pattern == end)
    				return 0;
    			pattern = end;	/* skip and continue */
    			break;
    		case 'N':
    			if (*data < '2' || *data > '9')
    				return 0;
    			break;
    		case 'X':
    			if (*data < '0' || *data > '9')
    				return 0;
    			break;
    		case 'Z':
    			if (*data < '1' || *data > '9')
    				return 0;
    			break;
    		case '.':	/* Must match, even with more digits */
    			return 1;
    		case '!':	/* Early match */
    			return 2;
    		case ' ':
    		case '-':	/* Ignore these in patterns */
    			data--; /* compensate the final data++ */
    			break;
    		default:
    			if (*data != *pattern)
    				return 0;
    		}
    		data++;
    		pattern++;
    	}
    	if (*data)			/* data longer than pattern, no match */
    		return 0;
    	/*
    	 * match so far, but ran off the end of the data.
    	 * Depending on what is next, determine match or not.
    	 */
    	if (*pattern == '\0' || *pattern == '/')	/* exact match */
    		return (mode == E_MATCHMORE) ? 0 : 1;	/* this is a failure for E_MATCHMORE */
    	else if (*pattern == '!')			/* early match */
    		return 2;
    	else						/* partial match */
    		return (mode == E_MATCH) ? 0 : 1;	/* this is a failure for E_MATCH */
    }
    
    static int extension_match_core(const char *pattern, const char *data, enum ext_match_t mode)
    {
    	int i;
    	i = _extension_match_core(pattern, data, mode);
    	return i;
    }
    
    static int ast_extension_match(const char *pattern, const char *data);
    
    static int ast_extension_match(const char *pattern, const char *data)
    {
    	return extension_match_core(pattern, data, E_MATCH);
    }
    
    static int matchcid(const char *cidpattern, const char *callerid)
    {
    	/* If the Caller*ID pattern is empty, then we're matching NO Caller*ID, so
    	   failing to get a number should count as a match, otherwise not */
    
    	if (ast_strlen_zero(callerid))
    		return ast_strlen_zero(cidpattern) ? 1 : 0;
    
    	return ast_extension_match(cidpattern, callerid);
    }
    
    static inline int include_valid(struct ast_include *i)
    {
    	if (!i->hastime)
    		return 1;
    
    	return ast_check_timing(&(i->timing));
    }
    
    
    
    static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
    
    											struct ast_context *bypass,
    
    											const char *context,
    											const char *exten,
    
    											const char *label,
    											const char *callerid,
    
    											enum ext_match_t action);
    
    
    static struct ast_exten *pbx_find_extension(struct ast_channel *chan,
    
    											struct ast_context *bypass,
    
    											const char *context,
    											const char *exten,
    
    											const char *label,
    											const char *callerid,
    
    											enum ext_match_t action)
    {
    	int x;
    	struct ast_context *tmp;
    	struct ast_exten *e, *eroot;
    	struct ast_include *i;
    
    
    	/* Initialize status if appropriate */
    	if (q->stacklen == 0) {
    		q->status = STATUS_NO_CONTEXT;
    		q->swo = NULL;
    		q->data = NULL;
    		q->foundcontext = NULL;
    	} else if (q->stacklen >= AST_PBX_MAX_STACK) {
    		ast_log(LOG_WARNING, "Maximum PBX stack exceeded\n");
    		return NULL;
    	}
    	/* Check first to see if we've already been checked */
    	for (x = 0; x < q->stacklen; x++) {
    		if (!strcasecmp(q->incstack[x], context))
    			return NULL;
    	}
    	if (bypass)	/* bypass means we only look there */
    		tmp = bypass;
    	else {	/* look in contexts */
    		tmp = NULL;
    		while ((tmp = ast_walk_contexts(tmp)) ) {
    			if (!strcmp(tmp->name, context))
    				break;
    		}
    		if (!tmp)
    			return NULL;
    	}
    	if (q->status < STATUS_NO_EXTENSION)
    		q->status = STATUS_NO_EXTENSION;
    
    	/* scan the list trying to match extension and CID */
    	eroot = NULL;
    	while ( (eroot = ast_walk_context_extensions(tmp, eroot)) ) {
    		int match = extension_match_core(eroot->exten, exten, action);
    		/* 0 on fail, 1 on match, 2 on earlymatch */
    
    		if (!match || (eroot->matchcid && !matchcid(eroot->cidmatch, callerid)))
    			continue;	/* keep trying */
    		if (match == 2 && action == E_MATCHMORE) {
    			/* We match an extension ending in '!'.
    			 * The decision in this case is final and is NULL (no match).
    			 */
    			return NULL;
    		}
    		/* found entry, now look for the right priority */
    		if (q->status < STATUS_NO_PRIORITY)
    			q->status = STATUS_NO_PRIORITY;
    		e = NULL;
    		while ( (e = ast_walk_extension_priorities(eroot, e)) ) {
    			/* Match label or priority */
    			if (action == E_FINDLABEL) {
    				if (q->status < STATUS_NO_LABEL)
    					q->status = STATUS_NO_LABEL;
    				if (label && e->label && !strcmp(label, e->label))
    					break;	/* found it */
    			} else if (e->priority == priority) {
    				break;	/* found it */
    			} /* else keep searching */
    		}
    		if (e) {	/* found a valid match */
    			q->status = STATUS_SUCCESS;
    			q->foundcontext = context;
    			return e;
    		}
    	}
    #ifdef NOT_RIGHT_NOW
    	/* Check alternative switches???  */
    	AST_LIST_TRAVERSE(&tmp->alts, sw, list) {
    		struct ast_switch *asw = pbx_findswitch(sw->name);
    		ast_switch_f *aswf = NULL;
    		char *datap;
    
    		if (!asw) {
    			ast_log(LOG_WARNING, "No such switch '%s'\n", sw->name);
    			continue;
    		}
    		/* No need to Substitute variables now; we shouldn't be here if there's any  */
    
    		/* equivalent of extension_match_core() at the switch level */
    		if (action == E_CANMATCH)
    			aswf = asw->canmatch;
    		else if (action == E_MATCHMORE)
    			aswf = asw->matchmore;
    		else /* action == E_MATCH */
    			aswf = asw->exists;
    		datap = sw->eval ? sw->tmpdata : sw->data;
    		res = !aswf ? 0 : aswf(chan, context, exten, priority, callerid, datap);
    		if (res) {	/* Got a match */
    			q->swo = asw;
    			q->data = datap;
    			q->foundcontext = context;
    			/* XXX keep status = STATUS_NO_CONTEXT ? */
    			return NULL;
    		}
    	}
    #endif
    	q->incstack[q->stacklen++] = tmp->name;	/* Setup the stack */
    	/* Now try any includes we have in this context */
    	for (i = tmp->includes; i; i = i->next) {
    		if (include_valid(i)) {
    			if ((e = pbx_find_extension(NULL, bypass, q, i->rname, exten, priority, label, callerid, action)))
    				return e;
    			if (q->swo)
    				return NULL;
    		}
    	}
    	return NULL;
    }
    
    struct ast_exten *localized_find_extension(struct ast_context *bypass,
    										  struct pbx_find_info *q,
    
    										  const char *context,
    										  const char *exten,
    
    										  const char *label,
    										  const char *callerid,
    
    										  enum ext_match_t action);
    
    struct ast_exten *localized_find_extension(struct ast_context *bypass,
    										  struct pbx_find_info *q,
    
    										  const char *context,
    										  const char *exten,
    
    										  const char *label,
    										  const char *callerid,
    
    										   enum ext_match_t action)
    {
    	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 = strchr(p, ',')) ) {
    
    		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;
    }