Skip to content
Snippets Groups Projects
pbx.c 164 KiB
Newer Older
Mark Spencer's avatar
Mark Spencer committed
	struct ast_ignorepat *ip, *ipl = NULL;

Russell Bryant's avatar
Russell Bryant committed
	for (ip = con->ignorepats; ip; ip = ip->next) {
Mark Spencer's avatar
Mark Spencer committed
		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;
		}
Russell Bryant's avatar
Russell Bryant committed
		ipl = ip;
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 *context, const char *value, const char *registrar)
Mark Spencer's avatar
Mark Spencer committed
{
	int ret = -1;
	struct ast_context *c = find_context_locked(context);
	if (c) {
		ret = ast_context_add_ignorepat2(c, value, registrar);
		ast_unlock_contexts();
Mark Spencer's avatar
Mark Spencer committed
	}
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;
Mark Spencer's avatar
Mark Spencer committed
		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);
Mark Spencer's avatar
Mark Spencer committed
	ignorepat->next = NULL;
	ignorepat->registrar = registrar;
Russell Bryant's avatar
Russell Bryant committed
	for (ignorepatc = con->ignorepats; ignorepatc; ignorepatc = ignorepatc->next) {
Mark Spencer's avatar
Mark Spencer committed
		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;
Luigi Rizzo's avatar
Luigi Rizzo committed
	if (ignorepatl)
Mark Spencer's avatar
Mark Spencer committed
		ignorepatl->next = ignorepat;
Mark Spencer's avatar
Mark Spencer committed
	else
Mark Spencer's avatar
Mark Spencer committed
		con->ignorepats = ignorepat;
Mark Spencer's avatar
Mark Spencer committed
	return 0;
int ast_ignore_pattern(const char *context, const char *pattern)
Mark Spencer's avatar
Mark Spencer committed
{
Luigi Rizzo's avatar
Luigi Rizzo committed
	struct ast_context *con = ast_context_find(context);
Mark Spencer's avatar
Mark Spencer committed
	if (con) {
Luigi Rizzo's avatar
Luigi Rizzo committed
		struct ast_ignorepat *pat;
Russell Bryant's avatar
Russell Bryant committed
		for (pat = con->ignorepats; pat; pat = pat->next) {
Mark Spencer's avatar
Mark Spencer committed
			if (ast_extension_match(pat->pattern, pattern))
				return 1;
		}
Mark Spencer's avatar
Mark Spencer committed
	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
{
	int ret = -1;
	struct ast_context *c = find_context_locked(context);
	if (c) {
		ret = ast_add_extension2(c, replace, extension, priority, label, callerid,
			application, data, datad, registrar);
		ast_unlock_contexts();
Mark Spencer's avatar
Mark Spencer committed
	}
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;
Luigi Rizzo's avatar
Luigi Rizzo 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 */
Luigi Rizzo's avatar
Luigi Rizzo committed
		struct ast_channel *tmpchan = ast_channel_alloc(0);
		if (!tmpchan)
			res = -1;
		else {
			ast_string_field_build(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 */
Luigi Rizzo's avatar
Luigi Rizzo committed
				S_OR(context, chan->context), S_OR(exten, chan->exten), priority);
Mark Spencer's avatar
Mark Spencer committed
			/* Masquerade into temp channel */
			ast_channel_masquerade(tmpchan, chan);
			/* Grab the locks and get going */
			ast_channel_lock(tmpchan);
			ast_channel_unlock(tmpchan);
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;
			}
		}
	}
	ast_channel_unlock(chan);
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_channel_unlock(chan);
/*! \brief copy a string skipping whitespace */
static int ext_strncpy(char *dst, const char *src, int len)
Mark Spencer's avatar
Mark Spencer committed
{
	int count=0;
Russell Bryant's avatar
Russell Bryant committed
	while (*src && (count < len - 1)) {
Mark Spencer's avatar
Mark Spencer committed
		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';
/*! \brief add the extension in the priority chain.
 * returns 0 on success, -1 on failure
 */
static int add_pri(struct ast_context *con, struct ast_exten *tmp,
	struct ast_exten *el, struct ast_exten *e, int replace)
{
	struct ast_exten *ep;

	for (ep = NULL; e ; ep = e, e = e->peer) {
		if (e->priority >= tmp->priority)
			break;
	}
	if (!e) {	/* go at the end, and ep is surely set because the list is not empty */
		ep->peer = tmp;
		return 0;	/* success */
	}
	if (e->priority == tmp->priority) {
		/* Can't have something exactly the same.  Is this a
		   replacement?  If so, replace, otherwise, bonk. */
		if (!replace) {
			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);
			return -1;
		}
		/* we are replacing e, so copy the link fields and then update
		 * whoever pointed to e to point to us
		 */
		tmp->next = e->next;	/* not meaningful if we are not first in the peer list */
		tmp->peer = e->peer;	/* always meaningful */
		if (ep)			/* We're in the peer list, just insert ourselves */
			ep->peer = tmp;
		else if (el)		/* We're the first extension. Take over e's functions */
			el->next = tmp;
		else			/* We're the very first extension.  */
			con->root = tmp;
		if (tmp->priority == PRIORITY_HINT)
			ast_change_hint(e,tmp);
		/* Destroy the old one */
		e->datad(e->data);
		free(e);
	} else {	/* Slip ourselves in just before e */
		tmp->peer = e;
		tmp->next = e->next;	/* extension chain, or NULL if e is not the first extension */
		if (ep)			/* Easy enough, we're just in the peer list */
			ep->peer = tmp;
		else {			/* we are the first in some peer list, so link in the ext list */
			if (el)
				el->next = tmp;	/* in the middle... */
			else
				con->root = tmp; /* ... or at the head */
			e->next = NULL;	/* e is no more at the head, so e->next must be reset */
		}
		/* And immediately return success. */
		if (tmp->priority == PRIORITY_HINT)
			 ast_add_hint(tmp);
	}
	return 0;
}

/*! \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.
 *
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)
	 * 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..
Mark Spencer's avatar
Mark Spencer committed
	 */
	struct ast_exten *tmp, *e, *el = NULL;
Mark Spencer's avatar
Mark Spencer committed
	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
	*/
	ast_mutex_lock(&globalslock);
	if (priority == PRIORITY_HINT && AST_LIST_FIRST(&globals) && strstr(application, "${")) {
		pbx_substitute_variables_varshead(&globals, application, expand_buf, sizeof(expand_buf));
		application = expand_buf;
	}
	ast_mutex_unlock(&globalslock);
	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
Luigi Rizzo's avatar
Luigi Rizzo committed
		length ++;	/* just the '\0' */
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
		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 */
Luigi Rizzo's avatar
Luigi Rizzo committed
	for (e = con->root; e; el = e, e = e->next) {   /* scan the extension list */
		res = ext_cmp(e->exten, extension);
Luigi Rizzo's avatar
Luigi Rizzo committed
		if (res == 0) { /* extension match, now look at cidmatch */
Mark Spencer's avatar
Mark Spencer committed
			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);
Luigi Rizzo's avatar
Luigi Rizzo committed
		ast_mutex_unlock(&con->lock);
		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;
		ast_mutex_unlock(&con->lock);
		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);
		}
	} 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);
