Newer
Older
app = ast_strdupa(ast_channel_appl(chan2));
Mark Michelson
committed
res = ast_local_setup_masquerade(local_chan, chan2);
}
if (res) {
ast_hangup(local_chan);
return AST_BRIDGE_TRANSFER_FAIL;
}
/*
* Since bridges need to be unlocked before entering ast_bridge_impart and
* core_local may call into it then the bridges need to be unlocked here.
*/
ast_bridge_unlock(bridge1);
if (bridge2) {
ast_bridge_unlock(bridge2);
}
Mark Michelson
committed
if (ast_call(local_chan, dest, 0)) {
ast_hangup(local_chan);
BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
Mark Michelson
committed
return AST_BRIDGE_TRANSFER_FAIL;
}
/* Get a ref for use later since this one is being stolen */
ao2_ref(local_chan, +1);
if (ast_bridge_impart(bridge1, local_chan, chan1, NULL,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
Mark Michelson
committed
ast_hangup(local_chan);
ao2_cleanup(local_chan);
BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
Mark Michelson
committed
return AST_BRIDGE_TRANSFER_FAIL;
}
BRIDGE_LOCK_ONE_OR_BOTH(bridge1, bridge2);
Mark Michelson
committed
if (bridge2) {
Mark Michelson
committed
struct ast_channel *locals[2];
/* Have to lock everything just in case a hangup comes in early */
ast_local_lock_all(local_chan, &locals[0], &locals[1]);
if (!locals[0] || !locals[1]) {
ast_log(LOG_ERROR, "Transfer failed probably due to an early hangup - "
"missing other half of '%s'\n", ast_channel_name(local_chan));
ast_local_unlock_all(local_chan);
ao2_cleanup(local_chan);
return AST_BRIDGE_TRANSFER_FAIL;
}
/* Make sure the peer is properly set */
if (local_chan != locals[0]) {
SWAP(locals[0], locals[1]);
}
Mark Michelson
committed
ast_attended_transfer_message_add_link(transfer_msg, locals);
ast_local_unlock_all(local_chan);
Mark Michelson
committed
ast_attended_transfer_message_add_app(transfer_msg, app, local_chan);
Mark Michelson
committed
ao2_cleanup(local_chan);
return AST_BRIDGE_TRANSFER_SUCCESS;
static enum ast_transfer_result try_parking(struct ast_channel *transferer,
const char *context, const char *exten, transfer_channel_cb new_channel_cb,
struct transfer_channel_data *user_data_wrapper)
Richard Mudgett
committed
RAII_VAR(struct ast_bridge_channel *, transferer_bridge_channel, NULL, ao2_cleanup);
if (!ast_parking_provider_registered()) {
return AST_BRIDGE_TRANSFER_FAIL;
}
Richard Mudgett
committed
ast_channel_lock(transferer);
transferer_bridge_channel = ast_channel_get_bridge_channel(transferer);
Richard Mudgett
committed
ast_channel_unlock(transferer);
if (!transferer_bridge_channel) {
return AST_BRIDGE_TRANSFER_FAIL;
Richard Mudgett
committed
}
if (ast_parking_blind_transfer_park(transferer_bridge_channel,
context, exten, new_channel_cb, user_data_wrapper)) {
return AST_BRIDGE_TRANSFER_FAIL;
Richard Mudgett
committed
}
return AST_BRIDGE_TRANSFER_SUCCESS;
void ast_bridge_set_transfer_variables(struct ast_channel *chan, const char *value, int attended)
{
char *writevar;
char *erasevar;
if (attended) {
writevar = ATTENDEDTRANSFER;
erasevar = BLINDTRANSFER;
} else {
writevar = BLINDTRANSFER;
erasevar = ATTENDEDTRANSFER;
}
pbx_builtin_setvar_helper(chan, writevar, value);
pbx_builtin_setvar_helper(chan, erasevar, NULL);
}
Richard Mudgett
committed
/*!
* \internal
* \brief Set the transfer variable as appropriate on channels involved in the transfer
Richard Mudgett
committed
*
* The transferer channel will have its variable set the same as its BRIDGEPEER
Richard Mudgett
committed
* variable. This will account for all channels that it is bridged to. The other channels
* involved in the transfer will have their variable set to the transferer
Richard Mudgett
committed
* channel's name.
*
* \param transferer The channel performing the transfer
Richard Mudgett
committed
* \param channels The channels belonging to the bridge
* \param is_attended false set BLINDTRANSFER and unset ATTENDEDTRANSFER
* true set ATTENDEDTRANSFER and unset BLINDTRANSFER
Richard Mudgett
committed
*/
static void set_transfer_variables_all(struct ast_channel *transferer, struct ao2_container *channels, int is_attended)
Richard Mudgett
committed
struct ao2_iterator iter;
struct ast_channel *chan;
const char *transferer_name;
const char *transferer_bridgepeer;
ast_channel_lock(transferer);
transferer_name = ast_strdupa(ast_channel_name(transferer));
transferer_bridgepeer = ast_strdupa(S_OR(pbx_builtin_getvar_helper(transferer, "BRIDGEPEER"), ""));
ast_channel_unlock(transferer);
for (iter = ao2_iterator_init(channels, 0);
(chan = ao2_iterator_next(&iter));
ao2_cleanup(chan)) {
if (chan == transferer) {
ast_bridge_set_transfer_variables(chan, transferer_bridgepeer, is_attended);
Richard Mudgett
committed
} else {
ast_bridge_set_transfer_variables(chan, transferer_name, is_attended);
Richard Mudgett
committed
}
}
Richard Mudgett
committed
ao2_iterator_destroy(&iter);
}
static struct ast_bridge *acquire_bridge(struct ast_channel *chan)
{
struct ast_bridge *bridge;
ast_channel_lock(chan);
bridge = ast_channel_get_bridge(chan);
ast_channel_unlock(chan);
if (bridge
&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) {
ao2_ref(bridge, -1);
bridge = NULL;
}
return bridge;
}
enum ast_transfer_result ast_bridge_transfer_blind(int is_external,
struct ast_channel *transferer, const char *exten, const char *context,
Richard Mudgett
committed
transfer_channel_cb new_channel_cb, void *user_data)
{
RAII_VAR(struct ast_bridge *, bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
Richard Mudgett
committed
RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, transferee, NULL, ast_channel_cleanup);
RAII_VAR(struct transfer_channel_data *, user_data_wrapper, NULL, ao2_cleanup);
Mark Michelson
committed
RAII_VAR(struct ast_blind_transfer_message *, transfer_message, NULL, ao2_cleanup);
Richard Mudgett
committed
int do_bridge_transfer;
int transfer_prohibited;
enum ast_transfer_result transfer_result;
Mark Michelson
committed
transfer_message = ast_blind_transfer_message_create(is_external, transferer, exten, context);
if (!transfer_message) {
/* Out of memory. Not even possible to publish a Stasis message about the
* failure
*/
ast_log(LOG_ERROR, "Unable to allocate memory for blind transfer publication from %s\n",
ast_channel_name(transferer));
return AST_BRIDGE_TRANSFER_FAIL;
}
bridge = acquire_bridge(transferer);
if (!bridge) {
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
Mark Michelson
committed
ast_bridge_lock(bridge);
transfer_message->bridge = ast_bridge_snapshot_create(bridge);
ast_bridge_unlock(bridge);
if (!transfer_message->bridge) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
}
transferee = ast_bridge_peer(bridge, transferer);
Mark Michelson
committed
if (transferee) {
transfer_message->transferee = ast_channel_snapshot_get_latest(ast_channel_uniqueid(transferee));
if (!transfer_message->transferee) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
}
}
Richard Mudgett
committed
ast_channel_lock(transferer);
bridge_channel = ast_channel_get_bridge_channel(transferer);
Richard Mudgett
committed
ast_channel_unlock(transferer);
if (!bridge_channel) {
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
user_data_wrapper = ao2_alloc(sizeof(*user_data_wrapper), NULL);
if (!user_data_wrapper) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
}
user_data_wrapper->data = user_data;
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(bridge_channel);
transfer_result = try_parking(transferer, context, exten, new_channel_cb, user_data_wrapper);
if (transfer_result == AST_BRIDGE_TRANSFER_SUCCESS) {
goto publish;
/* Since parking didn't take control of the user_data_wrapper, we are just going to raise the completed flag now. */
user_data_wrapper->completed = 1;
Richard Mudgett
committed
{
SCOPED_LOCK(lock, bridge, ast_bridge_lock, ast_bridge_unlock);
Richard Mudgett
committed
channels = ast_bridge_peers_nolock(bridge);
if (!channels) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
Richard Mudgett
committed
if (ao2_container_count(channels) <= 1) {
transfer_result = AST_BRIDGE_TRANSFER_INVALID;
goto publish;
Richard Mudgett
committed
}
transfer_prohibited = ast_test_flag(&bridge->feature_flags,
AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
do_bridge_transfer = ast_test_flag(&bridge->feature_flags,
AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) ||
ao2_container_count(channels) > 2;
}
Richard Mudgett
committed
if (transfer_prohibited) {
transfer_result = AST_BRIDGE_TRANSFER_NOT_PERMITTED;
goto publish;
Richard Mudgett
committed
}
set_transfer_variables_all(transferer, channels, 0);
Richard Mudgett
committed
if (do_bridge_transfer) {
transfer_result = blind_transfer_bridge(is_external, transferer, bridge,
Mark Michelson
committed
exten, context, transferee, new_channel_cb, user_data_wrapper, transfer_message);
goto publish;
Richard Mudgett
committed
}
Richard Mudgett
committed
/* Reaching this portion means that we're dealing with a two-party bridge */
Richard Mudgett
committed
if (!transferee) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
Richard Mudgett
committed
}
if (bridge_channel_internal_queue_blind_transfer(transferee, exten, context,
new_channel_cb, user_data_wrapper)) {
transfer_result = AST_BRIDGE_TRANSFER_FAIL;
goto publish;
Richard Mudgett
committed
ast_bridge_remove(bridge, transferer);
transfer_result = AST_BRIDGE_TRANSFER_SUCCESS;
publish:
Mark Michelson
committed
transfer_message->result = transfer_result;
ast_bridge_publish_blind_transfer(transfer_message);
return transfer_result;
Richard Mudgett
committed
}
Mark Michelson
committed
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
/*!
* \internal
* \brief Performs an attended transfer by moving a channel from one bridge to another
*
* The channel that is bridged to the source_channel is moved into the dest_bridge from
* the source_bridge_channel's bridge. The swap_channel is swapped out of the dest_bridge and placed in
* the source_bridge_channel's bridge.
*
* \note dest_bridge and source_bridge_channel's bridge MUST be locked before calling this function.
*
* \param dest_bridge The final bridge for the attended transfer
* \param source_channel Channel who is bridged to the channel that will move
* \param swap_channel Channel to be swapped out of the dest_bridge
* \return The success or failure of the swap attempt
*/
static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge *dest_bridge,
struct ast_bridge_channel *source_bridge_channel, struct ast_channel *swap_channel)
{
struct ast_bridge_channel *bridged_to_source;
bridged_to_source = ast_bridge_channel_peer(source_bridge_channel);
&& bridged_to_source->state == BRIDGE_CHANNEL_STATE_WAIT
&& !ast_test_flag(&bridged_to_source->features->feature_flags,
AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) {
Mark Michelson
committed
bridged_to_source->swap = swap_channel;
if (bridge_do_move(dest_bridge, bridged_to_source, 1, 0)) {
return AST_BRIDGE_TRANSFER_FAIL;
}
/* Must kick the source channel out of its bridge. */
Richard Mudgett
committed
ast_bridge_channel_leave_bridge(source_bridge_channel,
BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, AST_CAUSE_NORMAL_CLEARING);
return AST_BRIDGE_TRANSFER_SUCCESS;
Mark Michelson
committed
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
4343
4344
4345
} else {
return AST_BRIDGE_TRANSFER_INVALID;
}
}
/*!
* \internal
* \brief Function that performs an attended transfer when both transferer channels are bridged
*
* The method by which the transfer is performed is dependent on whether the bridges allow for
* optimization to occur between them. If no optimization is permitted, then an unreal channel
* is placed as a link between the two bridges. If optimization is permitted, then that means
* we are free to perform move or merge operations in order to perform the transfer.
*
* \note to_transferee_bridge and to_target_bridge MUST be locked before calling this function
*
* \param to_transferee The channel that is bridged to the transferee
* \param to_transferee_bridge_channel to_transferee's bridge_channel
* \param to_transfer_target The channel that is bridged to the transfer target
* \param to_target_bridge_channel to_transfer_target's bridge_channel
* \param to_transferee_bridge The bridge between to_transferee and the transferee
* \param to_target_bridge The bridge between to_transfer_target and the transfer_target
* \param publication Data to publish for a stasis attended transfer message
Mark Michelson
committed
* \return The success or failure of the attended transfer
*/
static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel *to_transferee,
struct ast_bridge_channel *to_transferee_bridge_channel,
struct ast_channel *to_transfer_target,
struct ast_bridge_channel *to_target_bridge_channel,
struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge,
Mark Michelson
committed
struct ast_attended_transfer_message *transfer_msg)
Mark Michelson
committed
{
struct ast_bridge_channel *kick_me[] = {
to_transferee_bridge_channel,
to_target_bridge_channel,
};
enum ast_transfer_result res;
struct ast_bridge *final_bridge = NULL;
RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
channels = ast_bridge_peers_nolock(to_transferee_bridge);
if (!channels) {
res = AST_BRIDGE_TRANSFER_FAIL;
goto end;
}
set_transfer_variables_all(to_transferee, channels, 1);
Mark Michelson
committed
switch (ast_bridges_allow_optimization(to_transferee_bridge, to_target_bridge)) {
case AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE:
final_bridge = to_transferee_bridge;
res = bridge_swap_attended_transfer(to_transferee_bridge, to_target_bridge_channel, to_transferee);
goto end;
Mark Michelson
committed
case AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE:
final_bridge = to_target_bridge;
res = bridge_swap_attended_transfer(to_target_bridge, to_transferee_bridge_channel, to_transfer_target);
goto end;
Mark Michelson
committed
case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE:
final_bridge = to_transferee_bridge;
bridge_do_merge(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me), 0);
res = AST_BRIDGE_TRANSFER_SUCCESS;
goto end;
Mark Michelson
committed
case AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE:
final_bridge = to_target_bridge;
bridge_do_merge(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me), 0);
res = AST_BRIDGE_TRANSFER_SUCCESS;
goto end;
Mark Michelson
committed
case AST_BRIDGE_OPTIMIZE_PROHIBITED:
default:
/* Just because optimization wasn't doable doesn't necessarily mean
* that we can actually perform the transfer. Some reasons for non-optimization
* indicate bridge invalidity, so let's check those before proceeding.
*/
if (to_transferee_bridge->inhibit_merge || to_transferee_bridge->dissolved ||
to_target_bridge->inhibit_merge || to_target_bridge->dissolved) {
Mark Michelson
committed
return AST_BRIDGE_TRANSFER_INVALID;
Mark Michelson
committed
}
Mark Michelson
committed
return attended_transfer_bridge(to_transferee, to_transfer_target,
Mark Michelson
committed
to_transferee_bridge, to_target_bridge, transfer_msg);
Mark Michelson
committed
}
end:
if (res == AST_BRIDGE_TRANSFER_SUCCESS) {
Mark Michelson
committed
ast_attended_transfer_message_add_merge(transfer_msg, final_bridge);
}
return res;
Mark Michelson
committed
}
Richard Mudgett
committed
enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
Mark Michelson
committed
struct ast_channel *to_transfer_target)
{
RAII_VAR(struct ast_bridge *, to_transferee_bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge *, to_target_bridge, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, to_transferee_bridge_channel, NULL, ao2_cleanup);
RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup);
Mark Michelson
committed
RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup);
Mark Michelson
committed
RAII_VAR(struct ast_attended_transfer_message *, transfer_msg, NULL, ao2_cleanup);
struct ast_bridge *the_bridge = NULL;
Mark Michelson
committed
struct ast_channel *chan_bridged;
struct ast_channel *chan_unbridged;
int transfer_prohibited;
int do_bridge_transfer;
enum ast_transfer_result res;
const char *app = NULL;
int hangup_target = 0;
Mark Michelson
committed
to_transferee_bridge = acquire_bridge(to_transferee);
to_target_bridge = acquire_bridge(to_transfer_target);
Mark Michelson
committed
transfer_msg = ast_attended_transfer_message_create(1, to_transferee, to_transferee_bridge,
to_transfer_target, to_target_bridge, NULL, NULL);
if (!transfer_msg) {
ast_log(LOG_ERROR, "Unable to create Stasis publication for attended transfer from %s\n",
ast_channel_name(to_transferee));
return AST_BRIDGE_TRANSFER_FAIL;
}
Mark Michelson
committed
/* They can't both be unbridged, you silly goose! */
if (!to_transferee_bridge && !to_target_bridge) {
res = AST_BRIDGE_TRANSFER_INVALID;
goto end;
Mark Michelson
committed
}
ast_channel_lock(to_transferee);
to_transferee_bridge_channel = ast_channel_get_bridge_channel(to_transferee);
ast_channel_unlock(to_transferee);
ast_channel_lock(to_transfer_target);
to_target_bridge_channel = ast_channel_get_bridge_channel(to_transfer_target);
ast_channel_unlock(to_transfer_target);
if (to_transferee_bridge_channel) {
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(to_transferee_bridge_channel);
}
if (to_target_bridge_channel) {
Richard Mudgett
committed
const char *target_complete_sound;
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(to_target_bridge_channel);
Richard Mudgett
committed
4469
4470
4471
4472
4473
4474
4475
4476
4477
4478
4479
4480
4481
4482
4483
4484
4485
4486
4487
4488
4489
4490
4491
4492
4493
4494
/* Is there a courtesy sound to play to the target? */
ast_channel_lock(to_transfer_target);
target_complete_sound = pbx_builtin_getvar_helper(to_transfer_target,
"ATTENDED_TRANSFER_COMPLETE_SOUND");
if (!ast_strlen_zero(target_complete_sound)) {
target_complete_sound = ast_strdupa(target_complete_sound);
} else {
target_complete_sound = NULL;
}
ast_channel_unlock(to_transfer_target);
if (!target_complete_sound) {
ast_channel_lock(to_transferee);
target_complete_sound = pbx_builtin_getvar_helper(to_transferee,
"ATTENDED_TRANSFER_COMPLETE_SOUND");
if (!ast_strlen_zero(target_complete_sound)) {
target_complete_sound = ast_strdupa(target_complete_sound);
} else {
target_complete_sound = NULL;
}
ast_channel_unlock(to_transferee);
}
if (target_complete_sound) {
ast_bridge_channel_write_playfile(to_target_bridge_channel, NULL,
target_complete_sound, NULL);
}
Mark Michelson
committed
/* Let's get the easy one out of the way first */
if (to_transferee_bridge && to_target_bridge) {
if (!to_transferee_bridge_channel || !to_target_bridge_channel) {
res = AST_BRIDGE_TRANSFER_INVALID;
goto end;
Mark Michelson
committed
ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel,
to_transfer_target, to_target_bridge_channel,
Mark Michelson
committed
to_transferee_bridge, to_target_bridge, transfer_msg);
Mark Michelson
committed
ast_bridge_unlock(to_transferee_bridge);
ast_bridge_unlock(to_target_bridge);
hangup_target = 1;
Mark Michelson
committed
}
the_bridge = to_transferee_bridge ?: to_target_bridge;
chan_bridged = to_transferee_bridge ? to_transferee : to_transfer_target;
chan_unbridged = to_transferee_bridge ? to_transfer_target : to_transferee;
/*
* Race condition makes it possible for app to be NULL, so get the app prior to
* transferring with a fallback of "unknown".
*/
app = ast_strdupa(ast_channel_appl(chan_unbridged) ?: "unknown");
Mark Michelson
committed
{
int chan_count;
SCOPED_LOCK(lock, the_bridge, ast_bridge_lock, ast_bridge_unlock);
channels = ast_bridge_peers_nolock(the_bridge);
if (!channels) {
res = AST_BRIDGE_TRANSFER_FAIL;
goto end;
Mark Michelson
committed
}
chan_count = ao2_container_count(channels);
if (chan_count <= 1) {
res = AST_BRIDGE_TRANSFER_INVALID;
goto end;
Mark Michelson
committed
}
transfer_prohibited = ast_test_flag(&the_bridge->feature_flags,
AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
do_bridge_transfer = ast_test_flag(&the_bridge->feature_flags,
AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) ||
chan_count > 2;
}
if (transfer_prohibited) {
res = AST_BRIDGE_TRANSFER_NOT_PERMITTED;
goto end;
Mark Michelson
committed
}
set_transfer_variables_all(to_transferee, channels, 1);
Mark Michelson
committed
if (do_bridge_transfer) {
/*
* Hang up the target if it was bridged. Note, if it is not bridged
* it is hung up during the masquerade.
*/
hangup_target = chan_bridged == to_transfer_target;
ast_bridge_lock(the_bridge);
Mark Michelson
committed
res = attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL, transfer_msg);
ast_bridge_unlock(the_bridge);
goto end;
Mark Michelson
committed
}
transferee = get_transferee(channels, chan_bridged);
if (!transferee) {
res = AST_BRIDGE_TRANSFER_FAIL;
goto end;
Mark Michelson
committed
}
if (bridge_channel_internal_queue_attended_transfer(transferee, chan_unbridged)) {
res = AST_BRIDGE_TRANSFER_FAIL;
goto end;
Mark Michelson
committed
}
Mark Michelson
committed
ast_bridge_remove(the_bridge, chan_bridged);
Mark Michelson
committed
ast_attended_transfer_message_add_app(transfer_msg, app, NULL);
res = AST_BRIDGE_TRANSFER_SUCCESS;
if (res == AST_BRIDGE_TRANSFER_SUCCESS && hangup_target) {
ast_softhangup(to_transfer_target, AST_SOFTHANGUP_DEV);
}
Mark Michelson
committed
transfer_msg->result = res;
ast_bridge_publish_attended_transfer(transfer_msg);
Richard Mudgett
committed
/*!
* \internal
* \brief Service the bridge manager request.
* \since 12.0.0
*
* \param bridge requesting service.
*
* \return Nothing
*/
static void bridge_manager_service(struct ast_bridge *bridge)
Richard Mudgett
committed
ast_bridge_lock(bridge);
if (bridge->callid) {
ast_callid_threadassoc_change(bridge->callid);
}
Richard Mudgett
committed
/* Do any pending bridge actions. */
bridge_handle_actions(bridge);
ast_bridge_unlock(bridge);
}
Richard Mudgett
committed
/*!
* \internal
* \brief Bridge manager service thread.
* \since 12.0.0
*
* \return Nothing
*/
static void *bridge_manager_thread(void *data)
{
struct bridge_manager_controller *manager = data;
struct bridge_manager_request *request;
ao2_lock(manager);
while (!manager->stop) {
request = AST_LIST_REMOVE_HEAD(&manager->service_requests, node);
if (!request) {
ast_cond_wait(&manager->cond, ao2_object_get_lockaddr(manager));
continue;
}
ao2_unlock(manager);
Richard Mudgett
committed
/* Service the bridge. */
bridge_manager_service(request->bridge);
ao2_ref(request->bridge, -1);
ast_free(request);
Richard Mudgett
committed
ao2_lock(manager);
}
ao2_unlock(manager);
Richard Mudgett
committed
return NULL;
Richard Mudgett
committed
/*!
* \internal
* \brief Destroy the bridge manager controller.
* \since 12.0.0
*
* \param obj Bridge manager to destroy.
*
* \return Nothing
*/
static void bridge_manager_destroy(void *obj)
Richard Mudgett
committed
struct bridge_manager_controller *manager = obj;
struct bridge_manager_request *request;
if (manager->thread != AST_PTHREADT_NULL) {
/* Stop the manager thread. */
ao2_lock(manager);
manager->stop = 1;
ast_cond_signal(&manager->cond);
ao2_unlock(manager);
ast_debug(1, "Waiting for bridge manager thread to die.\n");
pthread_join(manager->thread, NULL);
}
Richard Mudgett
committed
/* Destroy the service request queue. */
while ((request = AST_LIST_REMOVE_HEAD(&manager->service_requests, node))) {
ao2_ref(request->bridge, -1);
ast_free(request);
Richard Mudgett
committed
ast_cond_destroy(&manager->cond);
}
/*!
* \internal
* \brief Create the bridge manager controller.
* \since 12.0.0
*
* \retval manager on success.
* \retval NULL on error.
*/
static struct bridge_manager_controller *bridge_manager_create(void)
{
struct bridge_manager_controller *manager;
Richard Mudgett
committed
manager = ao2_alloc(sizeof(*manager), bridge_manager_destroy);
if (!manager) {
/* Well. This isn't good. */
return NULL;
}
ast_cond_init(&manager->cond, NULL);
AST_LIST_HEAD_INIT_NOLOCK(&manager->service_requests);
/* Create the bridge manager thread. */
if (ast_pthread_create(&manager->thread, NULL, bridge_manager_thread, manager)) {
/* Well. This isn't good either. */
manager->thread = AST_PTHREADT_NULL;
ao2_ref(manager, -1);
manager = NULL;
}
Richard Mudgett
committed
return manager;
Richard Mudgett
committed
/*!
* \internal
* \brief Bridge ao2 container sort function.
* \since 12.0.0
*
* \param obj_left pointer to the (user-defined part) of an object.
* \param obj_right pointer to the (user-defined part) of an object.
* \param flags flags from ao2_callback()
* OBJ_POINTER - if set, 'obj_right', is an object.
* OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
* OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
*
* \retval <0 if obj_left < obj_right
* \retval =0 if obj_left == obj_right
* \retval >0 if obj_left > obj_right
*/
static int bridge_sort_cmp(const void *obj_left, const void *obj_right, int flags)
Richard Mudgett
committed
const struct ast_bridge *bridge_left = obj_left;
const struct ast_bridge *bridge_right = obj_right;
const char *right_key = obj_right;
int cmp;
switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
default:
case OBJ_POINTER:
right_key = bridge_right->uniqueid;
/* Fall through */
case OBJ_KEY:
cmp = strcmp(bridge_left->uniqueid, right_key);
break;
case OBJ_PARTIAL_KEY:
cmp = strncmp(bridge_left->uniqueid, right_key, strlen(right_key));
break;
}
return cmp;
struct ast_bridge *ast_bridge_find_by_id(const char *bridge_id)
{
return ao2_find(bridges, bridge_id, OBJ_SEARCH_KEY);
}
Richard Mudgett
committed
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
struct bridge_complete {
/*! Nth match to return. */
int state;
/*! Which match currently on. */
int which;
};
static int complete_bridge_live_search(void *obj, void *arg, void *data, int flags)
{
struct bridge_complete *search = data;
if (++search->which > search->state) {
return CMP_MATCH;
}
return 0;
}
static char *complete_bridge_live(const char *word, int state)
{
char *ret;
struct ast_bridge *bridge;
struct bridge_complete search = {
.state = state,
};
bridge = ao2_callback_data(bridges, ast_strlen_zero(word) ? 0 : OBJ_PARTIAL_KEY,
complete_bridge_live_search, (char *) word, &search);
if (!bridge) {
return NULL;
}
ret = ast_strdup(bridge->uniqueid);
ao2_ref(bridge, -1);
return ret;
}
static char *complete_bridge_stasis(const char *word, int state)
char *ret = NULL;
int wordlen = strlen(word), which = 0;
RAII_VAR(struct ao2_container *, cached_bridges, NULL, ao2_cleanup);
struct ao2_iterator iter;
struct stasis_message *msg;
Richard Mudgett
committed
Richard Mudgett
committed
cached_bridges = stasis_cache_dump(ast_bridge_cache(), ast_bridge_snapshot_type());
if (!cached_bridges) {
return NULL;
Richard Mudgett
committed
}
iter = ao2_iterator_init(cached_bridges, 0);
for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {
struct ast_bridge_snapshot *snapshot = stasis_message_data(msg);
Richard Mudgett
committed
if (!strncasecmp(word, snapshot->uniqueid, wordlen) && (++which > state)) {
ret = ast_strdup(snapshot->uniqueid);
ao2_ref(msg, -1);
break;
}
ao2_iterator_destroy(&iter);
Richard Mudgett
committed
return ret;
}
Richard Mudgett
committed
static char *handle_bridge_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
#define FORMAT_HDR "%-36s %5s %-15s %s\n"
#define FORMAT_ROW "%-36s %5u %-15s %s\n"
RAII_VAR(struct ao2_container *, cached_bridges, NULL, ao2_cleanup);
Richard Mudgett
committed
struct ao2_iterator iter;
struct stasis_message *msg;
Richard Mudgett
committed
switch (cmd) {
case CLI_INIT:
e->command = "bridge show all";
e->usage =
"Usage: bridge show all\n"
" List all bridges\n";
return NULL;
case CLI_GENERATE:
return NULL;
Richard Mudgett
committed
cached_bridges = stasis_cache_dump(ast_bridge_cache(), ast_bridge_snapshot_type());
if (!cached_bridges) {
ast_cli(a->fd, "Failed to retrieve cached bridges\n");
return CLI_SUCCESS;
}
Richard Mudgett
committed
ast_cli(a->fd, FORMAT_HDR, "Bridge-ID", "Chans", "Type", "Technology");
iter = ao2_iterator_init(cached_bridges, 0);
for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {
struct ast_bridge_snapshot *snapshot = stasis_message_data(msg);
Richard Mudgett
committed
ast_cli(a->fd, FORMAT_ROW,
snapshot->uniqueid,
snapshot->num_channels,
S_OR(snapshot->subclass, "<unknown>"),
S_OR(snapshot->technology, "<unknown>"));
Richard Mudgett
committed
}
ao2_iterator_destroy(&iter);
return CLI_SUCCESS;
Richard Mudgett
committed
#undef FORMAT_HDR
#undef FORMAT_ROW
/*! \brief Internal callback function for sending channels in a bridge to the CLI */
static int bridge_show_specific_print_channel(void *obj, void *arg, int flags)
{
const char *uniqueid = obj;
struct ast_cli_args *a = arg;
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
struct ast_channel_snapshot *snapshot;
Richard Mudgett
committed
msg = stasis_cache_get(ast_channel_cache(), ast_channel_snapshot_type(), uniqueid);
if (!msg) {
return 0;
}
snapshot = stasis_message_data(msg);
ast_cli(a->fd, "Channel: %s\n", snapshot->name);
return 0;
}
Richard Mudgett
committed
static char *handle_bridge_show_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
struct ast_bridge_snapshot *snapshot;
Richard Mudgett
committed
switch (cmd) {
case CLI_INIT:
e->command = "bridge show";
e->usage =
"Usage: bridge show <bridge-id>\n"
" Show information about the <bridge-id> bridge\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
Richard Mudgett
committed
return complete_bridge_stasis(a->word, a->n);
Richard Mudgett
committed
}
return NULL;
}
if (a->argc != 3) {
return CLI_SHOWUSAGE;
msg = stasis_cache_get(ast_bridge_cache(), ast_bridge_snapshot_type(), a->argv[2]);
if (!msg) {
Richard Mudgett
committed
ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
return CLI_SUCCESS;
}
snapshot = stasis_message_data(msg);
ast_cli(a->fd, "Id: %s\n", snapshot->uniqueid);
ast_cli(a->fd, "Type: %s\n", S_OR(snapshot->subclass, "<unknown>"));
ast_cli(a->fd, "Technology: %s\n", S_OR(snapshot->technology, "<unknown>"));
ast_cli(a->fd, "Num-Channels: %u\n", snapshot->num_channels);
ao2_callback(snapshot->channels, OBJ_NODATA, bridge_show_specific_print_channel, a);
Richard Mudgett
committed
return CLI_SUCCESS;
Joshua Colp
committed
#ifdef AST_DEVMODE
Richard Mudgett
committed
static char *handle_bridge_destroy_specific(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Richard Mudgett
committed
struct ast_bridge *bridge;
Richard Mudgett
committed
switch (cmd) {
case CLI_INIT:
e->command = "bridge destroy";
e->usage =
"Usage: bridge destroy <bridge-id>\n"
" Destroy the <bridge-id> bridge\n";
return NULL;
case CLI_GENERATE:
if (a->pos == 2) {
Richard Mudgett
committed
return complete_bridge_live(a->word, a->n);
Richard Mudgett
committed
}
return NULL;
Richard Mudgett
committed
if (a->argc != 3) {
return CLI_SHOWUSAGE;
}
bridge = ast_bridge_find_by_id(a->argv[2]);
Richard Mudgett
committed
if (!bridge) {
ast_cli(a->fd, "Bridge '%s' not found\n", a->argv[2]);
return CLI_SUCCESS;
}
Richard Mudgett
committed
ast_cli(a->fd, "Destroying bridge '%s'\n", a->argv[2]);
Richard Mudgett
committed
ast_bridge_destroy(bridge, 0);
Richard Mudgett
committed
return CLI_SUCCESS;
Joshua Colp
committed
#endif
Richard Mudgett
committed
static char *complete_bridge_participant(const char *bridge_name, const char *line, const char *word, int pos, int state)
Joshua Colp
committed
struct ast_bridge *bridge;
Richard Mudgett
committed
struct ast_bridge_channel *bridge_channel;
int which;
int wordlen;
bridge = ast_bridge_find_by_id(bridge_name);
Richard Mudgett
committed
if (!bridge) {
return NULL;
Joshua Colp
committed
if (!state) {
ao2_ref(bridge, -1);
return ast_strdup("all");
}
state--;
Richard Mudgett
committed
{
SCOPED_LOCK(bridge_lock, bridge, ast_bridge_lock, ast_bridge_unlock);
which = 0;
wordlen = strlen(word);
AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
if (!strncasecmp(ast_channel_name(bridge_channel->chan), word, wordlen)
&& ++which > state) {
Joshua Colp
committed
ao2_ref(bridge, -1);
Richard Mudgett
committed
return ast_strdup(ast_channel_name(bridge_channel->chan));
}
Joshua Colp
committed
ao2_ref(bridge, -1);
Richard Mudgett
committed
return NULL;
Richard Mudgett
committed
static char *handle_bridge_kick_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Joshua Colp
committed
struct ast_bridge *bridge;
Richard Mudgett
committed
switch (cmd) {
case CLI_INIT:
e->command = "bridge kick";
e->usage =
Joshua Colp
committed
"Usage: bridge kick <bridge-id> <channel-name | all>\n"