Newer
Older
Richard Mudgett
committed
struct ao2_container *channels;
Richard Mudgett
committed
ast_bridge_lock(bridge);
channels = ast_bridge_peers_nolock(bridge);
ast_bridge_unlock(bridge);
Richard Mudgett
committed
return channels;
Richard Mudgett
committed
struct ast_channel *ast_bridge_peer_nolock(struct ast_bridge *bridge, struct ast_channel *chan)
Richard Mudgett
committed
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
struct ast_channel *peer = NULL;
struct ast_bridge_channel *iter;
/* Asking for the peer channel only makes sense on a two-party bridge. */
if (bridge->num_channels == 2
&& bridge->technology->capabilities
& (AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX)) {
int in_bridge = 0;
AST_LIST_TRAVERSE(&bridge->channels, iter, entry) {
if (iter->chan != chan) {
peer = iter->chan;
} else {
in_bridge = 1;
}
}
if (in_bridge && peer) {
ast_channel_ref(peer);
} else {
peer = NULL;
}
Richard Mudgett
committed
return peer;
}
Richard Mudgett
committed
struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channel *chan)
{
struct ast_channel *peer;
Richard Mudgett
committed
ast_bridge_lock(bridge);
peer = ast_bridge_peer_nolock(bridge, chan);
ast_bridge_unlock(bridge);
Richard Mudgett
committed
return peer;
Richard Mudgett
committed
/*!
* \internal
* \brief Transfer an entire bridge to a specific destination.
*
* This creates a local channel to dial out and swaps the called local channel
* with the transferer channel. By doing so, all participants in the bridge are
* connected to the specified destination.
*
* While this means of transferring would work for both two-party and multi-party
* bridges, this method is only used for multi-party bridges since this method would
* be less efficient for two-party bridges.
*
* \param is_external Whether the transfer is externally initiated
Richard Mudgett
committed
* \param transferer The channel performing a transfer
* \param bridge The bridge where the transfer is being performed
* \param exten The destination extension for the blind transfer
* \param context The destination context for the blind transfer
Mark Michelson
committed
* \param transferee The party being transferred if there is only one
* \param new_channel_cb Callback to call on channel that is created to
* facilitate the blind transfer.
* \param user_data_wrapper User-provided data needed in new_channel_cb
* \param transfer_message The Stasis publication for this transfer.
Richard Mudgett
committed
* \return The success or failure of the operation
*/
static enum ast_transfer_result blind_transfer_bridge(int is_external,
struct ast_channel *transferer, struct ast_bridge *bridge,
const char *exten, const char *context, struct ast_channel *transferee,
transfer_channel_cb new_channel_cb,
Mark Michelson
committed
struct transfer_channel_data *user_data_wrapper,
struct ast_blind_transfer_message *transfer_message)
Richard Mudgett
committed
struct ast_channel *local;
char chan_name[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 2];
int cause;
snprintf(chan_name, sizeof(chan_name), "%s@%s", exten, context);
local = ast_request("Local", ast_channel_nativeformats(transferer), NULL, transferer,
Richard Mudgett
committed
chan_name, &cause);
if (!local) {
return AST_BRIDGE_TRANSFER_FAIL;
}
ast_channel_lock_both(local, transferer);
ast_channel_req_accountcodes(local, transferer, AST_CHANNEL_REQUESTOR_REPLACEMENT);
Mark Michelson
committed
transfer_message->replace_channel = ast_channel_snapshot_get_latest(ast_channel_uniqueid(local));
if (!transfer_message->replace_channel) {
ast_hangup(local);
return AST_BRIDGE_TRANSFER_FAIL;
}
pbx_builtin_setvar_helper(local, BLINDTRANSFER, ast_channel_name(transferer));
ast_channel_unlock(local);
ast_channel_unlock(transferer);
Richard Mudgett
committed
if (new_channel_cb) {
new_channel_cb(local, user_data_wrapper, AST_BRIDGE_TRANSFER_MULTI_PARTY);
Richard Mudgett
committed
if (ast_call(local, chan_name, 0)) {
ast_hangup(local);
return AST_BRIDGE_TRANSFER_FAIL;
}
Mark Michelson
committed
if (ast_bridge_impart(bridge, local, transferer, NULL,
AST_BRIDGE_IMPART_CHAN_INDEPENDENT)) {
Richard Mudgett
committed
ast_hangup(local);
return AST_BRIDGE_TRANSFER_FAIL;
}
Mark Michelson
committed
Richard Mudgett
committed
return AST_BRIDGE_TRANSFER_SUCCESS;
}
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
/*!
* \internal
* \brief Get the transferee channel
*
* This is only applicable to cases where a transfer is occurring on a
* two-party bridge. The channels container passed in is expected to only
* contain two channels, the transferer and the transferee. The transferer
* channel is passed in as a parameter to ensure we don't return it as
* the transferee channel.
*
* \param channels A two-channel container containing the transferer and transferee
* \param transferer The party that is transfering the call
* \return The party that is being transferred
*/
static struct ast_channel *get_transferee(struct ao2_container *channels, struct ast_channel *transferer)
{
struct ao2_iterator channel_iter;
struct ast_channel *transferee;
for (channel_iter = ao2_iterator_init(channels, 0);
(transferee = ao2_iterator_next(&channel_iter));
ao2_cleanup(transferee)) {
if (transferee != transferer) {
break;
}
}
ao2_iterator_destroy(&channel_iter);
return transferee;
}
Mark Michelson
committed
/*!
* \brief Perform an attended transfer of a bridge
*
* This performs an attended transfer of an entire bridge to a target.
* The target varies, depending on what bridges exist during the transfer
* attempt.
*
* If two bridges exist, then a local channel is created to link the two
Mark Michelson
committed
* bridges together.
Mark Michelson
committed
*
* If only one bridge exists, then a local channel is created with one end
* placed into the existing bridge and the other end masquerading into
* the unbridged channel.
*
* \param chan1 Transferer channel. Guaranteed to be bridged.
* \param chan2 Other transferer channel. May or may not be bridged.
* \param bridge1 Bridge that chan1 is in. Guaranteed to be non-NULL.
* \param bridge2 Bridge that chan2 is in. If NULL, then chan2 is not bridged.
* \param publication Data to publish for a stasis attended transfer message.
Mark Michelson
committed
* \retval AST_BRIDGE_TRANSFER_FAIL Internal error occurred
* \retval AST_BRIDGE_TRANSFER_SUCCESS Succesfully transferred the bridge
*/
static enum ast_transfer_result attended_transfer_bridge(struct ast_channel *chan1,
struct ast_channel *chan2, struct ast_bridge *bridge1, struct ast_bridge *bridge2,
Mark Michelson
committed
struct ast_attended_transfer_message *transfer_msg)
Mark Michelson
committed
{
#define BRIDGE_LOCK_ONE_OR_BOTH(b1, b2) \
do { \
if (b2) { \
ast_bridge_lock_both(b1, b2); \
} else { \
ast_bridge_lock(b1); \
} \
} while (0)
Mark Michelson
committed
static const char *dest = "_attended@transfer/m";
struct ast_channel *local_chan;
int cause;
int res;
const char *app = NULL;
Mark Michelson
committed
local_chan = ast_request("Local", ast_channel_nativeformats(chan1), NULL, chan1,
Mark Michelson
committed
dest, &cause);
if (!local_chan) {
return AST_BRIDGE_TRANSFER_FAIL;
}
ast_channel_lock_both(local_chan, chan1);
ast_channel_req_accountcodes(local_chan, chan1, AST_CHANNEL_REQUESTOR_REPLACEMENT);
pbx_builtin_setvar_helper(local_chan, ATTENDEDTRANSFER, ast_channel_name(chan1));
ast_channel_unlock(local_chan);
ast_channel_unlock(chan1);
Mark Michelson
committed
if (bridge2) {
res = ast_local_setup_bridge(local_chan, bridge2, chan2, NULL);
} else {
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) {
void *tech;
Mark Michelson
committed
struct ast_channel *locals[2];
/* Have to lock everything just in case a hangup comes in early */
ast_local_lock_all2(local_chan, &tech, &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_all2(tech, locals[0], locals[1]);
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_all2(tech, locals[0], locals[1]);
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
4501
4502
4503
4504
4505
4506
4507
4508
4509
4510
4511
4512
4513
4514
4515
4516
4517
4518
4519
4520
4521
/*!
* \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
4534
4535
4536
4537
4538
4539
4540
4541
4542
4543
4544
4545
4546
4547
4548
4549
4550
4551
4552
4553
4554
4555
} 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
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
/* 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) || res == AST_BRIDGE_TRANSFER_FAIL) {
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
4963
4964
4965
4966
4967
4968
4969
4970
4971
4972
4973
4974
4975
4976
4977
4978
4979
4980
4981
4982
4983
4984
4985
4986
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
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;