Skip to content
Snippets Groups Projects
pbx.c 166 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	"jul",
    	"aug",
    	"sep",
    	"oct",
    	"nov",
    	"dec",
    };
    
    static unsigned int get_month(char *mon)
    {
    	char *c;
    	/* The following line is coincidence, really! */
    	int s, e, x;
    	unsigned int mask;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Check for all days */
    
    	if (ast_strlen_zero(mon) || !strcmp(mon, "*")) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return (1 << 12) - 1;
    	/* Get start and ending days */
    	c = strchr(mon, '-');
    	if (c) {
    		*c = '\0';
    		c++;
    	}
    	/* Find the start */
    	s = 0;
    	while((s < 12) && strcasecmp(mon, months[s])) s++;
    	if (s >= 12) {
    		ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", mon);
    		return 0;
    	}
    	if (c) {
    		e = 0;
    		while((e < 12) && strcasecmp(mon, months[e])) e++;
    		if (e >= 12) {
    			ast_log(LOG_WARNING, "Invalid month '%s', assuming none\n", c);
    			return 0;
    		}
    	} else
    		e = s;
    	mask = 0;
    
    	for (x=s; x!=e; x = (x + 1) % 12) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		mask |= (1 << x);
    	}
    	/* One last one */
    	mask |= (1 << x);
    	return mask;
    }
    
    
    int ast_build_timing(struct ast_timing *i, char *info_in)
    
    	char info_save[256];
    	char *info;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Check for empty just in case */
    
    	if (ast_strlen_zero(info_in))
    		return 0;
    	/* make a copy just in case we were passed a static string */
    
    	ast_copy_string(info_save, info_in, sizeof(info_save));
    
    	info = info_save;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Assume everything except time */
    	i->monthmask = (1 << 12) - 1;
    	i->daymask = (1 << 30) - 1 + (1 << 30);
    	i->dowmask = (1 << 7) - 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Avoid using str tok */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	FIND_NEXT;
    	/* Info has the time range, start with that */
    	get_timerange(i, info);
    	info = c;
    	if (!info)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	FIND_NEXT;
    	/* Now check for day of week */
    	i->dowmask = get_dow(info);
    
    	info = c;
    	if (!info)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	FIND_NEXT;
    	/* Now check for the day of the month */
    	i->daymask = get_day(info);
    	info = c;
    	if (!info)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	FIND_NEXT;
    	/* And finally go for the month */
    	i->monthmask = get_month(info);
    
    
    	return 1;
    }
    
    int ast_check_timing(struct ast_timing *i)
    {
    	struct tm tm;
    	time_t t;
    
    	time(&t);
    	localtime_r(&t,&tm);
    
    	/* If it's not the right month, return */
    	if (!(i->monthmask & (1 << tm.tm_mon))) {
    		return 0;
    	}
    
    	/* If it's not that time of the month.... */
    	/* Warning, tm_mday has range 1..31! */
    	if (!(i->daymask & (1 << (tm.tm_mday-1))))
    		return 0;
    
    	/* If it's not the right day of the week */
    	if (!(i->dowmask & (1 << tm.tm_wday)))
    		return 0;
    
    	/* Sanity check the hour just to be safe */
    	if ((tm.tm_hour < 0) || (tm.tm_hour > 23)) {
    		ast_log(LOG_WARNING, "Insane time...\n");
    		return 0;
    	}
    
    	/* Now the tough part, we calculate if it fits
    	   in the right time based on min/hour */
    	if (!(i->minmask[tm.tm_hour] & (1 << (tm.tm_min / 2))))
    		return 0;
    
    	/* If we got this far, then we're good */
    	return 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * errno values
     *  ENOMEM - out of memory
     *  EBUSY  - can't lock
     *  EEXIST - already included
     *  EINVAL - there is no existence of context for inclusion
     */
    
    int ast_context_add_include2(struct ast_context *con, const char *value,
    	const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_include *new_include;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char *c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_include *i, *il = NULL; /* include, include_last */
    
    	int length;
    	char *p;
    	
    	length = sizeof(struct ast_include);
    	length += 2 * (strlen(value) + 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* allocate new include structure ... */
    
    	if (!(new_include = malloc(length))) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		errno = ENOMEM;
    		return -1;
    	}
    	
    	/* ... fill in this structure ... */
    
    	memset(new_include, 0, length);
    	p = new_include->stuff;
    	new_include->name = p;
    	strcpy(new_include->name, value);
    	p += strlen(value) + 1;
    	new_include->rname = p;
    	strcpy(new_include->rname, value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	c = new_include->rname;
    	/* Strip off timing info */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Process if it's there */
    	if (*c) {
    
    	        new_include->hastime = ast_build_timing(&(new_include->timing), c+1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		*c = '\0';
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	new_include->next      = NULL;
    	new_include->registrar = registrar;
    
    	/* ... try to lock this context ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		free(new_include);
    		errno = EBUSY;
    		return -1;
    	}
    
    	/* ... go to last include and check if context is already included too... */
    	i = con->includes;
    	while (i) {
    		if (!strcasecmp(i->name, new_include->name)) {
    			free(new_include);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			errno = EEXIST;
    			return -1;
    		}
    		il = i;
    		i = i->next;
    	}
    
    	/* ... include new context into context list, unlock, return */
    	if (il)
    		il->next = new_include;
    	else
    		con->includes = new_include;
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "Including context '%s' in context '%s'\n", new_include->name, ast_get_context_name(con)); 
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 0;
    }
    
    /*
     * errno values
     *  EBUSY  - can't lock
    
    Mark Spencer's avatar
    Mark Spencer committed
     *  ENOENT - no existence of context
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    int ast_context_add_switch(const char *context, const char *sw, const char *data, int eval, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *c;
    
    	if (ast_lock_contexts()) {
    		errno = EBUSY;
    		return -1;
    	}
    
    	/* walk contexts ... */
    	c = ast_walk_contexts(NULL);
    	while (c) {
    		/* ... search for the right one ... */
    		if (!strcmp(ast_get_context_name(c), context)) {
    
    			int ret = ast_context_add_switch2(c, sw, data, eval, registrar);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* ... unlock contexts list and return */
    			ast_unlock_contexts();
    			return ret;
    		}
    		c = ast_walk_contexts(c);
    	}
    
    	/* we can't find the right context */
    	ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	errno = ENOENT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    /*
     * errno values
     *  ENOMEM - out of memory
     *  EBUSY  - can't lock
     *  EEXIST - already included
     *  EINVAL - there is no existence of context for inclusion
     */
    
    int ast_context_add_switch2(struct ast_context *con, const char *value,
    
    	const char *data, int eval, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_sw *new_sw;
    	struct ast_sw *i, *il = NULL; /* sw, sw_last */
    
    	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++;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* allocate new sw structure ... */
    
    	if (!(new_sw = malloc(length))) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		errno = ENOMEM;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* ... fill in this structure ... */
    
    	memset(new_sw, 0, length);
    	p = new_sw->stuff;
    	new_sw->name = p;
    	strcpy(new_sw->name, value);
    	p += strlen(value) + 1;
    	new_sw->data = p;
    
    		strcpy(new_sw->data, data);
    
    		strcpy(new_sw->data, "");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	new_sw->next      = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	new_sw->registrar = registrar;
    
    	/* ... try to lock this context ... */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		free(new_sw);
    		errno = EBUSY;
    		return -1;
    	}
    
    	/* ... go to last sw and check if context is already swd too... */
    	i = con->alts;
    	while (i) {
    
    		if (!strcasecmp(i->name, new_sw->name) && !strcasecmp(i->data, new_sw->data)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			free(new_sw);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			errno = EEXIST;
    			return -1;
    		}
    		il = i;
    		i = i->next;
    	}
    
    	/* ... sw new context into context list, unlock, return */
    	if (il)
    		il->next = new_sw;
    	else
    		con->alts = new_sw;
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "Including switch '%s/%s' in context '%s'\n", new_sw->name, new_sw->data, ast_get_context_name(con)); 
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	return 0;
    }
    
    /*
     * EBUSY  - can't lock
    
    Mark Spencer's avatar
    Mark Spencer committed
     * ENOENT - there is not context existence
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    int ast_context_remove_ignorepat(const char *context, const char *ignorepat, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *c;
    
    	if (ast_lock_contexts()) {
    		errno = EBUSY;
    		return -1;
    	}
    
    	c = ast_walk_contexts(NULL);
    	while (c) {
    		if (!strcmp(ast_get_context_name(c), context)) {
    			int ret = ast_context_remove_ignorepat2(c, ignorepat, registrar);
    			ast_unlock_contexts();
    			return ret;
    		}
    		c = ast_walk_contexts(c);
    	}
    
    	ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	errno = ENOENT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    int ast_context_remove_ignorepat2(struct ast_context *con, const char *ignorepat, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_ignorepat *ip, *ipl = NULL;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    		errno = EBUSY;
    		return -1;
    	}
    
    	ip = con->ignorepats;
    	while (ip) {
    		if (!strcmp(ip->pattern, ignorepat) &&
    
    			(!registrar || (registrar == ip->registrar))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (ipl) {
    				ipl->next = ip->next;
    				free(ip);
    			} else {
    				con->ignorepats = ip->next;
    				free(ip);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		ipl = ip; ip = ip->next;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	errno = EINVAL;
    	return -1;
    }
    
    /*
     * EBUSY - can't lock
    
    Mark Spencer's avatar
    Mark Spencer committed
     * ENOENT - there is no existence of context
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    int ast_context_add_ignorepat(const char *con, const char *value, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *c;
    
    	if (ast_lock_contexts()) {
    		errno = EBUSY;
    		return -1;
    	}
    
    	c = ast_walk_contexts(NULL);
    	while (c) {
    		if (!strcmp(ast_get_context_name(c), con)) {
    			int ret = ast_context_add_ignorepat2(c, value, registrar);
    			ast_unlock_contexts();
    			return ret;
    		} 
    		c = ast_walk_contexts(c);
    	}
    
    	ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	errno = ENOENT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    int ast_context_add_ignorepat2(struct ast_context *con, const char *value, const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_ignorepat *ignorepat, *ignorepatc, *ignorepatl = NULL;
    
    	int length;
    	length = sizeof(struct ast_ignorepat);
    	length += strlen(value) + 1;
    	ignorepat = malloc(length);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!ignorepat) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		errno = ENOMEM;
    		return -1;
    	}
    
    	memset(ignorepat, 0, length);
    	strcpy(ignorepat->pattern, value);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ignorepat->next = NULL;
    	ignorepat->registrar = registrar;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ignorepatc = con->ignorepats;
    	while(ignorepatc) {
    		ignorepatl = ignorepatc;
    		if (!strcasecmp(ignorepatc->pattern, value)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Already there */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			errno = EEXIST;
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ignorepatc = ignorepatc->next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ignorepatl) 
    		ignorepatl->next = ignorepat;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		con->ignorepats = ignorepat;
    
    int ast_ignore_pattern(const char *context, const char *pattern)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *con;
    	struct ast_ignorepat *pat;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	con = ast_context_find(context);
    	if (con) {
    		pat = con->ignorepats;
    		while (pat) {
    			if (ast_extension_match(pat->pattern, pattern))
    				return 1;
    			pat = pat->next;
    		}
    	} 
    	return 0;
    }
    
    /*
     * EBUSY   - can't lock
    
    Mark Spencer's avatar
    Mark Spencer committed
     * ENOENT  - no existence of context
    
    int ast_add_extension(const char *context, int replace, const char *extension, int priority, const char *label, const char *callerid,
    	const char *application, void *data, void (*datad)(void *), const char *registrar)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_context *c;
    
    	if (ast_lock_contexts()) {
    		errno = EBUSY;
    		return -1;
    	}
    
    	c = ast_walk_contexts(NULL);
    	while (c) {
    		if (!strcmp(context, ast_get_context_name(c))) {
    
    			int ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
    
    Mark Spencer's avatar
    Mark Spencer committed
    				application, data, datad, registrar);
    			ast_unlock_contexts();
    			return ret;
    		}
    		c = ast_walk_contexts(c);
    	}
    
    	ast_unlock_contexts();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	errno = ENOENT;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return -1;
    }
    
    
    int ast_explicit_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
    {
    	if (!chan)
    		return -1;
    
    
    		ast_copy_string(chan->context, context, sizeof(chan->context));
    
    		ast_copy_string(chan->exten, exten, sizeof(chan->exten));
    
    	if (priority > -1) {
    		chan->priority = priority;
    		/* see flag description in channel.h for explanation */
    		if (ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP))
    			chan->priority--;
    
    int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->pbx) {
    		/* This channel is currently in the PBX */
    
    		ast_explicit_goto(chan, context, exten, priority);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
    	} else {
    		/* In order to do it when the channel doesn't really exist within
    		   the PBX, we have to make a new channel, masquerade, and start the PBX
    		   at the new location */
    		struct ast_channel *tmpchan;
    		tmpchan = ast_channel_alloc(0);
    		if (tmpchan) {
    			snprintf(tmpchan->name, sizeof(tmpchan->name), "AsyncGoto/%s", chan->name);
    
    			ast_setstate(tmpchan, chan->_state);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Make formats okay */
    			tmpchan->readformat = chan->readformat;
    			tmpchan->writeformat = chan->writeformat;
    			/* Setup proper location */
    
    					  (!ast_strlen_zero(context)) ? context : chan->context,
    					  (!ast_strlen_zero(exten)) ? exten : chan->exten,
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Masquerade into temp channel */
    			ast_channel_masquerade(tmpchan, chan);
    
    Malcolm Davenport's avatar
    Malcolm Davenport committed
    		
    
    			/* Grab the locks and get going */
    			ast_mutex_lock(&tmpchan->lock);
    
    			ast_mutex_unlock(&tmpchan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Start the PBX going on our stolen channel */
    			if (ast_pbx_start(tmpchan)) {
    				ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmpchan->name);
    				ast_hangup(tmpchan);
    				res = -1;
    			}
    		} else {
    			res = -1;
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *chan;
    
    	chan = ast_get_channel_by_name_locked(channame);
    
    	if (chan) {
    		res = ast_async_goto(chan, context, exten, priority);
    		ast_mutex_unlock(&chan->lock);
    	}
    	return res;
    
    static int ext_strncpy(char *dst, const char *src, int len)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int count=0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	while(*src && (count < len - 1)) {
    		switch(*src) {
    		case ' ':
    
    			/*	otherwise exten => [a-b],1,... doesn't work */
    			/*		case '-': */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Ignore */
    			break;
    		default:
    			*dst = *src;
    			dst++;
    		}
    		src++;
    		count++;
    	}
    	*dst = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * EBUSY - can't lock
     * EEXIST - extension with the same priority exist and no replace is set
     *
     */
    
    Mark Spencer's avatar
    Mark Spencer committed
    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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define LOG do { 	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); \
    		} \
    	} else 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); \
    		} \
    	} } while(0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/*
    	 * This is a fairly complex routine.  Different extensions are kept
    	 * in order by the extension number.  Then, extensions of different
    	 * priorities (same extension) are kept in a list, according to the
    	 * peer pointer.
    	 */
    	struct ast_exten *tmp, *e, *el = NULL, *ep = NULL;
    	int res;
    
    	char expand_buf[VAR_BUF_SIZE] = { 0, };
    
    
    	/* if we are adding a hint, and there are global variables, and the hint
    	   contains variable references, then expand them
    	*/
    	if ((priority == PRIORITY_HINT) && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
    		pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
    		application = expand_buf;
    	}
    
    
    	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 ++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Be optimistic:  Build the extension structure first */
    
    	if (datad == NULL)
    		datad = null_datad;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (tmp) {
    
    		memset(tmp, 0, length);
    		p = tmp->stuff;
    		if (label) {
    			tmp->label = p;
    			strcpy(tmp->label, label);
    			p += strlen(label) + 1;
    		}
    		tmp->exten = p;
    
    		p += ext_strncpy(tmp->exten, extension, strlen(extension) + 1) + 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->priority = priority;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (callerid) {
    
    			p += ext_strncpy(tmp->cidmatch, callerid, strlen(callerid) + 1) + 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			tmp->matchcid = 1;
    		} else {
    
    			tmp->cidmatch[0] = '\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    			tmp->matchcid = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		tmp->app = p;
    		strcpy(tmp->app, application);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->data = data;
    		tmp->datad = datad;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->registrar = registrar;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp->peer = NULL;
    		tmp->next =  NULL;
    	} else {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		errno = ENOMEM;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		free(tmp);
    		/* And properly destroy the data */
    		datad(data);
    		ast_log(LOG_WARNING, "Failed to lock context '%s'\n", con->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		errno = EBUSY;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	e = con->root;
    	while(e) {
    
    		/* Make sure patterns are always last! */
    		if ((e->exten[0] != '_') && (extension[0] == '_'))
    			res = -1;
    		else if ((e->exten[0] == '_') && (extension[0] != '_'))
    			res = 1;
    		else
    			res= strcmp(e->exten, extension);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!res) {
    			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);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res == 0) {
    			/* We have an exact match, now we find where we are
    			   and be sure there's no duplicates */
    			while(e) {
    				if (e->priority == tmp->priority) {
    					/* Can't have something exactly the same.  Is this a
    					   replacement?  If so, replace, otherwise, bonk. */
    					if (replace) {
    						if (ep) {
    							/* We're in the peer list, insert ourselves */
    							ep->peer = tmp;
    							tmp->peer = e->peer;
    						} else if (el) {
    							/* We're the first extension. Take over e's functions */
    							el->next = tmp;
    							tmp->next = e->next;
    							tmp->peer = e->peer;
    						} else {
    							/* We're the very first extension.  */
    							con->root = tmp;
    							tmp->next = e->next;
    							tmp->peer = e->peer;
    						}
    
    						if (tmp->priority == PRIORITY_HINT)
    						    ast_change_hint(e,tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* Destroy the old one */
    						e->datad(e->data);
    						free(e);
    
    						if (tmp->priority == PRIORITY_HINT)
    						    ast_change_hint(e, tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* And immediately return success. */
    						LOG;
    						return 0;
    					} else {
    						ast_log(LOG_WARNING, "Unable to register extension '%s', priority %d in '%s', already in use\n", tmp->exten, tmp->priority, con->name);
    						tmp->datad(tmp->data);
    						free(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						errno = EEXIST;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						return -1;
    					}
    				} else if (e->priority > tmp->priority) {
    					/* Slip ourselves in just before e */
    					if (ep) {
    						/* Easy enough, we're just in the peer list */
    						ep->peer = tmp;
    						tmp->peer = e;
    					} else if (el) {
    						/* We're the first extension in this peer list */
    						el->next = tmp;
    						tmp->next = e->next;
    						e->next = NULL;
    						tmp->peer = e;
    					} else {
    						/* We're the very first extension altogether */
    
    Mark Spencer's avatar
    Mark Spencer committed
    						/* Con->root must always exist or we couldn't get here */
    
    Mark Spencer's avatar
    Mark Spencer committed
    						con->root = tmp;
    					}
    
    Mark Spencer's avatar
    Mark Spencer committed
    					/* And immediately return success. */
    
    					if (tmp->priority == PRIORITY_HINT)
    
    						 ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					LOG;
    					return 0;
    				}
    				ep = e;
    				e = e->peer;
    			}
    			/* If we make it here, then it's time for us to go at the very end.
    			   ep *must* be defined or we couldn't have gotten here. */
    			ep->peer = tmp;
    
    			if (tmp->priority == PRIORITY_HINT)
    
    				ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* And immediately return success. */
    			LOG;
    			return 0;
    				
    		} else if (res > 0) {
    			/* Insert ourselves just before 'e'.  We're the first extension of
    			   this kind */
    			tmp->next = e;
    			if (el) {
    				/* We're in the list somewhere */
    				el->next = tmp;
    			} else {
    				/* We're at the top of the list */
    				con->root = tmp;
    			}
    
    			if (tmp->priority == PRIORITY_HINT)
    
    				ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* And immediately return success. */
    			LOG;
    			return 0;
    		}			
    			
    		el = e;
    		e = e->next;
    	}
    	/* If we fall all the way through to here, then we need to be on the end. */
    	if (el)
    		el->next = tmp;
    	else
    		con->root = tmp;
    
    	if (tmp->priority == PRIORITY_HINT)
    
    		ast_add_hint(tmp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	LOG;
    	return 0;	
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct async_stat {
    	pthread_t p;
    	struct ast_channel *chan;
    
    	char context[AST_MAX_CONTEXT];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char exten[AST_MAX_EXTENSION];
    	int priority;
    	int timeout;
    	char app[AST_MAX_EXTENSION];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char appdata[1024];
    
    Mark Spencer's avatar
    Mark Spencer committed
    };
    
    static void *async_wait(void *data) 
    {
    	struct async_stat *as = data;
    	struct ast_channel *chan = as->chan;
    	int timeout = as->timeout;
    	int res;
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_app *app;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	while(timeout && (chan->_state != AST_STATE_UP)) {
    		res = ast_waitfor(chan, timeout);
    		if (res < 1) 
    			break;
    		if (timeout > -1)
    			timeout = res;
    		f = ast_read(chan);
    		if (!f)
    			break;
    		if (f->frametype == AST_FRAME_CONTROL) {
    			if ((f->subclass == AST_CONTROL_BUSY)  ||
    				(f->subclass == AST_CONTROL_CONGESTION) )
    					break;
    		}
    		ast_frfree(f);
    	}
    	if (chan->_state == AST_STATE_UP) {
    
    		if (!ast_strlen_zero(as->app)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			app = pbx_findapp(as->app);
    			if (app) {
    				if (option_verbose > 2)
    
    					ast_verbose(VERBOSE_PREFIX_3 "Launching %s(%s) on %s\n", as->app, as->appdata, chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				pbx_exec(chan, app, as->appdata, 1);
    			} else
    				ast_log(LOG_WARNING, "No such application '%s'\n", as->app);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} else {
    
    			if (!ast_strlen_zero(as->context))
    
    				ast_copy_string(chan->context, as->context, sizeof(chan->context));
    
    			if (!ast_strlen_zero(as->exten))
    
    				ast_copy_string(chan->exten, as->exten, sizeof(chan->exten));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (as->priority > 0)
    				chan->priority = as->priority;
    			/* Run the PBX */
    			if (ast_pbx_run(chan)) {
    
    				ast_log(LOG_ERROR, "Failed to start PBX on %s\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    				/* PBX will have taken care of this */
    				chan = NULL;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    			
    	}
    	free(as);
    	if (chan)
    		ast_hangup(chan);
    	return NULL;
    }
    
    
    /*! Function to update the cdr after a spool call fails.
     *
     *  This function updates the cdr for a failed spool call
     *  and takes the channel of the failed call as an argument.
     *
     */
    int ast_pbx_outgoing_cdr_failed(void)
    {
    	/* allocate a channel */
    	struct ast_channel *chan = ast_channel_alloc(0);
    	if(!chan) {
    		/* allocation of the channel failed, let some peeps know */
    		ast_log(LOG_WARNING, "Unable to allocate channel structure for CDR record\n");
    		return -1;  /* failure */
    	}
    
    	chan->cdr = ast_cdr_alloc();   /* allocate a cdr for the channel */
    
    	if(!chan->cdr) {
    		/* allocation of the cdr failed */
    		ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
    		ast_channel_free(chan);   /* free the channel */
    		return -1;                /* return failure */
    	}
    	
    	/* allocation of the cdr was successful */
    	ast_cdr_init(chan->cdr, chan);  /* initilize our channel's cdr */
    	ast_cdr_start(chan->cdr);       /* record the start and stop time */
    	ast_cdr_end(chan->cdr);
    	ast_cdr_failed(chan->cdr);      /* set the status to failed */
    
    	ast_cdr_detach(chan->cdr);      /* post and free the record */
    
    	ast_channel_free(chan);         /* free the channel */
    	
    	return 0;  /* success */
    }
    
    
    int ast_pbx_outgoing_exten(const char *type, int format, void *data, int timeout, const char *context, const char *exten, int priority, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, struct ast_channel **channel)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *chan;
    	struct async_stat *as;
    
    	int res = -1, cdr_res = -1;
    
    	pthread_attr_t attr;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (sync) {
    
    		chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
    
    		if (channel) {
    			*channel = chan;
    			if (chan)
    				ast_mutex_lock(&chan->lock);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan) {
    
    			if(chan->cdr) { /* check if the channel already has a cdr record, if not give it one */
    				ast_log(LOG_WARNING, "%s already has a call record??\n", chan->name);
    			} else {
    				chan->cdr = ast_cdr_alloc();   /* allocate a cdr for the channel */
    
    					/* allocation of the cdr failed */
    					ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
    					free(chan->pbx);
    
    				}
    				/* allocation of the cdr was successful */
    				ast_cdr_init(chan->cdr, chan);  /* initilize our channel's cdr */
    				ast_cdr_start(chan->cdr);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (chan->_state == AST_STATE_UP) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (option_verbose > 3)
    					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (sync > 1) {
    
    					if (channel)
    						ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					if (ast_pbx_run(chan)) {
    
    						ast_log(LOG_ERROR, "Unable to run PBX on %s\n", chan->name);
    
    						if (channel)
    							*channel = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_hangup(chan);
    						res = -1;
    					}
    				} else {
    					if (ast_pbx_start(chan)) {
    
    						ast_log(LOG_ERROR, "Unable to start PBX on %s\n", chan->name);
    
    						if (channel)
    							*channel = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    						ast_hangup(chan);