Luigi Rizzo's avatar
Luigi Rizzo committed
		}
Mark Spencer's avatar
Mark Spencer committed
	}
Luigi Rizzo's avatar
Luigi Rizzo committed
	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];
Luigi Rizzo's avatar
Luigi Rizzo committed
static void *async_wait(void *data)
Mark Spencer's avatar
Mark Spencer committed
{
	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;
Russell Bryant's avatar
Russell Bryant committed
	while (timeout && (chan->_state != AST_STATE_UP)) {
Mark Spencer's avatar
Mark Spencer committed
		res = ast_waitfor(chan, timeout);
Luigi Rizzo's avatar
Luigi Rizzo committed
		if (res < 1)
Mark Spencer's avatar
Mark Spencer committed
			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);
				pbx_exec(chan, app, as->appdata);
Mark Spencer's avatar
Mark Spencer committed
			} 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 post an empty cdr after a spool call fails.
 *  This function posts an empty cdr for a failed spool call
 *
 */
int ast_pbx_outgoing_cdr_failed(void)
{
	/* allocate a channel */
	struct ast_channel *chan = ast_channel_alloc(0);
		return -1;  /* failure */

	chan->cdr = ast_cdr_alloc();   /* allocate a cdr for the channel */

Russell Bryant's avatar
Russell Bryant committed
	if (!chan->cdr) {
		/* allocation of the cdr failed */
		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, const char *account, 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)
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 */
					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) {
						ast_channel_unlock(chan);
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) {
							ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
						ast_hangup(chan);
						res = -1;
Mark Spencer's avatar
Mark Spencer committed
				}
