From fac3839e6837241c10bee6f2563a27f1d367ddc6 Mon Sep 17 00:00:00 2001 From: Mark Michelson Date: Tue, 28 May 2013 14:45:31 +0000 Subject: Adds support for a core attended transfer function plus adds some hiding of masquerades. The attended transfer API call can complete the attended transfer in a number of ways depending on the current bridged states of the channels involved. The hiding of masquerades is done in some bridging-related functions, such as the manager Bridge action and the Bridge dialplan application. In addition, call pickup was edited to "move" a channel rather than masquerade it. Review: https://reviewboard.asterisk.org/r/2511 (closes issue ASTERISK-21334) Reported by Matt Jordan (closes issue Asterisk-21336) Reported by Matt Jordan git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389848 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- main/bridging.c | 627 +++++++++++++++++++++++++++++++++++++++++++++++--------- main/channel.c | 55 +++++ main/features.c | 479 ++++++++++++++++++++++--------------------- main/pbx.c | 76 ++----- 4 files changed, 838 insertions(+), 399 deletions(-) (limited to 'main') diff --git a/main/bridging.c b/main/bridging.c index f332dfab2..35cb85974 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -59,6 +59,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/features.h" #include "asterisk/cli.h" #include "asterisk/parking.h" +#include "asterisk/core_local.h" /*! All bridges container. */ static struct ao2_container *bridges; @@ -2003,6 +2004,48 @@ static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_chan bridge_handle_hangup(bridge_channel); } +static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data) +{ + RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); + ast_channel_move(chan_target, chan_bridged); +} + +static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data) +{ + RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup); + + ast_log(LOG_WARNING, "Unable to complete transfer: %s\n", + ast_after_bridge_cb_reason_string(reason)); +} + +static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel, + const char *target_chan_name) +{ + RAII_VAR(struct ast_channel *, chan_target, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel *, chan_bridged, NULL, ao2_cleanup); + + chan_target = ast_channel_get_by_name(target_chan_name); + if (!chan_target) { + /* Dang, it disappeared somehow */ + return; + } + + { + SCOPED_CHANNELLOCK(lock, bridge_channel); + chan_bridged = bridge_channel->chan; + if (!chan_bridged) { + return; + } + ao2_ref(chan_bridged, +1); + } + + if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel, + after_bridge_move_channel_fail, ast_channel_ref(chan_target))) { + return; + } + bridge_handle_hangup(bridge_channel); +} + /*! * \internal * \brief Handle bridge channel bridge action frame. @@ -2066,6 +2109,9 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann case AST_BRIDGE_ACTION_BLIND_TRANSFER: bridge_channel_blind_transfer(bridge_channel, action->data.ptr); break; + case AST_BRIDGE_ACTION_ATTENDED_TRANSFER: + bridge_channel_attended_transfer(bridge_channel, action->data.ptr); + break; default: break; } @@ -2714,6 +2760,23 @@ int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb return 0; } +const char *reason_strings[] = { + [AST_AFTER_BRIDGE_CB_REASON_DESTROY] = "Bridge Destroyed", + [AST_AFTER_BRIDGE_CB_REASON_REPLACED] = "Channel replaced", + [AST_AFTER_BRIDGE_CB_REASON_MASQUERADE] = "Channel masqueraded", + [AST_AFTER_BRIDGE_CB_REASON_DEPART] = "Channel departed", + [AST_AFTER_BRIDGE_CB_REASON_REMOVED] = "Channel removed", +}; + +const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason) +{ + if (reason < AST_AFTER_BRIDGE_CB_REASON_DESTROY || reason > AST_AFTER_BRIDGE_CB_REASON_REMOVED) { + return "Unknown"; + } + + return reason_strings[reason]; +} + struct after_bridge_goto_ds { /*! Goto string that can be parsed by ast_parseable_goto(). */ const char *parseable_goto; @@ -3742,6 +3805,19 @@ struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *br return other; } +static int bridge_allows_optimization(struct ast_bridge *bridge) +{ + return !(bridge->inhibit_merge + || bridge->dissolved + || ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)); +} + +static int bridge_channel_allows_optimization(struct ast_bridge_channel *bridge_channel) +{ + return bridge_channel->in_bridge + && AST_LIST_EMPTY(&bridge_channel->wr_queue); +} + /*! * \internal * \brief Lock the unreal channel stack for chan and prequalify it. @@ -3773,11 +3849,8 @@ static struct ast_bridge *optimize_lock_chan_stack(struct ast_channel *chan) ast_bridge_channel_unlock(bridge_channel); return NULL; } - if (bridge->inhibit_merge - || bridge->dissolved - || ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY) - || !bridge_channel->in_bridge - || !AST_LIST_EMPTY(&bridge_channel->wr_queue)) { + if (!bridge_channel_allows_optimization(bridge_channel) || + !bridge_allows_optimization(bridge)) { ast_bridge_unlock(bridge); ast_bridge_channel_unlock(bridge_channel); return NULL; @@ -3820,11 +3893,8 @@ static struct ast_bridge *optimize_lock_peer_stack(struct ast_channel *peer) ast_channel_unlock(peer); return NULL; } - if (bridge->inhibit_merge - || bridge->dissolved - || ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY) - || !bridge_channel->in_bridge - || !AST_LIST_EMPTY(&bridge_channel->wr_queue)) { + if (!bridge_allows_optimization(bridge) || + !bridge_channel_allows_optimization(bridge_channel)) { ast_bridge_unlock(bridge); ast_bridge_channel_unlock(bridge_channel); ast_channel_unlock(peer); @@ -3835,33 +3905,37 @@ static struct ast_bridge *optimize_lock_peer_stack(struct ast_channel *peer) /*! * \internal - * \brief Check and attempt to swap optimize out the unreal channels. - * \since 12.0.0 - * - * \param chan_bridge - * \param chan_bridge_channel - * \param peer_bridge - * \param peer_bridge_channel + * \brief Indicates allowability of a swap optimization + */ +enum bridge_allow_swap { + /*! Bridges cannot allow for a swap optimization to occur */ + SWAP_PROHIBITED, + /*! Bridge swap optimization can occur into the chan_bridge */ + SWAP_TO_CHAN_BRIDGE, + /*! Bridge swap optimization can occur into the peer_bridge */ + SWAP_TO_PEER_BRIDGE, +}; + +/*! + * \internal + * \brief Determine if two bridges allow for swap optimization to occur * - * \retval 1 if unreal channels failed to optimize out. - * \retval 0 if unreal channels were not optimized out. - * \retval -1 if unreal channels were optimized out. + * \param chan_bridge First bridge being tested + * \param peer_bridge Second bridge being tested + * \return Allowability of swap optimization */ -static int check_swap_optimize_out(struct ast_bridge *chan_bridge, - struct ast_bridge_channel *chan_bridge_channel, struct ast_bridge *peer_bridge, - struct ast_bridge_channel *peer_bridge_channel) +static enum bridge_allow_swap bridges_allow_swap_optimization(struct ast_bridge *chan_bridge, + struct ast_bridge *peer_bridge) { - struct ast_bridge *dst_bridge = NULL; - struct ast_bridge_channel *dst_bridge_channel = NULL; - struct ast_bridge_channel *src_bridge_channel = NULL; - int peer_priority; int chan_priority; - int res = 0; + int peer_priority; if (!ast_test_flag(&chan_bridge->feature_flags, - AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM) + AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | + AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) && !ast_test_flag(&peer_bridge->feature_flags, - AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)) { + AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | + AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY)) { /* * Can swap either way. Swap to the higher priority merge * bridge. @@ -3870,50 +3944,128 @@ static int check_swap_optimize_out(struct ast_bridge *chan_bridge, peer_priority = peer_bridge->v_table->get_merge_priority(peer_bridge); if (chan_bridge->num_channels == 2 && chan_priority <= peer_priority) { - dst_bridge = peer_bridge; - dst_bridge_channel = peer_bridge_channel; - src_bridge_channel = chan_bridge_channel; + return SWAP_TO_PEER_BRIDGE; } else if (peer_bridge->num_channels == 2 && peer_priority <= chan_priority) { - dst_bridge = chan_bridge; - dst_bridge_channel = chan_bridge_channel; - src_bridge_channel = peer_bridge_channel; + return SWAP_TO_CHAN_BRIDGE; } } else if (chan_bridge->num_channels == 2 - && !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM) + && !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) && !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) { /* Can swap optimize only one way. */ - dst_bridge = peer_bridge; - dst_bridge_channel = peer_bridge_channel; - src_bridge_channel = chan_bridge_channel; + return SWAP_TO_PEER_BRIDGE; } else if (peer_bridge->num_channels == 2 - && !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM) + && !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_BRIDGE_ONLY) && !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) { /* Can swap optimize only one way. */ + return SWAP_TO_CHAN_BRIDGE; + } + + return SWAP_PROHIBITED; +} + +/*! + * \internal + * \brief Check and attempt to swap optimize out the unreal channels. + * \since 12.0.0 + * + * \param chan_bridge + * \param chan_bridge_channel + * \param peer_bridge + * \param peer_bridge_channel + * + * \retval 1 if unreal channels failed to optimize out. + * \retval 0 if unreal channels were not optimized out. + * \retval -1 if unreal channels were optimized out. + */ +static int check_swap_optimize_out(struct ast_bridge *chan_bridge, + struct ast_bridge_channel *chan_bridge_channel, struct ast_bridge *peer_bridge, + struct ast_bridge_channel *peer_bridge_channel) +{ + struct ast_bridge *dst_bridge; + struct ast_bridge_channel *dst_bridge_channel; + struct ast_bridge_channel *src_bridge_channel; + struct ast_bridge_channel *other; + int res = 1; + + switch (bridges_allow_swap_optimization(chan_bridge, peer_bridge)) { + case SWAP_TO_CHAN_BRIDGE: dst_bridge = chan_bridge; dst_bridge_channel = chan_bridge_channel; src_bridge_channel = peer_bridge_channel; + break; + case SWAP_TO_PEER_BRIDGE: + dst_bridge = peer_bridge; + dst_bridge_channel = peer_bridge_channel; + src_bridge_channel = chan_bridge_channel; + break; + case SWAP_PROHIBITED: + default: + return 0; } - if (dst_bridge) { - struct ast_bridge_channel *other; - res = 1; - other = ast_bridge_channel_peer(src_bridge_channel); - if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { - ast_debug(1, "Move-swap optimizing %s <-- %s.\n", - ast_channel_name(dst_bridge_channel->chan), - ast_channel_name(other->chan)); + other = ast_bridge_channel_peer(src_bridge_channel); + if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { + ast_debug(1, "Move-swap optimizing %s <-- %s.\n", + ast_channel_name(dst_bridge_channel->chan), + ast_channel_name(other->chan)); - other->swap = dst_bridge_channel->chan; - if (!bridge_move_do(dst_bridge, other, 1)) { - ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); - res = -1; - } + other->swap = dst_bridge_channel->chan; + if (!bridge_move_do(dst_bridge, other, 1)) { + ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); + res = -1; } } return res; } +/*! + * \internal + * \brief Indicates allowability of a merge optimization + */ +enum bridge_allow_merge { + /*! Bridge properties prohibit merge optimization */ + MERGE_PROHIBITED, + /*! Merge optimization cannot occur because the source bridge has too few channels */ + MERGE_NOT_ENOUGH_CHANNELS, + /*! Merge optimization cannot occur because multimix capability could not be requested */ + MERGE_NO_MULTIMIX, + /*! Merge optimization allowed between bridges */ + MERGE_ALLOWED, +}; + +/*! + * \internal + * \brief Determines allowability of a merge optimization + * + * \note The merge output parameter is undefined if MERGE_PROHIBITED is returned. For success + * and other failure returns, a merge direction was determined, and the parameter is safe to + * access. + * + * \param chan_bridge First bridge being tested + * \param peer_bridge Second bridge being tested + * \param num_kick_channels The number of channels to remove from the bridges during merging + * \param[out] merge Indicates the recommended direction for the bridge merge + */ +static enum bridge_allow_merge bridges_allow_merge_optimization(struct ast_bridge *chan_bridge, + struct ast_bridge *peer_bridge, int num_kick_channels, struct merge_direction *merge) +{ + *merge = bridge_merge_determine_direction(chan_bridge, peer_bridge); + if (!merge->dest) { + return MERGE_PROHIBITED; + } + if (merge->src->num_channels < 2) { + return MERGE_NOT_ENOUGH_CHANNELS; + } else if ((2 + num_kick_channels) < merge->dest->num_channels + merge->src->num_channels + && !(merge->dest->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) + && (!ast_test_flag(&merge->dest->feature_flags, AST_BRIDGE_FLAG_SMART) + || !(merge->dest->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX))) { + return MERGE_NO_MULTIMIX; + } + + return MERGE_ALLOWED; +} + /*! * \internal * \brief Check and attempt to merge optimize out the unreal channels. @@ -3932,39 +4084,36 @@ static int check_merge_optimize_out(struct ast_bridge *chan_bridge, struct ast_bridge_channel *peer_bridge_channel) { struct merge_direction merge; - int res = 0; + struct ast_bridge_channel *kick_me[] = { + chan_bridge_channel, + peer_bridge_channel, + }; - merge = bridge_merge_determine_direction(chan_bridge, peer_bridge); - if (!merge.dest) { - return res; - } - if (merge.src->num_channels < 2) { + switch (bridges_allow_merge_optimization(chan_bridge, peer_bridge, ARRAY_LEN(kick_me), &merge)) { + case MERGE_ALLOWED: + break; + case MERGE_PROHIBITED: + return 0; + case MERGE_NOT_ENOUGH_CHANNELS: ast_debug(4, "Can't optimize %s -- %s out, not enough channels in bridge %s.\n", ast_channel_name(chan_bridge_channel->chan), ast_channel_name(peer_bridge_channel->chan), merge.src->uniqueid); - } else if ((2 + 2) < merge.dest->num_channels + merge.src->num_channels - && !(merge.dest->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) - && (!ast_test_flag(&merge.dest->feature_flags, AST_BRIDGE_FLAG_SMART) - || !(merge.dest->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX))) { + return 0; + case MERGE_NO_MULTIMIX: ast_debug(4, "Can't optimize %s -- %s out, multimix is needed and it cannot be acquired.\n", ast_channel_name(chan_bridge_channel->chan), ast_channel_name(peer_bridge_channel->chan)); - } else { - struct ast_bridge_channel *kick_me[] = { - chan_bridge_channel, - peer_bridge_channel, - }; + return 0; + } - ast_debug(1, "Merge optimizing %s -- %s out.\n", - ast_channel_name(chan_bridge_channel->chan), - ast_channel_name(peer_bridge_channel->chan)); + ast_debug(1, "Merge optimizing %s -- %s out.\n", + ast_channel_name(chan_bridge_channel->chan), + ast_channel_name(peer_bridge_channel->chan)); - bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me)); - res = -1; - } + bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me)); - return res; + return -1; } int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel *peer) @@ -4007,6 +4156,37 @@ int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel return res; } +enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge, + struct ast_bridge *peer_bridge) +{ + struct merge_direction merge; + + if (!bridge_allows_optimization(chan_bridge) || !bridge_allows_optimization(peer_bridge)) { + return AST_BRIDGE_OPTIMIZE_PROHIBITED; + } + + switch (bridges_allow_swap_optimization(chan_bridge, peer_bridge)) { + case SWAP_TO_CHAN_BRIDGE: + return AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE; + case SWAP_TO_PEER_BRIDGE: + return AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE; + case SWAP_PROHIBITED: + default: + break; + } + + /* Two channels will be kicked from the bridges, the unreal;1 and unreal;2 channels */ + if (bridges_allow_merge_optimization(chan_bridge, peer_bridge, 2, &merge) != MERGE_ALLOWED) { + return AST_BRIDGE_OPTIMIZE_PROHIBITED; + } + + if (merge.dest == chan_bridge) { + return AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE; + } else { + return AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE; + } +} + /*! * \internal * \brief Adjust the bridge merge inhibit request count. @@ -4999,7 +5179,7 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf } if (new_channel_cb) { - new_channel_cb(local, user_data); + new_channel_cb(local, user_data, AST_BRIDGE_TRANSFER_MULTI_PARTY); } if (ast_call(local, chan_name, 0)) { @@ -5013,6 +5193,67 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf return AST_BRIDGE_TRANSFER_SUCCESS; } +/*! + * \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 + * bridges together. Local channel optimization may result in the bridges + * merging. + * + * 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. + * \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) +{ + static const char *dest = "_attended@transfer/m"; + struct ast_channel *local_chan; + int cause; + int res; + + local_chan = ast_request("Local", ast_channel_nativeformats(chan1), chan1, + dest, &cause); + + if (!local_chan) { + return AST_BRIDGE_TRANSFER_FAIL; + } + + if (bridge2) { + res = ast_local_setup_bridge(local_chan, bridge2, chan2, NULL); + } else { + res = ast_local_setup_masquerade(local_chan, chan2); + } + + if (res) { + ast_hangup(local_chan); + return AST_BRIDGE_TRANSFER_FAIL; + } + + if (ast_call(local_chan, dest, 0)) { + ast_hangup(local_chan); + return AST_BRIDGE_TRANSFER_FAIL; + } + + if (ast_bridge_impart(bridge1, local_chan, chan1, NULL, 1)) { + ast_hangup(local_chan); + return AST_BRIDGE_TRANSFER_FAIL; + } + + return AST_BRIDGE_TRANSFER_SUCCESS; +} + /*! * \internal * \brief Get the transferee channel @@ -5076,7 +5317,7 @@ static int bridge_channel_queue_blind_transfer(struct ast_channel *transferee, } if (new_channel_cb) { - new_channel_cb(transferee, user_data); + new_channel_cb(transferee, user_data, AST_BRIDGE_TRANSFER_SINGLE_PARTY); } ast_copy_string(blind_data.exten, exten, sizeof(blind_data.exten)); @@ -5089,6 +5330,30 @@ static int bridge_channel_queue_blind_transfer(struct ast_channel *transferee, return 0; } +static int bridge_channel_queue_attended_transfer(struct ast_channel *transferee, + struct ast_channel *unbridged_chan) +{ + RAII_VAR(struct ast_bridge_channel *, transferee_bridge_channel, NULL, ao2_cleanup); + char unbridged_chan_name[AST_CHANNEL_NAME]; + + ast_channel_lock(transferee); + transferee_bridge_channel = ast_channel_get_bridge_channel(transferee); + ast_channel_unlock(transferee); + + if (!transferee_bridge_channel) { + return -1; + } + + ast_copy_string(unbridged_chan_name, ast_channel_name(unbridged_chan), + sizeof(unbridged_chan_name)); + + ast_bridge_channel_queue_action_data(transferee_bridge_channel, + AST_BRIDGE_ACTION_ATTENDED_TRANSFER, unbridged_chan_name, + sizeof(unbridged_chan_name)); + + return 0; +} + enum try_parking_result { PARKING_SUCCESS, PARKING_FAILURE, @@ -5236,27 +5501,195 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere return AST_BRIDGE_TRANSFER_SUCCESS; } +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 (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)) { + ao2_ref(bridge, -1); + bridge = NULL; + } + + return bridge; +} + +/*! + * \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); + if (bridged_to_source && bridged_to_source->state == AST_BRIDGE_CHANNEL_STATE_WAIT + && !ast_test_flag(&bridged_to_source->features->feature_flags, AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) { + bridged_to_source->swap = swap_channel; + return bridge_move_do(dest_bridge, bridged_to_source, 1) ? + AST_BRIDGE_TRANSFER_FAIL : AST_BRIDGE_TRANSFER_SUCCESS; + } 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 + * \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) +{ + struct ast_bridge_channel *kick_me[] = { + to_transferee_bridge_channel, + to_target_bridge_channel, + }; + + switch (ast_bridges_allow_optimization(to_transferee_bridge, to_target_bridge)) { + case AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE: + return bridge_swap_attended_transfer(to_transferee_bridge, to_target_bridge_channel, to_transferee); + case AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE: + return bridge_swap_attended_transfer(to_target_bridge, to_transferee_bridge_channel, to_transfer_target); + case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE: + bridge_merge_do(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me)); + return AST_BRIDGE_TRANSFER_SUCCESS; + case AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE: + bridge_merge_do(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me)); + return AST_BRIDGE_TRANSFER_SUCCESS; + 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) { + return AST_BRIDGE_TRANSFER_INVALID; + } + return attended_transfer_bridge(to_transferee, to_transfer_target, + to_transferee_bridge, to_target_bridge); + } +} + enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee, - struct ast_channel *to_transfer_target, struct ast_framehook *hook) -{ - /* First, check the validity of scenario. If invalid, return AST_BRIDGE_TRANSFER_INVALID. The following are invalid: - * 1) atxfer of an unbridged call to an unbridged call - * 2) atxfer of an unbridged call to a multi-party (n > 2) bridge - * 3) atxfer of a multi-party (n > 2) bridge to an unbridged call - * Second, check that the bridge(s) involved allows transfers. If not, return AST_BRIDGE_TRANSFER_NOT_PERMITTED. - * Third, break into different scenarios for different bridge situations: - * If both channels are bridged, perform a bridge merge. Direction of the merge is TBD. - * If channel A is bridged, and channel B is not (e.g. transferring to IVR or blond transfer) - * Some manner of masquerading is necessary. Presumably, you'd want to move channel A's bridge peer - * into where channel B is. However, it may be possible to do something a bit different, where a - * local channel is created and put into channel A's bridge. The local channel's ;2 channel - * is then masqueraded with channel B in some way. - * If channel A is not bridged and channel B is, then: - * This is similar to what is done in the previous scenario. Create a local channel and place it - * into B's bridge. Then masquerade the ;2 leg of the local channel. - */ + 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 ao2_container *, channels, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup); + struct ast_bridge *the_bridge; + struct ast_channel *chan_bridged; + struct ast_channel *chan_unbridged; + int transfer_prohibited; + int do_bridge_transfer; + + to_transferee_bridge = acquire_bridge(to_transferee); + to_target_bridge = acquire_bridge(to_transfer_target); + + /* They can't both be unbridged, you silly goose! */ + if (!to_transferee_bridge && !to_target_bridge) { + return AST_BRIDGE_TRANSFER_INVALID; + } + + /* Let's get the easy one out of the way first */ + if (to_transferee_bridge && to_target_bridge) { + 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); + enum ast_transfer_result res; + + 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); + + 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, + to_transferee_bridge, to_target_bridge); + ast_bridge_unlock(to_transferee_bridge); + ast_bridge_unlock(to_target_bridge); + + return res; + } + + 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; + + { + int chan_count; + SCOPED_LOCK(lock, the_bridge, ast_bridge_lock, ast_bridge_unlock); + + channels = ast_bridge_peers_nolock(the_bridge); + if (!channels) { + return AST_BRIDGE_TRANSFER_FAIL; + } + chan_count = ao2_container_count(channels); + if (chan_count <= 1) { + return AST_BRIDGE_TRANSFER_INVALID; + } + 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) { + return AST_BRIDGE_TRANSFER_NOT_PERMITTED; + } + + if (do_bridge_transfer) { + return attended_transfer_bridge(chan_bridged, chan_unbridged, the_bridge, NULL); + } + + transferee = get_transferee(channels, chan_bridged); + if (!transferee) { + return AST_BRIDGE_TRANSFER_FAIL; + } + + if (bridge_channel_queue_attended_transfer(transferee, chan_unbridged)) { + return AST_BRIDGE_TRANSFER_FAIL; + } - /* XXX STUB */ + ast_bridge_remove(the_bridge, chan_bridged); return AST_BRIDGE_TRANSFER_SUCCESS; } diff --git a/main/channel.c b/main/channel.c index f7aa0bf09..8d6262521 100644 --- a/main/channel.c +++ b/main/channel.c @@ -11261,3 +11261,58 @@ struct ast_bridge_channel *ast_channel_get_bridge_channel(struct ast_channel *ch } return bridge_channel; } + +struct ast_channel *ast_channel_yank(struct ast_channel *yankee) +{ + struct ast_channel *yanked_chan; + struct { + char *accountcode; + char *exten; + char *context; + char *linkedid; + char *name; + int amaflags; + struct ast_format readformat; + struct ast_format writeformat; + } my_vars = { 0, }; + + ast_channel_lock(yankee); + my_vars.accountcode = ast_strdupa(ast_channel_accountcode(yankee)); + my_vars.exten = ast_strdupa(ast_channel_exten(yankee)); + my_vars.context = ast_strdupa(ast_channel_context(yankee)); + my_vars.linkedid = ast_strdupa(ast_channel_linkedid(yankee)); + my_vars.name = ast_strdupa(ast_channel_name(yankee)); + my_vars.amaflags = ast_channel_amaflags(yankee); + ast_format_copy(&my_vars.writeformat, ast_channel_writeformat(yankee)); + ast_format_copy(&my_vars.readformat, ast_channel_readformat(yankee)); + ast_channel_unlock(yankee); + + /* Do not hold any channel locks while calling channel_alloc() since the function + * locks the channel container when linking the new channel in. */ + if (!(yanked_chan = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, my_vars.accountcode, + my_vars.exten, my_vars.context, my_vars.linkedid, my_vars.amaflags, + "Surrogate/%s", my_vars.name))) { + return NULL; + } + + /* Make formats okay */ + ast_format_copy(ast_channel_readformat(yanked_chan), &my_vars.readformat); + ast_format_copy(ast_channel_writeformat(yanked_chan), &my_vars.writeformat); + + if (ast_channel_move(yanked_chan, yankee)) { + ast_hangup(yanked_chan); + return NULL; + } + + return yanked_chan; +} + +int ast_channel_move(struct ast_channel *dest, struct ast_channel *source) +{ + if (ast_channel_masquerade(dest, source)) { + return -1; + } + + ast_do_masquerade(dest); + return 0; +} diff --git a/main/features.c b/main/features.c index 69355e920..cd0f05a0b 100644 --- a/main/features.c +++ b/main/features.c @@ -288,8 +288,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") Play courtesy tone to Channel 2. - + + + @@ -4473,24 +4475,10 @@ static void bridge_failed_peer_goto(struct ast_channel *chan, struct ast_channel } } -/*! - * \brief bridge the call and set CDR - * - * \param chan The bridge considers this channel the caller. - * \param peer The bridge considers this channel the callee. - * \param config Configuration for this bridge. - * - * Set start time, check for two channels,check if monitor on - * check for feature activation, create new CDR - * \retval res on success. - * \retval -1 on failure to bridge. - */ -int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) +static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config, + struct ast_bridge_features *chan_features, struct ast_bridge_features *peer_features) { int res; - struct ast_bridge *bridge; - struct ast_bridge_features chan_features; - struct ast_bridge_features *peer_features; /* BUGBUG these channel vars may need to be made dynamic so they update when transfers happen. */ pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer)); @@ -4523,7 +4511,6 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a /* Answer if need be */ if (ast_channel_state(chan) != AST_STATE_UP) { if (ast_raw_answer(chan, 1)) { - bridge_failed_peer_goto(chan, peer); return -1; } } @@ -4552,18 +4539,8 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a ast_channel_lock(peer); res |= ast_bridge_features_ds_set(peer, &config->features_callee); ast_channel_unlock(peer); - if (res) { - bridge_failed_peer_goto(chan, peer); - return -1; - } - /* Setup features. */ - res = ast_bridge_features_init(&chan_features); - peer_features = ast_bridge_features_new(); - if (res || !peer_features) { - ast_bridge_features_destroy(peer_features); - ast_bridge_features_cleanup(&chan_features); - bridge_failed_peer_goto(chan, peer); + if (res) { return -1; } @@ -4575,9 +4552,6 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a if (ast_bridge_features_limits_construct(&call_duration_limits_chan)) { ast_log(LOG_ERROR, "Could not construct caller duration limits. Bridge canceled.\n"); - ast_bridge_features_destroy(peer_features); - ast_bridge_features_cleanup(&chan_features); - bridge_failed_peer_goto(chan, peer); return -1; } @@ -4585,15 +4559,12 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a ast_log(LOG_ERROR, "Could not construct callee duration limits. Bridge canceled.\n"); ast_bridge_features_limits_destroy(&call_duration_limits_chan); - ast_bridge_features_destroy(peer_features); - ast_bridge_features_cleanup(&chan_features); - bridge_failed_peer_goto(chan, peer); return -1; } bridge_config_set_limits(config, &call_duration_limits_chan, &call_duration_limits_peer); - if (ast_bridge_features_set_limits(&chan_features, &call_duration_limits_chan, 0)) { + if (ast_bridge_features_set_limits(chan_features, &call_duration_limits_chan, 0)) { abandon_call = 1; } if (ast_bridge_features_set_limits(peer_features, &call_duration_limits_peer, 0)) { @@ -4606,13 +4577,49 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a if (abandon_call) { ast_log(LOG_ERROR, "Could not set duration limits on one or more sides of the call. Bridge canceled.\n"); - ast_bridge_features_destroy(peer_features); - ast_bridge_features_cleanup(&chan_features); - bridge_failed_peer_goto(chan, peer); return -1; } } + return 0; +} + +/*! + * \brief bridge the call and set CDR + * + * \param chan The bridge considers this channel the caller. + * \param peer The bridge considers this channel the callee. + * \param config Configuration for this bridge. + * + * Set start time, check for two channels,check if monitor on + * check for feature activation, create new CDR + * \retval res on success. + * \retval -1 on failure to bridge. + */ +int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config) +{ + int res; + struct ast_bridge *bridge; + struct ast_bridge_features chan_features; + struct ast_bridge_features *peer_features; + + /* Setup features. */ + res = ast_bridge_features_init(&chan_features); + peer_features = ast_bridge_features_new(); + if (res || !peer_features) { + ast_bridge_features_destroy(peer_features); + ast_bridge_features_cleanup(&chan_features); + bridge_failed_peer_goto(chan, peer); + return -1; + } + + if (pre_bridge_setup(chan, peer, config, &chan_features, peer_features)) { + ast_bridge_features_destroy(peer_features); + ast_bridge_features_cleanup(&chan_features); + bridge_failed_peer_goto(chan, peer); + return -1; + } + /* Create bridge */ bridge = ast_bridge_basic_new(); if (!bridge) { @@ -6594,45 +6601,101 @@ static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast } /*! - * \brief Actual bridge - * \param chan - * \param tmpchan - * - * Stop hold music, lock both channels, masq channels, - * after bridge return channel to next priority. - * - * \retval 0 on success. - * \retval -1 on error. + * \internal + * \brief Add an arbitrary channel to a bridge + * + * The channel that is being added to the bridge can be in any state: unbridged, + * bridged, answered, unanswered, etc. The channel will be added asynchronously, + * meaning that when this function returns once the channel has been added to + * the bridge, not once the channel has been removed from the bridge. + * + * In addition, a tone can optionally be played to the channel once the + * channel is placed into the bridge. + * + * \note When this function returns, there is no guarantee that the channel that + * was passed in is valid any longer. Do not attempt to operate on the channel + * after this function returns. + * + * \param bridge Bridge to which the channel should be added + * \param chan The channel to add to the bridge + * \param features Features for this channel in the bridge + * \param play_tone Indicates if a tone should be played to the channel + * \retval 0 Success + * \retval -1 Failure */ -static int do_bridge_masquerade(struct ast_channel *chan, struct ast_channel *tmpchan) +static int add_to_bridge(struct ast_bridge *bridge, struct ast_channel *chan, + struct ast_bridge_features *features, int play_tone) { - const char *context; - const char *exten; - int priority; + RAII_VAR(struct ast_bridge *, chan_bridge, NULL, ao2_cleanup); + struct ast_channel *bridge_chan = NULL; - ast_moh_stop(chan); - ast_channel_lock_both(chan, tmpchan); - context = ast_strdupa(ast_channel_context(chan)); - exten = ast_strdupa(ast_channel_exten(chan)); - priority = ast_channel_priority(chan); - ast_setstate(tmpchan, ast_channel_state(chan)); - ast_format_copy(ast_channel_readformat(tmpchan), ast_channel_readformat(chan)); - ast_format_copy(ast_channel_writeformat(tmpchan), ast_channel_writeformat(chan)); + ast_channel_lock(chan); + chan_bridge = ast_channel_get_bridge(chan); ast_channel_unlock(chan); - ast_channel_unlock(tmpchan); - /* Masquerade setup and execution must be done without any channel locks held */ - if (ast_channel_masquerade(tmpchan, chan)) { - return -1; + if (chan_bridge) { + if (ast_bridge_move(bridge, chan_bridge, chan, NULL, 1)) { + return -1; + } + } else { + /* Slightly less easy case. We need to yank channel A from + * where he currently is and impart him into our bridge. + */ + bridge_chan = ast_channel_yank(chan); + if (!bridge_chan) { + ast_log(LOG_WARNING, "Could not gain control of channel %s\n", ast_channel_name(chan)); + return -1; + } + if (ast_channel_state(bridge_chan) != AST_STATE_UP) { + ast_answer(bridge_chan); + } + if (ast_bridge_impart(bridge, bridge_chan, NULL, features, 1)) { + ast_log(LOG_WARNING, "Could not add %s to the bridge\n", ast_channel_name(chan)); + return -1; + } } - ast_do_masquerade(tmpchan); - /* when returning from bridge, the channel will continue at the next priority */ - ast_explicit_goto(tmpchan, context, exten, priority + 1); + if (play_tone && !ast_strlen_zero(xfersound)) { + struct ast_channel *play_chan = bridge_chan ?: chan; + RAII_VAR(struct ast_bridge_channel *, play_bridge_channel, NULL, ao2_cleanup); + + ast_channel_lock(play_chan); + play_bridge_channel = ast_channel_get_bridge_channel(play_chan); + ast_channel_unlock(play_chan); + if (!play_bridge_channel) { + ast_log(LOG_WARNING, "Unable to play tone for channel %s. Unable to get bridge channel\n", + ast_channel_name(play_chan)); + } else { + ast_bridge_channel_queue_playfile(play_bridge_channel, NULL, xfersound, NULL); + } + } return 0; } +enum play_tone_action { + PLAYTONE_NONE = 0, + PLAYTONE_CHANNEL1 = (1 << 0), + PLAYTONE_CHANNEL2 = (1 << 1), + PLAYTONE_BOTH = PLAYTONE_CHANNEL1 | PLAYTONE_CHANNEL2, +}; + +static enum play_tone_action parse_playtone(const char *playtone_val) +{ + if (ast_strlen_zero(playtone_val) || ast_false(playtone_val)) { + return PLAYTONE_NONE; + } if (!strcasecmp(playtone_val, "channel1")) { + return PLAYTONE_CHANNEL1; + } else if (!strcasecmp(playtone_val, "channel2") || ast_true(playtone_val)) { + return PLAYTONE_CHANNEL2; + } else if (!strcasecmp(playtone_val, "both")) { + return PLAYTONE_BOTH; + } else { + /* Invalid input. Assume none */ + return PLAYTONE_NONE; + } +} + /*! * \brief Bridge channels together * \param s @@ -6650,10 +6713,18 @@ static int action_bridge(struct mansession *s, const struct message *m) { const char *channela = astman_get_header(m, "Channel1"); const char *channelb = astman_get_header(m, "Channel2"); - const char *playtone = astman_get_header(m, "Tone"); - struct ast_channel *chana = NULL, *chanb = NULL, *chans[2]; - struct ast_channel *tmpchana = NULL, *tmpchanb = NULL; - struct ast_bridge_thread_obj *tobj = NULL; + enum play_tone_action playtone = parse_playtone(astman_get_header(m, "Tone")); + RAII_VAR(struct ast_channel *, chana, NULL, ao2_cleanup); + RAII_VAR(struct ast_channel *, chanb, NULL, ao2_cleanup); + const char *chana_name; + const char *chana_exten; + const char *chana_context; + int chana_priority; + const char *chanb_name; + const char *chanb_exten; + const char *chanb_context; + int chanb_priority; + struct ast_bridge *bridge; char buf[256]; /* make sure valid channels were specified */ @@ -6665,97 +6736,58 @@ static int action_bridge(struct mansession *s, const struct message *m) /* Start with chana */ chana = ast_channel_get_by_name_prefix(channela, strlen(channela)); if (!chana) { - snprintf(buf, sizeof(buf), "Channel1 does not exists: %s", channela); + snprintf(buf, sizeof(buf), "Channel1 does not exist: %s", channela); astman_send_error(s, m, buf); return 0; } - - /* Answer the channels if needed */ - if (ast_channel_state(chana) != AST_STATE_UP) - ast_answer(chana); - - /* create the placeholder channels and grab the other channels */ - if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, - NULL, NULL, ast_channel_linkedid(chana), 0, "Bridge/%s", ast_channel_name(chana)))) { - astman_send_error(s, m, "Unable to create temporary channel!"); - chana = ast_channel_unref(chana); - return 0; + ast_channel_lock(chana); + chana_name = ast_strdupa(ast_channel_name(chana)); + chana_exten = ast_strdupa(ast_channel_exten(chana)); + chana_context = ast_strdupa(ast_channel_context(chana)); + chana_priority = ast_channel_priority(chana); + if (!ast_test_flag(ast_channel_flags(chana), AST_FLAG_IN_AUTOLOOP)) { + chana_priority++; } + ast_channel_unlock(chana); - if (do_bridge_masquerade(chana, tmpchana)) { - snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channela); - astman_send_error(s, m, buf); - ast_hangup(tmpchana); - chana = ast_channel_unref(chana); - return 0; - } - - chana = ast_channel_unref(chana); - - /* now do chanb */ chanb = ast_channel_get_by_name_prefix(channelb, strlen(channelb)); if (!chanb) { - snprintf(buf, sizeof(buf), "Channel2 does not exists: %s", channelb); + snprintf(buf, sizeof(buf), "Channel2 does not exist: %s", channelb); astman_send_error(s, m, buf); - ast_hangup(tmpchana); return 0; } - - /* Answer the channels if needed */ - if (ast_channel_state(chanb) != AST_STATE_UP) - ast_answer(chanb); - - /* create the placeholder channels and grab the other channels */ - if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, - NULL, NULL, ast_channel_linkedid(chanb), 0, "Bridge/%s", ast_channel_name(chanb)))) { - astman_send_error(s, m, "Unable to create temporary channels!"); - ast_hangup(tmpchana); - chanb = ast_channel_unref(chanb); - return 0; + ast_channel_lock(chanb); + chanb_name = ast_strdupa(ast_channel_name(chanb)); + chanb_exten = ast_strdupa(ast_channel_exten(chanb)); + chanb_context = ast_strdupa(ast_channel_context(chanb)); + chanb_priority = ast_channel_priority(chanb); + if (!ast_test_flag(ast_channel_flags(chanb), AST_FLAG_IN_AUTOLOOP)) { + chanb_priority++; } + ast_channel_unlock(chanb); - if (do_bridge_masquerade(chanb, tmpchanb)) { - snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channelb); - astman_send_error(s, m, buf); - ast_hangup(tmpchana); - ast_hangup(tmpchanb); - chanb = ast_channel_unref(chanb); + bridge = ast_bridge_basic_new(); + if (!bridge) { + astman_send_error(s, m, "Unable to create bridge\n"); return 0; } - chanb = ast_channel_unref(chanb); - - /* make the channels compatible, send error if we fail doing so */ - if (ast_channel_make_compatible(tmpchana, tmpchanb)) { - ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb)); - astman_send_error(s, m, "Could not make channels compatible for manager bridge"); - ast_hangup(tmpchana); - ast_hangup(tmpchanb); + ast_after_bridge_set_go_on(chana, chana_context, chana_exten, chana_priority, NULL); + if (add_to_bridge(bridge, chana, NULL, playtone & PLAYTONE_CHANNEL1)) { + snprintf(buf, sizeof(buf), "Unable to add Channel1 to bridge: %s", ast_channel_name(chana)); + astman_send_error(s, m, buf); + ast_bridge_destroy(bridge); return 0; } - /* setup the bridge thread object and start the bridge */ - if (!(tobj = ast_calloc(1, sizeof(*tobj)))) { - ast_log(LOG_WARNING, "Unable to spawn a new bridge thread on %s and %s: %s\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb), strerror(errno)); - astman_send_error(s, m, "Unable to spawn a new bridge thread"); - ast_hangup(tmpchana); - ast_hangup(tmpchanb); + ast_after_bridge_set_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL); + if (add_to_bridge(bridge, chanb, NULL, playtone & PLAYTONE_CHANNEL2)) { + snprintf(buf, sizeof(buf), "Unable to add Channel2 to bridge: %s", ast_channel_name(chanb)); + astman_send_error(s, m, buf); + ast_bridge_destroy(bridge); return 0; } - tobj->chan = tmpchanb; - tobj->peer = tmpchana; - tobj->return_to_pbx = 1; - - if (ast_true(playtone)) { - if (!ast_strlen_zero(xfersound) && !ast_streamfile(tmpchanb, xfersound, ast_channel_language(tmpchanb))) { - if (ast_waitstream(tmpchanb, "") < 0) - ast_log(LOG_WARNING, "Failed to play a courtesy tone on chan %s\n", ast_channel_name(tmpchanb)); - } - } - - chans[0] = tmpchana; - chans[1] = tmpchanb; /*** DOCUMENTATION Raised when a bridge is successfully created due to a manager action. @@ -6772,15 +6804,13 @@ static int action_bridge(struct mansession *s, const struct message *m) ***/ - ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeAction", 2, chans, +/* BUGBUG This event used to use ast_manager_event_multichan. Now channel variables are not included in the event */ + manager_event(EVENT_FLAG_CALL, "BridgeAction", "Response: Success\r\n" "Channel1: %s\r\n" - "Channel2: %s\r\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb)); + "Channel2: %s\r\n", chana_name, chanb_name); -/* BUGBUG there seems to be no COLP update here. */ - bridge_call_thread_launch(tobj); - - astman_send_ack(s, m, "Launched bridge thread with success"); + astman_send_ack(s, m, "Channels have been bridged"); return 0; } @@ -7109,7 +7139,7 @@ int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target) /* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */ ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE); - if (ast_channel_masquerade(target, chan)) { + if (ast_channel_move(target, chan)) { ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name, target_name); goto pickup_failed; @@ -7130,8 +7160,6 @@ int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target) "TargetChannel: %s\r\n", chan_name, target_name); - /* Do the masquerade manually to make sure that it is completed. */ - ast_do_masquerade(target); res = 0; pickup_failed: @@ -7309,8 +7337,7 @@ int ast_bridge_timelimit(struct ast_channel *chan, struct ast_bridge_config *con */ static int bridge_exec(struct ast_channel *chan, const char *data) { - struct ast_channel *current_dest_chan; - struct ast_channel *final_dest_chan; + RAII_VAR(struct ast_channel *, current_dest_chan, NULL, ao2_cleanup); struct ast_channel *chans[2]; char *tmp_data = NULL; struct ast_flags opts = { 0, }; @@ -7320,6 +7347,9 @@ static int bridge_exec(struct ast_channel *chan, const char *data) const char *context; const char *extension; int priority; + struct ast_bridge_features chan_features; + struct ast_bridge_features *peer_features; + struct ast_bridge *bridge; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(dest_chan); @@ -7336,27 +7366,6 @@ static int bridge_exec(struct ast_channel *chan, const char *data) if (!ast_strlen_zero(args.options)) ast_app_parse_options(bridge_exec_options, &opts, opt_args, args.options); - /* avoid bridge with ourselves */ - if (!strcmp(ast_channel_name(chan), args.dest_chan)) { - ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", ast_channel_name(chan)); - /*** DOCUMENTATION - - Raised when an error occurs during bridge creation. - - Bridge - - - ***/ - ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", - "Response: Failed\r\n" - "Reason: Unable to bridge channel to itself\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", - ast_channel_name(chan), args.dest_chan); - pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP"); - return 0; - } - /* make sure we have a valid end point */ if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan, strlen(args.dest_chan)))) { @@ -7371,17 +7380,24 @@ static int bridge_exec(struct ast_channel *chan, const char *data) return 0; } - /* try to allocate a place holder where current_dest_chan will be placed */ - if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, - NULL, NULL, ast_channel_linkedid(current_dest_chan), 0, "Bridge/%s", ast_channel_name(current_dest_chan)))) { - ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_chan); + /* avoid bridge with ourselves */ + if (chan == current_dest_chan) { + ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", ast_channel_name(chan)); + /*** DOCUMENTATION + + Raised when an error occurs during bridge creation. + + Bridge + + + ***/ ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", "Response: Failed\r\n" - "Reason: Cannot create placeholder channel\r\n" + "Reason: Unable to bridge channel to itself\r\n" "Channel1: %s\r\n" - "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan); - pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE"); - ast_channel_unref(current_dest_chan); + "Channel2: %s\r\n", + ast_channel_name(chan), args.dest_chan); + pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP"); return 0; } @@ -7393,48 +7409,12 @@ static int bridge_exec(struct ast_channel *chan, const char *data) "Reason: Cannot setup bridge time limit\r\n" "Channel1: %s\r\n" "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan); - ast_hangup(final_dest_chan); - pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE"); - current_dest_chan = ast_channel_unref(current_dest_chan); - goto done; - } - - if (do_bridge_masquerade(current_dest_chan, final_dest_chan)) { - ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec", - "Response: Failed\r\n" - "Reason: Cannot masquerade channels\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan); - ast_hangup(final_dest_chan); pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE"); - current_dest_chan = ast_channel_unref(current_dest_chan); goto done; } - /* answer the channel if needed */ - if (ast_channel_state(final_dest_chan) != AST_STATE_UP) { - ast_answer(final_dest_chan); - } - - chans[0] = current_dest_chan; - chans[1] = final_dest_chan; - - /* now current_dest_chan is a ZOMBIE and with softhangup set to 1 and final_dest_chan is our end point */ - /* try to make compatible, send error if we fail */ - if (ast_channel_make_compatible(chan, final_dest_chan) < 0) { - ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for bridge\n", ast_channel_name(chan), ast_channel_name(final_dest_chan)); - ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans, - "Response: Failed\r\n" - "Reason: Could not make channels compatible for bridge\r\n" - "Channel1: %s\r\n" - "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan)); - - bridge_failed_peer_goto(chan, final_dest_chan); - - pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "INCOMPATIBLE"); - current_dest_chan = ast_channel_unref(current_dest_chan); - goto done; - } + chans[0] = chan; + chans[1] = current_dest_chan; /* Report that the bridge will be successfull */ /*** DOCUMENTATION @@ -7448,17 +7428,7 @@ static int bridge_exec(struct ast_channel *chan, const char *data) ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans, "Response: Success\r\n" "Channel1: %s\r\n" - "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan)); - - current_dest_chan = ast_channel_unref(current_dest_chan); - - /* we have 2 valid channels to bridge, now it is just a matter of setting up the bridge config and starting the bridge */ - if (ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE) && !ast_strlen_zero(xfersound)) { - if (!ast_streamfile(final_dest_chan, xfersound, ast_channel_language(final_dest_chan))) { - if (ast_waitstream(final_dest_chan, "") < 0) - ast_log(LOG_WARNING, "Failed to play courtesy tone on %s\n", ast_channel_name(final_dest_chan)); - } - } + "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(current_dest_chan)); if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT); @@ -7484,18 +7454,51 @@ static int bridge_exec(struct ast_channel *chan, const char *data) extension = ast_strdupa(ast_channel_exten(chan)); priority = ast_channel_priority(chan); ast_channel_unlock(chan); - ast_after_bridge_set_go_on(final_dest_chan, context, extension, priority, + ast_after_bridge_set_go_on(current_dest_chan, context, extension, priority, opt_args[OPT_ARG_CALLEE_GO_ON]); } else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) { - ast_channel_lock(final_dest_chan); - context = ast_strdupa(ast_channel_context(final_dest_chan)); - extension = ast_strdupa(ast_channel_exten(final_dest_chan)); - priority = ast_channel_priority(final_dest_chan); - ast_channel_unlock(final_dest_chan); - ast_after_bridge_set_goto(final_dest_chan, context, extension, priority); + ast_channel_lock(current_dest_chan); + context = ast_strdupa(ast_channel_context(current_dest_chan)); + extension = ast_strdupa(ast_channel_exten(current_dest_chan)); + priority = ast_channel_priority(current_dest_chan); + ast_channel_unlock(current_dest_chan); + ast_after_bridge_set_goto(current_dest_chan, context, extension, priority); + } + + if (ast_bridge_features_init(&chan_features)) { + ast_bridge_features_cleanup(&chan_features); + goto done; } - ast_bridge_call(chan, final_dest_chan, &bconfig); + peer_features = ast_bridge_features_new(); + if (!peer_features) { + ast_bridge_features_cleanup(&chan_features); + goto done; + } + + if (pre_bridge_setup(chan, current_dest_chan, &bconfig, &chan_features, peer_features)) { + ast_bridge_features_destroy(peer_features); + ast_bridge_features_cleanup(&chan_features); + goto done; + } + + bridge = ast_bridge_basic_new(); + if (!bridge) { + ast_bridge_features_destroy(peer_features); + ast_bridge_features_cleanup(&chan_features); + goto done; + } + + if (add_to_bridge(bridge, current_dest_chan, peer_features, ast_test_flag(&opts, BRIDGE_OPT_PLAYTONE))) { + ast_bridge_features_destroy(peer_features); + ast_bridge_features_cleanup(&chan_features); + ast_bridge_destroy(bridge); + goto done; + } + + ast_bridge_join(bridge, chan, NULL, &chan_features, NULL, 1); + + ast_bridge_features_cleanup(&chan_features); /* The bridge has ended, set BRIDGERESULT to SUCCESS. */ pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "SUCCESS"); diff --git a/main/pbx.c b/main/pbx.c index 1c26a9c10..97cd1d105 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -9339,20 +9339,7 @@ int ast_explicit_goto(struct ast_channel *chan, const char *context, const char int ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority) { - int res = 0; - struct ast_channel *tmpchan; - struct { - char *accountcode; - char *exten; - char *context; - char *linkedid; - char *name; - struct ast_cdr *cdr; - int amaflags; - int state; - struct ast_format readformat; - struct ast_format writeformat; - } tmpvars = { 0, }; + struct ast_channel *newchan; ast_channel_lock(chan); /* Channels in a bridge or running a PBX can be sent directly to the specified destination */ @@ -9363,63 +9350,24 @@ int ast_async_goto(struct ast_channel *chan, const char *context, const char *ex ast_explicit_goto(chan, context, exten, priority); ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO); ast_channel_unlock(chan); - return res; + return 0; } - - /* In order to do it when the channel doesn't really exist within - * the PBX, we have to make a new channel, masquerade, and start the PBX - * at the new location */ - tmpvars.accountcode = ast_strdupa(ast_channel_accountcode(chan)); - tmpvars.exten = ast_strdupa(ast_channel_exten(chan)); - tmpvars.context = ast_strdupa(ast_channel_context(chan)); - tmpvars.linkedid = ast_strdupa(ast_channel_linkedid(chan)); - tmpvars.name = ast_strdupa(ast_channel_name(chan)); - tmpvars.amaflags = ast_channel_amaflags(chan); - tmpvars.state = ast_channel_state(chan); - ast_format_copy(&tmpvars.writeformat, ast_channel_writeformat(chan)); - ast_format_copy(&tmpvars.readformat, ast_channel_readformat(chan)); - tmpvars.cdr = ast_channel_cdr(chan) ? ast_cdr_dup(ast_channel_cdr(chan)) : NULL; - ast_channel_unlock(chan); - /* Do not hold any channel locks while calling channel_alloc() since the function - * locks the channel container when linking the new channel in. */ - if (!(tmpchan = ast_channel_alloc(0, tmpvars.state, 0, 0, tmpvars.accountcode, tmpvars.exten, tmpvars.context, tmpvars.linkedid, tmpvars.amaflags, "AsyncGoto/%s", tmpvars.name))) { - ast_cdr_discard(tmpvars.cdr); + /* Otherwise, we need to gain control of the channel first */ + newchan = ast_channel_yank(chan); + if (!newchan) { + ast_log(LOG_WARNING, "Unable to gain control of channel %s\n", ast_channel_name(chan)); return -1; } - - /* copy the cdr info over */ - if (tmpvars.cdr) { - ast_cdr_discard(ast_channel_cdr(tmpchan)); - ast_channel_cdr_set(tmpchan, tmpvars.cdr); - tmpvars.cdr = NULL; - } - - /* Make formats okay */ - ast_format_copy(ast_channel_readformat(tmpchan), &tmpvars.readformat); - ast_format_copy(ast_channel_writeformat(tmpchan), &tmpvars.writeformat); - - /* Setup proper location. Never hold another channel lock while calling this function. */ - ast_explicit_goto(tmpchan, S_OR(context, tmpvars.context), S_OR(exten, tmpvars.exten), priority); - - /* Masquerade into tmp channel */ - if (ast_channel_masquerade(tmpchan, chan)) { - /* Failed to set up the masquerade. */ - ast_hangup(tmpchan); - tmpchan = NULL; - res = -1; - } else { - ast_do_masquerade(tmpchan); - /* Start the PBX going on our stolen channel */ - if (ast_pbx_start(tmpchan)) { - ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(tmpchan)); - ast_hangup(tmpchan); - res = -1; - } + ast_explicit_goto(newchan, context, exten, priority); + if (ast_pbx_start(newchan)) { + ast_hangup(newchan); + ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(newchan)); + return -1; } - return res; + return 0; } int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority) -- cgit v1.2.3