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
changes/78/78/1
Mark Michelson 12 years ago
parent 2d2a47fae3
commit fac3839e68

@ -92,6 +92,12 @@ AMI (Asterisk Manager Interface)
* The JabberEvent event has been removed. It is not AMI's purpose to be a
carrier for another protocol.
* The Bridge Manager action's Playtone header now accepts more fine-grained
options. "Channel1" and "Channel2" may be specified in order to play a tone
to the specific channel. "Both" may be specified to play a tone to both
channels. The old "yes" option is still accepted as a way of playing the
tone to Channel2 only.
* The AMI 'Status' response event to the AMI Status action replaces the
BridgedChannel and BridgedUniqueid headers with the BridgeID header to
indicate what bridge the channel is currently in.

@ -161,6 +161,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</see-also>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="ConfbridgeTalking">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a confbridge participant unmutes.</synopsis>

@ -95,6 +95,16 @@ static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len
return res;
}
static void copy_caller_data(struct ast_channel *dest, struct ast_channel *caller)
{
ast_channel_lock_both(caller, dest);
ast_connected_line_copy_from_caller(ast_channel_connected(dest), ast_channel_caller(caller));
ast_channel_inherit_variables(caller, dest);
ast_channel_datastore_inherit(caller, dest);
ast_channel_unlock(dest);
ast_channel_unlock(caller);
}
/*! \brief Helper function that creates an outgoing channel and returns it immediately */
static struct ast_channel *dial_transfer(struct ast_channel *caller, const char *exten, const char *context)
{
@ -113,12 +123,7 @@ static struct ast_channel *dial_transfer(struct ast_channel *caller, const char
}
/* Before we actually dial out let's inherit appropriate information. */
ast_channel_lock_both(caller, chan);
ast_connected_line_copy_from_caller(ast_channel_connected(chan), ast_channel_caller(caller));
ast_channel_inherit_variables(caller, chan);
ast_channel_datastore_inherit(caller, chan);
ast_channel_unlock(chan);
ast_channel_unlock(caller);
copy_caller_data(chan, caller);
/* Since the above worked fine now we actually call it and return the channel */
if (ast_call(chan, destination, 0)) {
@ -159,19 +164,30 @@ static const char *get_transfer_context(struct ast_channel *transferer, const ch
return "default";
}
static void blind_transfer_cb(struct ast_channel *new_channel, void *user_data,
enum ast_transfer_type transfer_type)
{
struct ast_channel *transferer_channel = user_data;
if (transfer_type == AST_BRIDGE_TRANSFER_MULTI_PARTY) {
copy_caller_data(new_channel, transferer_channel);
}
}
/*! \brief Internal built in feature for blind transfers */
static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{
char exten[AST_MAX_EXTENSION] = "";
struct ast_channel *chan = NULL;
struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
const char *context;
struct ast_exten *park_exten;
char *goto_on_blindxfr;
/* BUGBUG the peer needs to be put on hold for the transfer. */
ast_channel_lock(bridge_channel->chan);
context = ast_strdupa(get_transfer_context(bridge_channel->chan,
blind_transfer ? blind_transfer->context : NULL));
goto_on_blindxfr = ast_strdupa(S_OR(pbx_builtin_getvar_helper(bridge_channel->chan,
"GOTO_ON_BLINDXFR"), ""));
ast_channel_unlock(bridge_channel->chan);
/* Grab the extension to transfer to */
@ -179,30 +195,16 @@ static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_c
return 0;
}
/* Parking blind transfer override - phase this out for something more general purpose in the future. */
park_exten = ast_get_parking_exten(exten, bridge_channel->chan, context);
if (park_exten) {
/* We are transfering the transferee to a parking lot. */
if (ast_park_blind_xfer(bridge, bridge_channel, park_exten)) {
ast_log(LOG_ERROR, "%s attempted to transfer to park application and failed.\n", ast_channel_name(bridge_channel->chan));
};
return 0;
if (!ast_strlen_zero(goto_on_blindxfr)) {
ast_debug(1, "After transfer, transferer %s goes to %s\n",
ast_channel_name(bridge_channel->chan), goto_on_blindxfr);
ast_after_bridge_set_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr);
}
/* BUGBUG just need to ast_async_goto the peer so this bridge will go away and not accumulate local channels and bridges if the destination is to an application. */
/* ast_async_goto actually is a blind transfer. */
/* BUGBUG Use the bridge count to determine if can do DTMF transfer features. If count is not 2 then don't allow it. */
/* Get a channel that is the destination we wish to call */
chan = dial_transfer(bridge_channel->chan, exten, context);
if (!chan) {
return 0;
}
/* Impart the new channel onto the bridge, and have it take our place. */
if (ast_bridge_impart(bridge_channel->bridge, chan, bridge_channel->chan, NULL, 1)) {
ast_hangup(chan);
return 0;
if (ast_bridge_transfer_blind(bridge_channel->chan, exten, context, blind_transfer_cb,
bridge_channel->chan) != AST_BRIDGE_TRANSFER_SUCCESS &&
!ast_strlen_zero(goto_on_blindxfr)) {
ast_after_bridge_goto_discard(bridge_channel->chan);
}
return 0;

@ -3236,7 +3236,7 @@ static int attempt_transfer(struct mgcp_endpoint *p, struct mgcp_subchannel *sub
ast_mutex_unlock(&p->sub->next->lock);
ast_mutex_unlock(&p->sub->lock);
res = ast_bridge_transfer_attended(sub->owner, sub->next->owner, NULL);
res = ast_bridge_transfer_attended(sub->owner, sub->next->owner);
/* Subs are only freed when the endpoint itself is destroyed, so they will continue to exist
* after ast_bridge_transfer_attended returns making this safe without reference counting

@ -26234,9 +26234,11 @@ struct blind_transfer_cb_data {
* we may send out.
*
* \param chan The new outbound channel
* \user_data A blind_transfer_cb_data struct
* \param user_data A blind_transfer_cb_data struct
* \param transfer_type Unused
*/
static void blind_transfer_cb(struct ast_channel *chan, void *user_data)
static void blind_transfer_cb(struct ast_channel *chan, void *user_data,
enum ast_transfer_type transfer_type)
{
struct blind_transfer_cb_data *cb_data = user_data;

@ -225,6 +225,8 @@ enum ast_bridge_action_type {
AST_BRIDGE_ACTION_RUN_APP,
/*! Bridge channel is to execute a blind transfer. */
AST_BRIDGE_ACTION_BLIND_TRANSFER,
/*! Bridge channel is to execute an attended transfer */
AST_BRIDGE_ACTION_ATTENDED_TRANSFER,
/*
* Bridge actions put after this comment must never be put onto
@ -878,6 +880,45 @@ int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan);
*/
int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel *peer);
/*!
* \brief Tells, if optimization is allowed, how the optimization would be performed
*/
enum ast_bridge_optimization {
/*! Optimization would swap peer into the chan_bridge */
AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE,
/*! Optimization would swap chan into the peer_bridge */
AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE,
/*! Optimization would merge peer_bridge into chan_bridge */
AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE,
/*! Optimization would merge chan_bridge into peer_bridge */
AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE,
/*! Optimization is not permitted on one or both bridges */
AST_BRIDGE_OPTIMIZE_PROHIBITED,
};
/*!
* \brief Determine if bridges allow for optimization to occur betweem them
* \since 12.0.0
*
* \param chan_bridge First bridge being tested
* \param peer_bridge Second bridge being tested
*
* This determines if two bridges allow for unreal channel optimization
* to occur between them. The function does not require for unreal channels
* to already be in the bridges when called.
*
* \note It is assumed that both bridges are locked prior to calling this function
*
* \note A return other than AST_BRIDGE_OPTIMIZE_PROHIBITED does not guarantee
* that an optimization attempt will succeed. However, a return of
* AST_BRIDGE_OPTIMIZE_PROHIBITED guarantees that an optimization attempt will
* never succeed.
*
* \returns Optimization allowability for the bridges
*/
enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge,
struct ast_bridge *peer_bridge);
/*!
* \brief Try locking the bridge_channel.
*
@ -1288,7 +1329,26 @@ enum ast_transfer_result {
AST_BRIDGE_TRANSFER_FAIL,
};
typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data);
enum ast_transfer_type {
/*! Transfer of a single party */
AST_BRIDGE_TRANSFER_SINGLE_PARTY,
/*! Transfer of multiple parties */
AST_BRIDGE_TRANSFER_MULTI_PARTY,
};
/*!
* \brief Callback function type called during blind transfers
*
* A caller of ast_bridge_transfer_blind() may wish to set data on
* the channel that ends up running dialplan. For instance, it may
* be useful to set channel variables on the channel.
*
* \param chan The involved channel
* \param user_data User-provided data needed in the callback
* \param transfer_type The type of transfer being completed
*/
typedef void (*transfer_channel_cb)(struct ast_channel *chan, void *user_data,
enum ast_transfer_type transfer_type);
/*!
* \brief Blind transfer target to the extension and context provided
@ -1326,20 +1386,15 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere
* the transfer). The second is the channel that is bridged to the transfer
* target (or if unbridged, the 'second' call of the transfer).
*
* Like with a blind transfer, a frame hook can be provided to monitor the
* resulting call after the transfer completes. If the transfer fails, the
* hook will not be attached to any call.
*
* \note Absolutely _NO_ channel locks should be held before
* calling this function.
*
* \param to_transferee Transferer channel on initial call (presumably bridged to transferee)
* \param to_transfer_target Transferer channel on consultation call (presumably bridged to transfer target)
* \param hook A frame hook to attach to the resultant call
* \return The success or failure of the attended transfer
*/
enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
struct ast_channel *to_transfer_target, struct ast_framehook *hook);
struct ast_channel *to_transfer_target);
/*!
* \brief Set channel to goto specific location after the bridge.
* \since 12.0.0
@ -1511,6 +1566,16 @@ void ast_after_bridge_callback_discard(struct ast_channel *chan, enum ast_after_
*/
int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb callback, ast_after_bridge_cb_failed failed, void *data);
/*!
* \brief Get a string representation of an after bridge callback reason
* \since 12.0.0
*
* \param reason The reason to interpret to a string
* \retval NULL Unrecognized reason
* \retval non-NULL String representation of reason
*/
const char *ast_after_bridge_cb_reason_string(enum ast_after_bridge_cb_reason reason);
/*!
* \brief Get a container of all channels in the bridge
* \since 12.0.0

@ -4229,4 +4229,55 @@ struct ast_channel *ast_channel_bridge_peer(struct ast_channel *chan);
*/
struct ast_bridge_channel *ast_channel_get_bridge_channel(struct ast_channel *chan);
/*!
* \since 12
* \brief Gain control of a channel in the system
*
* The intention of this function is to take a channel that currently
* is running in one thread and gain control of it in the current thread.
* This can be used to redirect a channel to a different place in the dialplan,
* for instance.
*
* \note This function is NOT intended to be used on bridged channels. If you
* need to control a bridged channel, you can set a callback to be called
* once the channel exits the bridge, and run your controlling logic in that
* callback
*
* XXX Put name of callback-setting function in above paragraph once it is written
*
* \note When this function returns successfully, the yankee channel is in a state where
* it cannot be used any further. Always use the returned channel instead.
*
* \note absolutely _NO_ channel locks should be held before calling this function.
*
* \param yankee The channel to gain control of
* \retval NULL Could not gain control of the channel
* \retval non-NULL The channel
*/
struct ast_channel *ast_channel_yank(struct ast_channel *yankee);
/*!
* \since 12
* \brief Move a channel from its current location to a new location
*
* The intention of this function is to have the destination channel
* take on the identity of the source channel.
*
* \note This function is NOT intended to be used on bridged channels. If you
* wish to move an unbridged channel into the place of a bridged channel, then
* use ast_bridge_join() or ast_bridge_impart(). If you wish to move a bridged
* channel into the place of another bridged channel, then use ast_bridge_move().
*
* \note When this function returns succesfully, the source channel is in a
* state where its continued use is unreliable.
*
* \note absolutely _NO_ channel locks should be held before calling this function.
*
* \param dest The place to move the source channel
* \param source The channel to move
* \retval 0 Success
* \retval non-zero Failure
*/
int ast_channel_move(struct ast_channel *dest, struct ast_channel *source);
#endif /* _ASTERISK_CHANNEL_H */

@ -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;
}

@ -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;
}

@ -288,8 +288,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Tone">
<para>Play courtesy tone to Channel 2.</para>
<enumlist>
<enum name="yes" />
<enum name="no" />
<enum name="Channel1" />
<enum name="Channel2" />
<enum name="Both" />
</enumlist>
</parameter>
</syntax>
@ -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
<managerEventInstance>
<synopsis>Raised when a bridge is successfully created due to a manager action.</synopsis>
@ -6772,15 +6804,13 @@ static int action_bridge(struct mansession *s, const struct message *m)
</see-also>
</managerEventInstance>
***/
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
<managerEventInstance>
<synopsis>Raised when an error occurs during bridge creation.</synopsis>
<see-also>
<ref type="application">Bridge</ref>
</see-also>
</managerEventInstance>
***/
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
<managerEventInstance>
<synopsis>Raised when an error occurs during bridge creation.</synopsis>
<see-also>
<ref type="application">Bridge</ref>
</see-also>
</managerEventInstance>
***/
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");

@ -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)

Loading…
Cancel
Save