Mark Spencer's avatar
Mark Spencer committed
			} else {
				if (option_verbose > 3)
					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);

				if(chan->cdr) { /* update the cdr */
					/* here we update the status of the call, which sould be busy.
					 * if that fails then we set the status to failed */
					if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
						ast_cdr_failed(chan->cdr);
				}
				if (channel) {
					ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
				ast_hangup(chan);
			}
		if (res < 0) { /* the call failed for some reason */
			if (*reason == 0) { /* if the call failed (not busy or no answer)
				            * update the cdr with the failed message */
				cdr_res = ast_pbx_outgoing_cdr_failed();
				if (cdr_res != 0) {
					res = cdr_res;
					goto outgoing_exten_cleanup;
				}
Martin Pycko's avatar
 
Martin Pycko committed
			/* create a fake channel and execute the "failed" extension (if it exists) within the requested context */
			/* check if "failed" exists */
			if (ast_exists_extension(chan, context, "failed", 1, NULL)) {
				chan = ast_channel_alloc(0);
					ast_string_field_set(chan, name, "OutgoingSpoolFailed");
						ast_copy_string(chan->context, context, sizeof(chan->context));
					set_ext_pri(chan, "failed", 1);
					if (account)
						ast_cdr_setaccount(chan, account);
Luigi Rizzo's avatar
Luigi Rizzo committed
					ast_pbx_run(chan);
				}
Martin Pycko's avatar
 
Martin Pycko committed
			}
Mark Spencer's avatar
Mark Spencer committed
		}
	} else {
		chan = ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name);
		if (channel) {
			*channel = chan;
			if (chan)
Mark Spencer's avatar
Mark Spencer committed
		if (!chan) {
			free(as);
Mark Spencer's avatar
Mark Spencer committed
		}
		as->chan = chan;
		ast_copy_string(as->context, context, sizeof(as->context));
		set_ext_pri(as->chan,  exten, priority);
Mark Spencer's avatar
Mark Spencer committed
		as->timeout = timeout;
		if (account)
			ast_cdr_setaccount(chan, account);
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
		if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
Mark Spencer's avatar
Mark Spencer committed
			ast_log(LOG_WARNING, "Failed to start async wait\n");
			free(as);
			if (channel) {
				ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
			ast_hangup(chan);
Mark Spencer's avatar
Mark Spencer committed
		}
		res = 0;
	}
outgoing_exten_cleanup:
	ast_variables_destroy(vars);
Mark Spencer's avatar
Mark Spencer committed
	return res;
}

Mark Spencer's avatar
Mark Spencer committed
struct app_tmp {
	char app[256];
	char data[256];
	struct ast_channel *chan;
	pthread_t t;
};

/*! \brief run the application and free the descriptor once done */
Mark Spencer's avatar
Mark Spencer committed
static void *ast_pbx_run_app(void *data)
{
	struct app_tmp *tmp = data;
	struct ast_app *app;
	app = pbx_findapp(tmp->app);
	if (app) {
		if (option_verbose > 3)
			ast_verbose(VERBOSE_PREFIX_4 "Launching %s(%s) on %s\n", tmp->app, tmp->data, tmp->chan->name);
		pbx_exec(tmp->chan, app, tmp->data);
Mark Spencer's avatar
Mark Spencer committed
	} else
		ast_log(LOG_WARNING, "No such application '%s'\n", tmp->app);
	ast_hangup(tmp->chan);
	free(tmp);
	return NULL;
}

