static int app_control_remove_channel_from_bridge( struct stasis_app_control *control, struct ast_channel *chan, void *data) { struct ast_bridge *bridge = data; if (!control) { return -1; } /* We should only depart from our own bridge */ ast_debug(3, "%s: Departing bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); if (bridge != stasis_app_get_bridge(control)) { ast_log(LOG_WARNING, "%s: Not in bridge %s; not removing\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); return -1; } ast_bridge_depart(chan); return 0; }
/*! * \brief Depart a channel from a bridge, and potentially add it back to the dial bridge * * \param control Take a guess * \param chan Take another guess */ static int depart_channel(struct stasis_app_control *control, struct ast_channel *chan) { ast_bridge_depart(chan); if (!ast_check_hangup(chan) && ast_channel_state(chan) != AST_STATE_UP) { /* Channel is still being dialed, so put it back in the dialing bridge */ add_to_dial_bridge(control, chan); } return 0; }
static int app_control_continue(struct stasis_app_control *control, struct ast_channel *chan, void *data) { RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free); ast_assert(control->channel != NULL); /* If we're in a Stasis bridge, depart it before going back to the * dialplan */ if (stasis_app_get_bridge(control)) { ast_bridge_depart(control->channel); } /* Called from stasis_app_exec thread; no lock needed */ ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority); control->is_done = 1; return 0; }
static int bridge_channel_depart(struct stasis_app_control *control, struct ast_channel *chan, void *data) { RAII_VAR(struct ast_bridge_channel *, bridge_channel, data, ao2_cleanup); { SCOPED_CHANNELLOCK(lock, chan); if (bridge_channel != ast_channel_internal_bridge_channel(chan)) { ast_debug(3, "%s: Channel is no longer in departable state\n", ast_channel_uniqueid(chan)); return -1; } } ast_debug(3, "%s: Channel departing bridge\n", ast_channel_uniqueid(chan)); ast_bridge_depart(chan); return 0; }
void sccpconf_announce_channel_depart(struct ast_channel *chan) { struct announce_pvt *p = ast_channel_tech_pvt(chan); if (!p) { return; } ao2_ref(p, +1); ao2_lock(p); if (!ast_test_flag(&p->base, AST_UNREAL_CARETAKER_THREAD)) { ao2_unlock(p); ao2_ref(p, -1); return; } ast_clear_flag(&p->base, AST_UNREAL_CARETAKER_THREAD); chan = p->base.chan; ao2_unlock(p); ao2_ref(p, -1); if (chan) { ast_bridge_depart(chan); ast_channel_unref(chan); } }
/*! \brief Internal built in feature for attended transfers */ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) { char exten[AST_MAX_EXTENSION] = ""; struct ast_channel *chan = NULL; struct ast_bridge *attended_bridge = NULL; struct ast_bridge_features caller_features, called_features; enum ast_bridge_channel_state attended_bridge_result; struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt; const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : bridge_channel->chan->context); /* Grab the extension to transfer to */ if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) { ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Get a channel that is the destination we wish to call */ if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) { ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Create a bridge to use to talk to the person we are calling */ if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) { ast_hangup(chan); ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); return 0; } /* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */ ast_bridge_features_init(&called_features); ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE); /* This is how this is going down, we are imparting the channel we called above into this bridge first */ ast_bridge_impart(attended_bridge, chan, NULL, &called_features); /* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */ ast_bridge_features_init(&caller_features); ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP, (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL); ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"), attended_threeway_transfer, NULL); ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"), attended_abort_transfer, NULL); /* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */ attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features); /* Since the above returned the caller features structure is of no more use */ ast_bridge_features_cleanup(&caller_features); /* Drop the channel we are transferring to out of the above bridge since it has ended */ if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) { /* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */ if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) { /* We want to impart them upon the bridge and just have us return to it as normal */ ast_bridge_impart(bridge, chan, NULL, NULL); } else { ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL); } } else { ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY); ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT); } /* Now that all channels are out of it we can destroy the bridge and the called features structure */ ast_bridge_features_cleanup(&called_features); ast_bridge_destroy(attended_bridge); return 0; }
static int app_control_add_channel_to_bridge( struct stasis_app_control *control, struct ast_channel *chan, void *data) { struct ast_bridge *bridge = data; int res; if (!control || !bridge) { return -1; } ast_debug(3, "%s: Adding to bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); ast_assert(chan != NULL); /* Depart whatever Stasis bridge we're currently in. */ if (stasis_app_get_bridge(control)) { /* Note that it looks like there's a race condition here, since * we don't have control locked. But this happens from the * control callback thread, so there won't be any other * concurrent attempts to bridge. */ ast_bridge_depart(chan); } res = ast_bridge_set_after_callback(chan, bridge_after_cb, bridge_after_cb_failed, control); if (res != 0) { ast_log(LOG_ERROR, "Error setting after-bridge callback\n"); return -1; } { /* pbx and bridge are modified by the bridging impart thread. * It shouldn't happen concurrently, but we still need to lock * for the memory fence. */ SCOPED_AO2LOCK(lock, control); /* Save off the channel's PBX */ ast_assert(control->pbx == NULL); if (!control->pbx) { control->pbx = ast_channel_pbx(chan); ast_channel_pbx_set(chan, NULL); } res = ast_bridge_impart(bridge, chan, NULL, /* swap channel */ NULL, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; return -1; } ast_assert(stasis_app_get_bridge(control) == NULL); control->bridge = bridge; } return 0; }
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap) { int res; if (!control || !bridge) { return -1; } ast_debug(3, "%s: Adding to bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); ast_assert(chan != NULL); /* Depart whatever Stasis bridge we're currently in. */ if (stasis_app_get_bridge(control)) { /* Note that it looks like there's a race condition here, since * we don't have control locked. But this happens from the * control callback thread, so there won't be any other * concurrent attempts to bridge. */ ast_bridge_depart(chan); } res = ast_bridge_set_after_callback(chan, bridge_after_cb, bridge_after_cb_failed, control); if (res != 0) { ast_log(LOG_ERROR, "Error setting after-bridge callback\n"); return -1; } { /* pbx and bridge are modified by the bridging impart thread. * It shouldn't happen concurrently, but we still need to lock * for the memory fence. */ SCOPED_AO2LOCK(lock, control); /* Ensure the controlling application is subscribed early enough * to receive the ChannelEnteredBridge message. This works in concert * with the subscription handled in the Stasis application execution * loop */ app_subscribe_bridge(control->app, bridge); /* Save off the channel's PBX */ ast_assert(control->pbx == NULL); if (!control->pbx) { control->pbx = ast_channel_pbx(chan); ast_channel_pbx_set(chan, NULL); } res = ast_bridge_impart(bridge, chan, swap, NULL, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; return -1; } ast_assert(stasis_app_get_bridge(control) == NULL); control->bridge = bridge; ast_channel_lock(chan); set_interval_hook(chan); ast_channel_unlock(chan); } return 0; }
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap) { int res; struct ast_bridge_features *features; if (!control || !bridge) { return -1; } ast_debug(3, "%s: Adding to bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); ast_assert(chan != NULL); /* Depart whatever Stasis bridge we're currently in. */ if (stasis_app_get_bridge(control)) { /* Note that it looks like there's a race condition here, since * we don't have control locked. But this happens from the * control callback thread, so there won't be any other * concurrent attempts to bridge. */ ast_bridge_depart(chan); } res = ast_bridge_set_after_callback(chan, bridge_after_cb, bridge_after_cb_failed, control); if (res != 0) { ast_log(LOG_ERROR, "Error setting after-bridge callback\n"); return -1; } ao2_lock(control); /* Ensure the controlling application is subscribed early enough * to receive the ChannelEnteredBridge message. This works in concert * with the subscription handled in the Stasis application execution * loop */ app_subscribe_bridge(control->app, bridge); /* Save off the channel's PBX */ ast_assert(control->pbx == NULL); if (!control->pbx) { control->pbx = ast_channel_pbx(chan); ast_channel_pbx_set(chan, NULL); } /* Pull bridge features from the control */ features = control->bridge_features; control->bridge_features = NULL; ast_assert(stasis_app_get_bridge(control) == NULL); /* We need to set control->bridge here since bridge_after_cb may be run * before ast_bridge_impart returns. bridge_after_cb gets a reason * code so it can tell if the bridge is actually valid or not. */ control->bridge = bridge; /* We can't be holding the control lock while impart is running * or we could create a deadlock with bridge_after_cb which also * tries to lock control. */ ao2_unlock(control); res = ast_bridge_impart(bridge, chan, swap, features, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { /* ast_bridge_impart failed before it could spawn the depart * thread. The callbacks aren't called in this case. * The impart could still fail even if ast_bridge_impart returned * ok but that's handled by bridge_after_cb. */ ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ao2_lock(control); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; control->bridge = NULL; ao2_unlock(control); } else { ast_channel_lock(chan); set_interval_hook(chan); ast_channel_unlock(chan); } return res; }