Originated calls: Fix several originate call problems.

* Restore the reason value set by pbx_outgoing_attempt() to use
AST_CONTROL_xxx values as all the consumers were expecting rather than
cause codes.

* Fixed the dial routines to set cause codes for more than just
ast_request() so pbx_outgoing_attempt() reason codes will function.

* Fix inconsistent locked_channel return status in pbx_outgoing_attempt().
The chanel may not have been locked or the channel may have been a stale
pointer.

* Fixed the OutgoingSpoolFailed channel to run dialplan whenever the
dialing fails for an originate exten and 1 < synchronous.

* Fix incorrect ast_cond_wait() usage in pbx_outgoing_attempt().
Indroduced by issue ASTERISK-22212 patch.

* Made struct pbx_outgoing use the ao2 lock instead of its own lock for
the cond wait mutex.  No sense in having two locks associated with the
same struct when only one is needed.

Review: https://reviewboard.asterisk.org/r/3421/
........

Merged revisions 412581 from http://svn.asterisk.org/svn/asterisk/branches/12


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@412583 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Richard Mudgett 2014-04-18 16:44:48 +00:00
parent cbe7f65674
commit 51b6c49681
4 changed files with 224 additions and 109 deletions

View File

@ -177,14 +177,14 @@ static int originate_exec(struct ast_channel *chan, const char *data)
chantech, chandata, args.arg1, exten, priority);
ast_pbx_outgoing_exten(chantech, cap_slin, chandata,
timeout * 1000, args.arg1, exten, priority, &outgoing_status, 0, NULL,
timeout * 1000, args.arg1, exten, priority, &outgoing_status, 1, NULL,
NULL, NULL, NULL, NULL, 0, NULL);
} else if (!strcasecmp(args.type, "app")) {
ast_debug(1, "Originating call to '%s/%s' and connecting them to %s(%s)\n",
chantech, chandata, args.arg1, S_OR(args.arg2, ""));
ast_pbx_outgoing_app(chantech, cap_slin, chandata,
timeout * 1000, args.arg1, args.arg2, &outgoing_status, 0, NULL,
timeout * 1000, args.arg1, args.arg2, &outgoing_status, 1, NULL,
NULL, NULL, NULL, NULL, NULL);
} else {
ast_log(LOG_ERROR, "Incorrect type, it should be 'exten' or 'app': %s\n",

View File

@ -1102,7 +1102,8 @@ int ast_async_goto(struct ast_channel *chan, const char *context, const char *ex
*/
int ast_async_goto_by_name(const char *chan, const char *context, const char *exten, int priority);
/*! \brief Synchronously or asynchronously make an outbound call and send it to a
/*!
* \brief Synchronously or asynchronously make an outbound call and send it to a
* particular extension
*
* \param type The channel technology to create
@ -1112,27 +1113,34 @@ int ast_async_goto_by_name(const char *chan, const char *context, const char *ex
* \param context The destination context for the outbound channel
* \param exten The destination extension for the outbound channel
* \param priority The destination priority for the outbound channel
* \param reason Optional. If provided, the hangup cause code of the outbound channel if
* it failed
* \param sync If non-zero, block until the outbound channel answers
* \param reason Optional. If provided, the dialed status of the outgoing channel.
* Codes are AST_CONTROL_xxx values. Valid only if synchronous is non-zero.
* \param synchronous If zero then don't wait for anything.
* If one then block until the outbound channel answers or the call fails.
* If greater than one then wait for the call to complete or if the call doesn't
* answer and failed@context exists then run a channel named OutgoingSpoolFailed
* at failed@context.
* \param cid_num The caller ID number to set on the outbound channel
* \param cid_name The caller ID name to set on the outbound channel
* \param vars Variables to set on the outbound channel
* \param account The accountcode for the outbound channel
* \param locked_channel Optional. The outbound channel that was created. This is returned
* both locked and reference bumped. If a caller provides a channel parameter, it must
* unlock the channel and decrement the reference count.
* \param assignedid Optional. The uniqueid to assign the channel that was created.
* \param assignedid2 Optional. The uniqueid to assign the second local channel.
* \param early_media If non-zero, allow early-media on the originated channel
* \param locked_channel Optional. The outbound channel that was created if success
* is returned. Otherwise it is set to NULL. This is returned both locked
* and reference bumped.
* \param early_media If non-zero the channel "answers" when progress is indicated.
* \param assignedids Optional. The uniqueid(s) to assign the channel(s) that are created.
*
* \retval 0 on success
* \retval -1 on failure
*/
int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr,
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 **locked_channel, int early_media,
int timeout, const char *context, const char *exten, int priority, int *reason,
int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars,
const char *account, struct ast_channel **locked_channel, int early_media,
const struct ast_assigned_ids *assignedids);
/*! \brief Synchronously or asynchronously make an outbound call and execute an
/*!
* \brief Synchronously or asynchronously make an outbound call and execute an
* application on the channel.
*
* Note that when the application stops executing, the channel is hungup.
@ -1143,23 +1151,27 @@ int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const c
* \param timeout How long we should attempt to dial the outbound channel
* \param app The name of the application to execute
* \param appdata Data to pass to the application
* \param reason Optional. If provided, the hangup cause code of the outbound channel if
* it failed
* \param sync If non-zero, block until the outbound channel answers
* \param reason Optional. If provided, the dialed status of the outgoing channel.
* Codes are AST_CONTROL_xxx values. Valid only if synchronous is non-zero.
* \param synchronous If zero then don't wait for anything.
* If one then block until the outbound channel answers or the call fails.
* If greater than one then wait for the call to complete.
* \param cid_num The caller ID number to set on the outbound channel
* \param cid_name The caller ID name to set on the outbound channel
* \param vars Variables to set on the outbound channel
* \param account The accountcode for the outbound channel
* \param locked_channel Optional. The outbound channel that was created. This is returned
* \param assignedid Optional. The uniqueid to assign the channel that was created.
* \param assignedid2 Optional. The uniqueid to assign the second local channel.
* both locked and reference bumped. If a caller provides a channel parameter, it must
* unlock the channel and decrement the reference count.
* \param locked_channel Optional. The outbound channel that was created if success
* is returned. Otherwise it is set to NULL. This is returned both locked
* and reference bumped.
* \param assignedids Optional. The uniqueid(s) to assign the channel(s) that are created.
*
* \retval 0 on success
* \retval -1 on failure
*/
int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr,
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,
int timeout, const char *app, const char *appdata, int *reason, int synchronous,
const char *cid_num, const char *cid_name, struct ast_variable *vars,
const char *account, struct ast_channel **locked_channel,
const struct ast_assigned_ids *assignedids);
/*!

View File

@ -541,12 +541,14 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel
ast_verb(3, "%s is busy\n", ast_channel_name(channel->owner));
ast_channel_publish_dial(chan, channel->owner, channel->device, "BUSY");
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_USER_BUSY;
channel->owner = NULL;
break;
case AST_CONTROL_CONGESTION:
ast_verb(3, "%s is circuit-busy\n", ast_channel_name(channel->owner));
ast_channel_publish_dial(chan, channel->owner, channel->device, "CONGESTION");
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
channel->owner = NULL;
break;
case AST_CONTROL_INCOMPLETE:
@ -593,7 +595,7 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel
break;
case AST_CONTROL_HOLD:
ast_verb(3, "Call on %s placed on hold\n", ast_channel_name(chan));
ast_indicate(chan, AST_CONTROL_HOLD);
ast_indicate_data(chan, AST_CONTROL_HOLD, fr->data.ptr, fr->datalen);
break;
case AST_CONTROL_UNHOLD:
ast_verb(3, "Call on %s left from hold\n", ast_channel_name(chan));
@ -613,8 +615,6 @@ static void handle_frame(struct ast_dial *dial, struct ast_dial_channel *channel
break;
}
}
return;
}
/*! \brief Helper function that handles control frames WITHOUT owner */
@ -638,12 +638,25 @@ static void handle_frame_ownerless(struct ast_dial *dial, struct ast_dial_channe
ast_verb(3, "%s is busy\n", ast_channel_name(channel->owner));
ast_channel_publish_dial(NULL, channel->owner, channel->device, "BUSY");
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_USER_BUSY;
channel->owner = NULL;
break;
case AST_CONTROL_CONGESTION:
ast_verb(3, "%s is circuit-busy\n", ast_channel_name(channel->owner));
ast_channel_publish_dial(NULL, channel->owner, channel->device, "CONGESTION");
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_NORMAL_CIRCUIT_CONGESTION;
channel->owner = NULL;
break;
case AST_CONTROL_INCOMPLETE:
/*
* Nothing to do but abort the call since we have no
* controlling channel to ask for more digits.
*/
ast_verb(3, "%s dialed Incomplete extension %s\n",
ast_channel_name(channel->owner), ast_channel_exten(channel->owner));
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_UNALLOCATED;
channel->owner = NULL;
break;
case AST_CONTROL_RINGING:
@ -661,8 +674,6 @@ static void handle_frame_ownerless(struct ast_dial *dial, struct ast_dial_channe
default:
break;
}
return;
}
/*! \brief Helper function to handle when a timeout occurs on dialing attempt */
@ -686,6 +697,7 @@ static int handle_timeout_trip(struct ast_dial *dial, struct timeval start)
AST_LIST_TRAVERSE(&dial->channels, channel, list) {
if (dial->state == AST_DIAL_RESULT_TIMEOUT || diff >= channel->timeout) {
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_NO_ANSWER;
channel->owner = NULL;
} else if ((lowest_timeout == -1) || (lowest_timeout > channel->timeout)) {
lowest_timeout = channel->timeout;
@ -835,6 +847,7 @@ static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_chann
ast_poll_channel_del(chan, channel->owner);
ast_channel_publish_dial(chan, channel->owner, channel->device, "CANCEL");
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_ANSWERED_ELSEWHERE;
channel->owner = NULL;
}
AST_LIST_UNLOCK(&dial->channels);
@ -859,6 +872,7 @@ static enum ast_dial_result monitor_dial(struct ast_dial *dial, struct ast_chann
ast_poll_channel_del(chan, channel->owner);
ast_channel_publish_dial(chan, channel->owner, channel->device, "CANCEL");
ast_hangup(channel->owner);
channel->cause = AST_CAUSE_NORMAL_CLEARING;
channel->owner = NULL;
}
AST_LIST_UNLOCK(&dial->channels);

View File

@ -10092,8 +10092,6 @@ static int ast_add_extension2_lockopt(struct ast_context *con,
struct pbx_outgoing {
/*! \brief Dialing structure being used */
struct ast_dial *dial;
/*! \brief Mutex lock for synchronous dialing */
ast_mutex_t lock;
/*! \brief Condition for synchronous dialing */
ast_cond_t cond;
/*! \brief Application to execute */
@ -10123,7 +10121,6 @@ static void pbx_outgoing_destroy(void *obj)
ast_dial_destroy(outgoing->dial);
}
ast_mutex_destroy(&outgoing->lock);
ast_cond_destroy(&outgoing->cond);
ast_free(outgoing->appdata);
@ -10136,12 +10133,12 @@ static void *pbx_outgoing_exec(void *data)
enum ast_dial_result res;
/* Notify anyone interested that dialing is complete */
ast_mutex_lock(&outgoing->lock);
res = ast_dial_run(outgoing->dial, NULL, 0);
ao2_lock(outgoing);
outgoing->dial_res = res;
outgoing->dialed = 1;
ast_cond_signal(&outgoing->cond);
ast_mutex_unlock(&outgoing->lock);
ao2_unlock(outgoing);
/* If the outgoing leg was not answered we can immediately return and go no further */
if (res != AST_DIAL_RESULT_ANSWERED) {
@ -10182,10 +10179,10 @@ static void *pbx_outgoing_exec(void *data)
}
/* Notify anyone else again that may be interested that execution is complete */
ast_mutex_lock(&outgoing->lock);
ao2_lock(outgoing);
outgoing->executed = 1;
ast_cond_signal(&outgoing->cond);
ast_mutex_unlock(&outgoing->lock);
ao2_unlock(outgoing);
return NULL;
}
@ -10209,21 +10206,69 @@ static void pbx_outgoing_state_callback(struct ast_dial *dial)
ast_queue_control(channel, AST_CONTROL_ANSWER);
}
static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context,
const char *exten, int priority, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num,
const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, int early_media, const struct ast_assigned_ids *assignedids)
/*!
* \brief Attempt to convert disconnect cause to old originate reason.
*
* \todo XXX The old originate reasons need to be trashed and replaced
* with normal disconnect cause codes if the call was not answered.
* The internal consumers of the reason values would also need to be
* updated: app_originate, call files, and AMI OriginateResponse.
*/
static enum ast_control_frame_type pbx_dial_reason(enum ast_dial_result dial_result, int cause)
{
RAII_VAR(struct pbx_outgoing *, outgoing, ao2_alloc(sizeof(*outgoing), pbx_outgoing_destroy), ao2_cleanup);
enum ast_control_frame_type pbx_reason;
if (dial_result == AST_DIAL_RESULT_ANSWERED) {
/* Remote end answered. */
pbx_reason = AST_CONTROL_ANSWER;
} else if (dial_result == AST_DIAL_RESULT_HANGUP) {
/* Caller hungup */
pbx_reason = AST_CONTROL_HANGUP;
} else {
switch (cause) {
case AST_CAUSE_USER_BUSY:
pbx_reason = AST_CONTROL_BUSY;
break;
case AST_CAUSE_CALL_REJECTED:
case AST_CAUSE_NETWORK_OUT_OF_ORDER:
case AST_CAUSE_DESTINATION_OUT_OF_ORDER:
case AST_CAUSE_NORMAL_TEMPORARY_FAILURE:
case AST_CAUSE_SWITCH_CONGESTION:
case AST_CAUSE_NORMAL_CIRCUIT_CONGESTION:
pbx_reason = AST_CONTROL_CONGESTION;
break;
case AST_CAUSE_ANSWERED_ELSEWHERE:
case AST_CAUSE_NO_ANSWER:
/* Remote end was ringing (but isn't anymore) */
pbx_reason = AST_CONTROL_RINGING;
break;
case AST_CAUSE_UNALLOCATED:
default:
/* Call Failure (not BUSY, and not NO_ANSWER, maybe Circuit busy or down?) */
pbx_reason = 0;
break;
}
}
return pbx_reason;
}
static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap,
const char *addr, int timeout, const char *context, const char *exten, int priority,
const char *app, const char *appdata, int *reason, int synchronous,
const char *cid_num, const char *cid_name, struct ast_variable *vars,
const char *account, struct ast_channel **locked_channel, int early_media,
const struct ast_assigned_ids *assignedids)
{
RAII_VAR(struct pbx_outgoing *, outgoing, NULL, ao2_cleanup);
struct ast_channel *dialed;
pthread_t thread;
outgoing = ao2_alloc(sizeof(*outgoing), pbx_outgoing_destroy);
if (!outgoing) {
return -1;
}
if (channel) {
*channel = NULL;
}
ast_cond_init(&outgoing->cond, NULL);
if (!ast_strlen_zero(app)) {
ast_copy_string(outgoing->app, app, sizeof(outgoing->app));
@ -10245,6 +10290,10 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co
ast_dial_set_global_timeout(outgoing->dial, timeout);
if (ast_dial_prerun(outgoing->dial, NULL, cap)) {
if (synchronous && reason) {
*reason = pbx_dial_reason(AST_DIAL_RESULT_FAILED,
ast_dial_reason(outgoing->dial, 0));
}
return -1;
}
@ -10257,7 +10306,6 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co
if (vars) {
ast_set_variables(dialed, vars);
}
if (account) {
ast_channel_accountcode_set(dialed, account);
}
@ -10295,107 +10343,148 @@ static int pbx_outgoing_attempt(const char *type, struct ast_format_cap *cap, co
ast_dial_set_state_callback(outgoing->dial, pbx_outgoing_state_callback);
}
if (channel) {
*channel = dialed;
ast_channel_ref(*channel);
ast_channel_lock(*channel);
if (locked_channel) {
/*
* Keep a dialed channel ref since the caller wants
* the channel returned. We must get the ref before
* spawning off pbx_outgoing_exec().
*/
ast_channel_ref(dialed);
if (!synchronous) {
/*
* Lock it now to hold off pbx_outgoing_exec() in case the
* calling function needs the channel state/snapshot before
* dialing actually happens.
*/
ast_channel_lock(dialed);
}
}
ast_mutex_init(&outgoing->lock);
ast_cond_init(&outgoing->cond, NULL);
ao2_ref(outgoing, +1);
ast_mutex_lock(&outgoing->lock);
if (ast_pthread_create_detached(&thread, NULL, pbx_outgoing_exec, outgoing)) {
ast_log(LOG_WARNING, "Unable to spawn dialing thread for '%s/%s'\n", type, addr);
if (channel) {
ast_channel_unlock(*channel);
ast_channel_unref(*channel);
}
ast_mutex_unlock(&outgoing->lock);
ao2_ref(outgoing, -1);
if (locked_channel) {
if (!synchronous) {
ast_channel_unlock(dialed);
}
ast_channel_unref(dialed);
}
return -1;
}
/* Wait for dialing to complete */
if (synchronous) {
if (channel && *channel) {
ast_channel_unlock(*channel);
}
ao2_lock(outgoing);
/* Wait for dialing to complete */
while (!outgoing->dialed) {
ast_cond_wait(&outgoing->cond, &outgoing->lock);
if (outgoing->dial_res != AST_DIAL_RESULT_ANSWERED) {
ast_mutex_unlock(&outgoing->lock);
/* The dial operation failed. */
return -1;
ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing));
}
if (1 < synchronous
&& outgoing->dial_res == AST_DIAL_RESULT_ANSWERED) {
/* Wait for execution to complete */
while (!outgoing->executed) {
ast_cond_wait(&outgoing->cond, ao2_object_get_lockaddr(outgoing));
}
}
if (channel && *channel) {
ast_channel_lock(*channel);
ao2_unlock(outgoing);
/* Determine the outcome of the dialing attempt up to it being answered. */
if (reason) {
*reason = pbx_dial_reason(outgoing->dial_res,
ast_dial_reason(outgoing->dial, 0));
}
if (outgoing->dial_res != AST_DIAL_RESULT_ANSWERED) {
/* The dial operation failed. */
if (locked_channel) {
ast_channel_unref(dialed);
}
return -1;
}
if (locked_channel) {
ast_channel_lock(dialed);
}
}
/* Wait for execution to complete */
if (synchronous > 1) {
while (!outgoing->executed) {
ast_cond_wait(&outgoing->cond, &outgoing->lock);
}
if (locked_channel) {
*locked_channel = dialed;
}
return 0;
}
int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr,
int timeout, const char *context, const char *exten, int priority, int *reason,
int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars,
const char *account, struct ast_channel **locked_channel, int early_media,
const struct ast_assigned_ids *assignedids)
{
int res;
int my_reason;
if (!reason) {
reason = &my_reason;
}
*reason = 0;
if (locked_channel) {
*locked_channel = NULL;
}
ast_mutex_unlock(&outgoing->lock);
res = pbx_outgoing_attempt(type, cap, addr, timeout, context, exten, priority,
NULL, NULL, reason, synchronous, cid_num, cid_name, vars, account, locked_channel,
early_media, assignedids);
if (reason) {
*reason = ast_dial_reason(outgoing->dial, 0);
}
if (res < 0 /* Call failed to get connected for some reason. */
&& 1 < synchronous
&& ast_exists_extension(NULL, context, "failed", 1, NULL)) {
struct ast_channel *failed;
if ((synchronous > 1) && ast_dial_state(outgoing->dial) != AST_DIAL_RESULT_ANSWERED &&
ast_strlen_zero(app) && ast_exists_extension(NULL, context, "failed", 1, NULL)) {
struct ast_channel *failed = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", NULL, NULL, 0, "OutgoingSpoolFailed");
/* We do not have to worry about a locked_channel if dialing failed. */
ast_assert(!locked_channel || !*locked_channel);
/*!
* \todo XXX Not good. The channel name is not unique if more than
* one originate fails at a time.
*/
failed = ast_channel_alloc(0, AST_STATE_DOWN, cid_num, cid_name, account,
"failed", context, NULL, NULL, 0, "OutgoingSpoolFailed");
if (failed) {
char failed_reason[4] = "";
char failed_reason[12];
if (!ast_strlen_zero(context)) {
ast_channel_context_set(failed, context);
}
if (account) {
ast_channel_accountcode_set(failed, account);
}
set_ext_pri(failed, "failed", 1);
ast_set_variables(failed, vars);
snprintf(failed_reason, sizeof(failed_reason), "%d", ast_dial_reason(outgoing->dial, 0));
snprintf(failed_reason, sizeof(failed_reason), "%d", *reason);
pbx_builtin_setvar_helper(failed, "REASON", failed_reason);
ast_channel_unlock(failed);
if (ast_pbx_run(failed)) {
ast_log(LOG_ERROR, "Unable to run PBX on '%s'\n", ast_channel_name(failed));
ast_log(LOG_ERROR, "Unable to run PBX on '%s'\n",
ast_channel_name(failed));
ast_hangup(failed);
}
}
}
return 0;
return res;
}
int ast_pbx_outgoing_exten(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *context, const char *exten, int priority, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **channel, int early_media, const struct ast_assigned_ids *assignedids)
{
return pbx_outgoing_attempt(type, cap, addr, timeout, context, exten, priority, NULL, NULL, reason, synchronous, cid_num,
cid_name, vars, account, channel, early_media, assignedids);
}
int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr, int timeout, const char *app, const char *appdata, int *reason, int synchronous, const char *cid_num, const char *cid_name, struct ast_variable *vars, const char *account, struct ast_channel **locked_channel, const struct ast_assigned_ids *assignedids)
int ast_pbx_outgoing_app(const char *type, struct ast_format_cap *cap, const char *addr,
int timeout, const char *app, const char *appdata, int *reason, int synchronous,
const char *cid_num, const char *cid_name, struct ast_variable *vars,
const char *account, struct ast_channel **locked_channel,
const struct ast_assigned_ids *assignedids)
{
if (reason) {
*reason = 0;
}
if (locked_channel) {
*locked_channel = NULL;
}
if (ast_strlen_zero(app)) {
return -1;
}
return pbx_outgoing_attempt(type, cap, addr, timeout, NULL, NULL, 0, app, appdata, reason, synchronous, cid_num,
cid_name, vars, account, locked_channel, 0, assignedids);
return pbx_outgoing_attempt(type, cap, addr, timeout, NULL, NULL, 0, app, appdata,
reason, synchronous, cid_num, cid_name, vars, account, locked_channel, 0,
assignedids);
}
/* this is the guts of destroying a context --