int ast_pbx_outgoing_app(const char *type, int format, void *data, int timeout, const char *app, const char *appdata, int *reason, int sync, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_channel *chan;
	struct app_tmp *tmp;
	int res = -1, cdr_res = -1;
	pthread_attr_t attr;
Luigi Rizzo's avatar
Luigi Rizzo committed
	oh.account = account;
Luigi Rizzo's avatar
Luigi Rizzo committed
	if (locked_channel)
Luigi Rizzo's avatar
Luigi Rizzo committed
		goto outgoing_app_cleanup;
Mark Spencer's avatar
Mark Spencer committed
	if (sync) {
		chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
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 */
				if(!chan->cdr) {
					/* allocation of the cdr failed */
					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);
			}
			if (account)
				ast_cdr_setaccount(chan, account);
Mark Spencer's avatar
Mark Spencer committed
			if (chan->_state == AST_STATE_UP) {
				res = 0;
				if (option_verbose > 3)
					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was answered.\n", chan->name);
				tmp = ast_calloc(1, sizeof(*tmp));
				if (!tmp)
					res = -1;
				else {
					ast_copy_string(tmp->app, app, sizeof(tmp->app));
					if (appdata)
						ast_copy_string(tmp->data, appdata, sizeof(tmp->data));
Mark Spencer's avatar
Mark Spencer committed
					tmp->chan = chan;
					if (sync > 1) {
						if (locked_channel)
							ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
						ast_pbx_run_app(tmp);
					} else {
						pthread_attr_init(&attr);
						pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Luigi Rizzo's avatar
Luigi Rizzo committed
						if (locked_channel)
						if (ast_pthread_create(&tmp->t, &attr, ast_pbx_run_app, tmp)) {
Mark Spencer's avatar
Mark Spencer committed
							ast_log(LOG_WARNING, "Unable to spawn execute thread on %s: %s\n", chan->name, strerror(errno));
							free(tmp);
Luigi Rizzo's avatar
Luigi Rizzo committed
							if (locked_channel)
								ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
							ast_hangup(chan);
							res = -1;
Luigi Rizzo's avatar
Luigi Rizzo committed
							if (locked_channel)
Mark Spencer's avatar
Mark Spencer committed
						}
					}
				}
			} else {
				if (option_verbose > 3)
					ast_verbose(VERBOSE_PREFIX_4 "Channel %s was never answered.\n", chan->name);
				if (chan->cdr) { /* update the cdr */
					/* here we update the status of the call, which sould be busy.
					 * if that fails then we set the status to failed */
					if (ast_cdr_disposition(chan->cdr, chan->hangupcause))
						ast_cdr_failed(chan->cdr);
				}
Mark Spencer's avatar
Mark Spencer committed
				ast_hangup(chan);
			}
		}
		if (res < 0) { /* the call failed for some reason */
			if (*reason == 0) { /* if the call failed (not busy or no answer)
				            * update the cdr with the failed message */
				cdr_res = ast_pbx_outgoing_cdr_failed();
				if (cdr_res != 0) {
					res = cdr_res;
					goto outgoing_app_cleanup;
				}
Mark Spencer's avatar
Mark Spencer committed
	} else {
		chan = __ast_request_and_dial(type, format, data, timeout, reason, cid_num, cid_name, &oh);
Mark Spencer's avatar
Mark Spencer committed
		if (!chan) {
			free(as);
Mark Spencer's avatar
Mark Spencer committed
		}
		as->chan = chan;
		ast_copy_string(as->app, app, sizeof(as->app));
Mark Spencer's avatar
Mark Spencer committed
		if (appdata)
			ast_copy_string(as->appdata,  appdata, sizeof(as->appdata));
Mark Spencer's avatar
Mark Spencer committed
		as->timeout = timeout;
		if (account)
			ast_cdr_setaccount(chan, account);
		/* Start a new thread, and get something handling this channel. */
		pthread_attr_init(&attr);
		pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
Luigi Rizzo's avatar
Luigi Rizzo committed
		if (locked_channel)
		if (ast_pthread_create(&as->p, &attr, async_wait, as)) {
Mark Spencer's avatar
Mark Spencer committed
			ast_log(LOG_WARNING, "Failed to start async wait\n");
			free(as);
Luigi Rizzo's avatar
Luigi Rizzo committed
			if (locked_channel)
				ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
			ast_hangup(chan);
		} else {
			if (locked_channel)
				*locked_channel = chan;
Mark Spencer's avatar
Mark Spencer committed
		}
		res = 0;
	}
outgoing_app_cleanup:
	ast_variables_destroy(vars);
Mark Spencer's avatar
Mark Spencer committed
	return res;
}

void __ast_context_destroy(struct ast_context *con, const char *registrar)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_context *tmp, *tmpl=NULL;
Luigi Rizzo's avatar
Luigi Rizzo committed
	struct ast_include *tmpi;
	struct ast_exten *e, *el, *en;
Luigi Rizzo's avatar
Luigi Rizzo committed
	struct ast_ignorepat *ipi;
Luigi Rizzo's avatar
Luigi Rizzo committed
	for (tmp = contexts; tmp; ) {
		struct ast_context *next;	/* next starting point */
		for (; tmp; tmpl = tmp, tmp = tmp->next) {
			ast_log(LOG_DEBUG, "check ctx %s %s\n", tmp->name, tmp->registrar);
Luigi Rizzo's avatar
Luigi Rizzo committed
			if ( (!registrar || !strcasecmp(registrar, tmp->registrar)) &&
			     (!con || !strcasecmp(tmp->name, con->name)) )
				break;	/* found it */
		}
		if (!tmp)	/* not found, we are done */
			break;
		ast_mutex_lock(&tmp->lock);
		ast_log(LOG_DEBUG, "delete ctx %s %s\n", tmp->name, tmp->registrar);
Luigi Rizzo's avatar
Luigi Rizzo committed
		next = tmp->next;
		if (tmpl)
			tmpl->next = next;
		else
			contexts = next;
		/* Okay, now we're safe to let it go -- in a sense, we were
		   ready to let it go as soon as we locked it. */
		ast_mutex_unlock(&tmp->lock);
		for (tmpi = tmp->includes; tmpi; ) { /* Free includes */
			struct ast_include *tmpil = tmpi;
			tmpi = tmpi->next;
			free(tmpil);
		}
		for (ipi = tmp->ignorepats; ipi; ) { /* Free ignorepats */
			struct ast_ignorepat *ipl = ipi;
			ipi = ipi->next;
			free(ipl);
		}
		while ((sw = AST_LIST_REMOVE_HEAD(&tmp->alts, list)))
			free(sw);
		for (e = tmp->root; e;) {
			for (en = e->peer; en;) {
				el = en;
				en = en->peer;
Luigi Rizzo's avatar
Luigi Rizzo committed
			el = e;
			e = e->next;
			destroy_exten(el);
Mark Spencer's avatar
Mark Spencer committed
		}
Luigi Rizzo's avatar
Luigi Rizzo committed
		ast_mutex_destroy(&tmp->lock);
		free(tmp);
		/* if we have a specific match, we are done, otherwise continue */
		tmp = con ? NULL : next;
Mark Spencer's avatar
Mark Spencer committed
	}
void ast_context_destroy(struct ast_context *con, const char *registrar)
static void wait_for_hangup(struct ast_channel *chan, void *data)
Mark Spencer's avatar
Mark Spencer committed
{
	int res;
	struct ast_frame *f;
	if (ast_strlen_zero(data) || (sscanf(data, "%d", &waittime) != 1) || (waittime < 0))
		waittime = -1;
	if (waittime > -1) {
		ast_safe_sleep(chan, waittime * 1000);
	} else do {
Mark Spencer's avatar
Mark Spencer committed
		res = ast_waitfor(chan, -1);
		if (res < 0)
			return;
		f = ast_read(chan);
		if (f)
			ast_frfree(f);
	} while(f);
}

/*!
 * \ingroup applications
 */
Mark Spencer's avatar
Mark Spencer committed
static int pbx_builtin_progress(struct ast_channel *chan, void *data)
{
	ast_indicate(chan, AST_CONTROL_PROGRESS);
	return 0;
}

/*!
 * \ingroup applications
 */
Mark Spencer's avatar
Mark Spencer committed
static int pbx_builtin_ringing(struct ast_channel *chan, void *data)
{
	ast_indicate(chan, AST_CONTROL_RINGING);
	return 0;
}

/*!
 * \ingroup applications
 */
Mark Spencer's avatar
Mark Spencer committed
static int pbx_builtin_busy(struct ast_channel *chan, void *data)
{
Luigi Rizzo's avatar
Luigi Rizzo committed
	ast_indicate(chan, AST_CONTROL_BUSY);
	wait_for_hangup(chan, data);
Mark Spencer's avatar
Mark Spencer committed
	return -1;
}

/*!
 * \ingroup applications
 */
Mark Spencer's avatar
Mark Spencer committed
static int pbx_builtin_congestion(struct ast_channel *chan, void *data)
{
	ast_indicate(chan, AST_CONTROL_CONGESTION);
	wait_for_hangup(chan, data);
Mark Spencer's avatar
Mark Spencer committed
	return -1;
}

/*!
 * \ingroup applications
 */
Mark Spencer's avatar
Mark Spencer committed
static int pbx_builtin_answer(struct ast_channel *chan, void *data)
Mark Spencer's avatar
Mark Spencer committed
{
	if (chan->_state == AST_STATE_UP)
		delay = 0;
	else if (!ast_strlen_zero(data))
		delay = atoi(data);

	res = ast_answer(chan);
	if (res)
		return res;
	if (delay)
		res = ast_safe_sleep(chan, delay);
AST_APP_OPTIONS(resetcdr_opts, {
	AST_APP_OPTION('w', AST_CDR_FLAG_POSTED),
	AST_APP_OPTION('a', AST_CDR_FLAG_LOCKED),
	AST_APP_OPTION('v', AST_CDR_FLAG_KEEP_VARS),
});

/*!
 * \ingroup applications
 */
static int pbx_builtin_resetcdr(struct ast_channel *chan, void *data)
{
	char *args;
	struct ast_flags flags = { 0 };
		ast_app_parse_options(resetcdr_opts, &flags, NULL, args);
	}

	ast_cdr_reset(chan->cdr, &flags);