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 * The JabberEvent event has been removed. It is not AMI's purpose to be a
carrier for another protocol. 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 * The AMI 'Status' response event to the AMI Status action replaces the
BridgedChannel and BridgedUniqueid headers with the BridgeID header to BridgedChannel and BridgedUniqueid headers with the BridgeID header to
indicate what bridge the channel is currently in. indicate what bridge the channel is currently in.

@ -161,6 +161,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
</see-also> </see-also>
</managerEventInstance> </managerEventInstance>
</managerEvent> </managerEvent>
<managerEvent language="en_US" name="ConfbridgeTalking"> <managerEvent language="en_US" name="ConfbridgeTalking">
<managerEventInstance class="EVENT_FLAG_CALL"> <managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when a confbridge participant unmutes.</synopsis> <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; 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 */ /*! \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) 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. */ /* Before we actually dial out let's inherit appropriate information. */
ast_channel_lock_both(caller, chan); copy_caller_data(chan, caller);
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);
/* Since the above worked fine now we actually call it and return the channel */ /* Since the above worked fine now we actually call it and return the channel */
if (ast_call(chan, destination, 0)) { if (ast_call(chan, destination, 0)) {
@ -159,19 +164,30 @@ static const char *get_transfer_context(struct ast_channel *transferer, const ch
return "default"; 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 */ /*! \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) static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
{ {
char exten[AST_MAX_EXTENSION] = ""; char exten[AST_MAX_EXTENSION] = "";
struct ast_channel *chan = NULL;
struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt; struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
const char *context; const char *context;
struct ast_exten *park_exten; char *goto_on_blindxfr;
/* BUGBUG the peer needs to be put on hold for the transfer. */ /* BUGBUG the peer needs to be put on hold for the transfer. */
ast_channel_lock(bridge_channel->chan); ast_channel_lock(bridge_channel->chan);
context = ast_strdupa(get_transfer_context(bridge_channel->chan, context = ast_strdupa(get_transfer_context(bridge_channel->chan,
blind_transfer ? blind_transfer->context : NULL)); 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); ast_channel_unlock(bridge_channel->chan);
/* Grab the extension to transfer to */ /* 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; return 0;
} }
/* Parking blind transfer override - phase this out for something more general purpose in the future. */ if (!ast_strlen_zero(goto_on_blindxfr)) {
park_exten = ast_get_parking_exten(exten, bridge_channel->chan, context); ast_debug(1, "After transfer, transferer %s goes to %s\n",
if (park_exten) { ast_channel_name(bridge_channel->chan), goto_on_blindxfr);
/* We are transfering the transferee to a parking lot. */ ast_after_bridge_set_go_on(bridge_channel->chan, NULL, NULL, 0, goto_on_blindxfr);
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;
} }
/* 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. */ if (ast_bridge_transfer_blind(bridge_channel->chan, exten, context, blind_transfer_cb,
/* ast_async_goto actually is a blind transfer. */ bridge_channel->chan) != AST_BRIDGE_TRANSFER_SUCCESS &&
/* BUGBUG Use the bridge count to determine if can do DTMF transfer features. If count is not 2 then don't allow it. */ !ast_strlen_zero(goto_on_blindxfr)) {
ast_after_bridge_goto_discard(bridge_channel->chan);
/* 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;
} }
return 0; 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->next->lock);
ast_mutex_unlock(&p->sub->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 /* 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 * 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. * we may send out.
* *
* \param chan The new outbound channel * \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; struct blind_transfer_cb_data *cb_data = user_data;

@ -225,6 +225,8 @@ enum ast_bridge_action_type {
AST_BRIDGE_ACTION_RUN_APP, AST_BRIDGE_ACTION_RUN_APP,
/*! Bridge channel is to execute a blind transfer. */ /*! Bridge channel is to execute a blind transfer. */
AST_BRIDGE_ACTION_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 * 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); 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. * \brief Try locking the bridge_channel.
* *
@ -1288,7 +1329,26 @@ enum ast_transfer_result {
AST_BRIDGE_TRANSFER_FAIL, 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 * \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 * the transfer). The second is the channel that is bridged to the transfer
* target (or if unbridged, the 'second' call of 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 * \note Absolutely _NO_ channel locks should be held before
* calling this function. * calling this function.
* *
* \param to_transferee Transferer channel on initial call (presumably bridged to transferee) * \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 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 * \return The success or failure of the attended transfer
*/ */
enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee, 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. * \brief Set channel to goto specific location after the bridge.
* \since 12.0.0 * \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); 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 * \brief Get a container of all channels in the bridge
* \since 12.0.0 * \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); 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 */ #endif /* _ASTERISK_CHANNEL_H */

@ -59,6 +59,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/features.h" #include "asterisk/features.h"
#include "asterisk/cli.h" #include "asterisk/cli.h"
#include "asterisk/parking.h" #include "asterisk/parking.h"
#include "asterisk/core_local.h"
/*! All bridges container. */ /*! All bridges container. */
static struct ao2_container *bridges; 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); 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 * \internal
* \brief Handle bridge channel bridge action frame. * \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: case AST_BRIDGE_ACTION_BLIND_TRANSFER:
bridge_channel_blind_transfer(bridge_channel, action->data.ptr); bridge_channel_blind_transfer(bridge_channel, action->data.ptr);
break; break;
case AST_BRIDGE_ACTION_ATTENDED_TRANSFER:
bridge_channel_attended_transfer(bridge_channel, action->data.ptr);
break;
default: default:
break; break;
} }
@ -2714,6 +2760,23 @@ int ast_after_bridge_callback_set(struct ast_channel *chan, ast_after_bridge_cb
return 0; 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 { struct after_bridge_goto_ds {
/*! Goto string that can be parsed by ast_parseable_goto(). */ /*! Goto string that can be parsed by ast_parseable_goto(). */
const char *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; 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 * \internal
* \brief Lock the unreal channel stack for chan and prequalify it. * \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); ast_bridge_channel_unlock(bridge_channel);
return NULL; return NULL;
} }
if (bridge->inhibit_merge if (!bridge_channel_allows_optimization(bridge_channel) ||
|| bridge->dissolved !bridge_allows_optimization(bridge)) {
|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
|| !bridge_channel->in_bridge
|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
ast_bridge_unlock(bridge); ast_bridge_unlock(bridge);
ast_bridge_channel_unlock(bridge_channel); ast_bridge_channel_unlock(bridge_channel);
return NULL; return NULL;
@ -3820,11 +3893,8 @@ static struct ast_bridge *optimize_lock_peer_stack(struct ast_channel *peer)
ast_channel_unlock(peer); ast_channel_unlock(peer);
return NULL; return NULL;
} }
if (bridge->inhibit_merge if (!bridge_allows_optimization(bridge) ||
|| bridge->dissolved !bridge_channel_allows_optimization(bridge_channel)) {
|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
|| !bridge_channel->in_bridge
|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
ast_bridge_unlock(bridge); ast_bridge_unlock(bridge);
ast_bridge_channel_unlock(bridge_channel); ast_bridge_channel_unlock(bridge_channel);
ast_channel_unlock(peer); ast_channel_unlock(peer);
@ -3835,33 +3905,37 @@ static struct ast_bridge *optimize_lock_peer_stack(struct ast_channel *peer)
/*! /*!
* \internal * \internal
* \brief Check and attempt to swap optimize out the unreal channels. * \brief Indicates allowability of a swap optimization
* \since 12.0.0 */
* enum bridge_allow_swap {
* \param chan_bridge /*! Bridges cannot allow for a swap optimization to occur */
* \param chan_bridge_channel SWAP_PROHIBITED,
* \param peer_bridge /*! Bridge swap optimization can occur into the chan_bridge */
* \param peer_bridge_channel 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. * \param chan_bridge First bridge being tested
* \retval 0 if unreal channels were not optimized out. * \param peer_bridge Second bridge being tested
* \retval -1 if unreal channels were optimized out. * \return Allowability of swap optimization
*/ */
static int check_swap_optimize_out(struct ast_bridge *chan_bridge, static enum bridge_allow_swap bridges_allow_swap_optimization(struct ast_bridge *chan_bridge,
struct ast_bridge_channel *chan_bridge_channel, struct ast_bridge *peer_bridge, struct ast_bridge *peer_bridge)
struct ast_bridge_channel *peer_bridge_channel)
{ {
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 chan_priority;
int res = 0; int peer_priority;
if (!ast_test_flag(&chan_bridge->feature_flags, 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_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 * Can swap either way. Swap to the higher priority merge
* bridge. * 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); peer_priority = peer_bridge->v_table->get_merge_priority(peer_bridge);
if (chan_bridge->num_channels == 2 if (chan_bridge->num_channels == 2
&& chan_priority <= peer_priority) { && chan_priority <= peer_priority) {
dst_bridge = peer_bridge; return SWAP_TO_PEER_BRIDGE;
dst_bridge_channel = peer_bridge_channel;
src_bridge_channel = chan_bridge_channel;
} else if (peer_bridge->num_channels == 2 } else if (peer_bridge->num_channels == 2
&& peer_priority <= chan_priority) { && peer_priority <= chan_priority) {
dst_bridge = chan_bridge; return SWAP_TO_CHAN_BRIDGE;
dst_bridge_channel = chan_bridge_channel;
src_bridge_channel = peer_bridge_channel;
} }
} else if (chan_bridge->num_channels == 2 } 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)) { && !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
/* Can swap optimize only one way. */ /* Can swap optimize only one way. */
dst_bridge = peer_bridge; return SWAP_TO_PEER_BRIDGE;
dst_bridge_channel = peer_bridge_channel;
src_bridge_channel = chan_bridge_channel;
} else if (peer_bridge->num_channels == 2 } 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)) { && !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
/* Can swap optimize only one way. */ /* 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 = chan_bridge;
dst_bridge_channel = chan_bridge_channel; dst_bridge_channel = chan_bridge_channel;
src_bridge_channel = peer_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);
other = ast_bridge_channel_peer(src_bridge_channel); if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { ast_debug(1, "Move-swap optimizing %s <-- %s.\n",
ast_debug(1, "Move-swap optimizing %s <-- %s.\n", ast_channel_name(dst_bridge_channel->chan),
ast_channel_name(dst_bridge_channel->chan), ast_channel_name(other->chan));
ast_channel_name(other->chan));
other->swap = dst_bridge_channel->chan; other->swap = dst_bridge_channel->chan;
if (!bridge_move_do(dst_bridge, other, 1)) { if (!bridge_move_do(dst_bridge, other, 1)) {
ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
res = -1; res = -1;
}
} }
} }
return res; 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 * \internal
* \brief Check and attempt to merge optimize out the unreal channels. * \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 ast_bridge_channel *peer_bridge_channel)
{ {
struct merge_direction merge; 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); switch (bridges_allow_merge_optimization(chan_bridge, peer_bridge, ARRAY_LEN(kick_me), &merge)) {
if (!merge.dest) { case MERGE_ALLOWED:
return res; break;
} case MERGE_PROHIBITED:
if (merge.src->num_channels < 2) { return 0;
case MERGE_NOT_ENOUGH_CHANNELS:
ast_debug(4, "Can't optimize %s -- %s out, not enough channels in bridge %s.\n", 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(chan_bridge_channel->chan),
ast_channel_name(peer_bridge_channel->chan), ast_channel_name(peer_bridge_channel->chan),
merge.src->uniqueid); merge.src->uniqueid);
} else if ((2 + 2) < merge.dest->num_channels + merge.src->num_channels return 0;
&& !(merge.dest->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) case MERGE_NO_MULTIMIX:
&& (!ast_test_flag(&merge.dest->feature_flags, AST_BRIDGE_FLAG_SMART)
|| !(merge.dest->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX))) {
ast_debug(4, "Can't optimize %s -- %s out, multimix is needed and it cannot be acquired.\n", 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(chan_bridge_channel->chan),
ast_channel_name(peer_bridge_channel->chan)); ast_channel_name(peer_bridge_channel->chan));
} else { return 0;
struct ast_bridge_channel *kick_me[] = { }
chan_bridge_channel,
peer_bridge_channel,
};
ast_debug(1, "Merge optimizing %s -- %s out.\n", ast_debug(1, "Merge optimizing %s -- %s out.\n",
ast_channel_name(chan_bridge_channel->chan), ast_channel_name(chan_bridge_channel->chan),
ast_channel_name(peer_bridge_channel->chan)); ast_channel_name(peer_bridge_channel->chan));
bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me)); bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me));
res = -1;
}
return res; return -1;
} }
int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel *peer) 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; 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 * \internal
* \brief Adjust the bridge merge inhibit request count. * \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) { 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)) { 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; 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 * \internal
* \brief Get the transferee channel * \brief Get the transferee channel
@ -5076,7 +5317,7 @@ static int bridge_channel_queue_blind_transfer(struct ast_channel *transferee,
} }
if (new_channel_cb) { 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)); 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; 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 { enum try_parking_result {
PARKING_SUCCESS, PARKING_SUCCESS,
PARKING_FAILURE, PARKING_FAILURE,
@ -5236,27 +5501,195 @@ enum ast_transfer_result ast_bridge_transfer_blind(struct ast_channel *transfere
return AST_BRIDGE_TRANSFER_SUCCESS; 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, 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)
{ {
/* First, check the validity of scenario. If invalid, return AST_BRIDGE_TRANSFER_INVALID. The following are invalid: RAII_VAR(struct ast_bridge *, to_transferee_bridge, NULL, ao2_cleanup);
* 1) atxfer of an unbridged call to an unbridged call RAII_VAR(struct ast_bridge *, to_target_bridge, NULL, ao2_cleanup);
* 2) atxfer of an unbridged call to a multi-party (n > 2) bridge RAII_VAR(struct ao2_container *, channels, NULL, ao2_cleanup);
* 3) atxfer of a multi-party (n > 2) bridge to an unbridged call RAII_VAR(struct ast_channel *, transferee, NULL, ao2_cleanup);
* Second, check that the bridge(s) involved allows transfers. If not, return AST_BRIDGE_TRANSFER_NOT_PERMITTED. struct ast_bridge *the_bridge;
* Third, break into different scenarios for different bridge situations: struct ast_channel *chan_bridged;
* If both channels are bridged, perform a bridge merge. Direction of the merge is TBD. struct ast_channel *chan_unbridged;
* If channel A is bridged, and channel B is not (e.g. transferring to IVR or blond transfer) int transfer_prohibited;
* Some manner of masquerading is necessary. Presumably, you'd want to move channel A's bridge peer int do_bridge_transfer;
* 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 to_transferee_bridge = acquire_bridge(to_transferee);
* is then masqueraded with channel B in some way. to_target_bridge = acquire_bridge(to_transfer_target);
* 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 /* They can't both be unbridged, you silly goose! */
* into B's bridge. Then masquerade the ;2 leg of the local channel. 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; 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; 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"> <parameter name="Tone">
<para>Play courtesy tone to Channel 2.</para> <para>Play courtesy tone to Channel 2.</para>
<enumlist> <enumlist>
<enum name="yes" />
<enum name="no" /> <enum name="no" />
<enum name="Channel1" />
<enum name="Channel2" />
<enum name="Both" />
</enumlist> </enumlist>
</parameter> </parameter>
</syntax> </syntax>
@ -4473,24 +4475,10 @@ static void bridge_failed_peer_goto(struct ast_channel *chan, struct ast_channel
} }
} }
/*! static int pre_bridge_setup(struct ast_channel *chan, struct ast_channel *peer, struct ast_bridge_config *config,
* \brief bridge the call and set CDR struct ast_bridge_features *chan_features, struct ast_bridge_features *peer_features)
*
* \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; 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. */ /* 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)); 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 */ /* Answer if need be */
if (ast_channel_state(chan) != AST_STATE_UP) { if (ast_channel_state(chan) != AST_STATE_UP) {
if (ast_raw_answer(chan, 1)) { if (ast_raw_answer(chan, 1)) {
bridge_failed_peer_goto(chan, peer);
return -1; return -1;
} }
} }
@ -4552,18 +4539,8 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a
ast_channel_lock(peer); ast_channel_lock(peer);
res |= ast_bridge_features_ds_set(peer, &config->features_callee); res |= ast_bridge_features_ds_set(peer, &config->features_callee);
ast_channel_unlock(peer); ast_channel_unlock(peer);
if (res) {
bridge_failed_peer_goto(chan, peer);
return -1;
}
/* Setup features. */ if (res) {
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; 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)) { if (ast_bridge_features_limits_construct(&call_duration_limits_chan)) {
ast_log(LOG_ERROR, "Could not construct caller duration limits. Bridge canceled.\n"); 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; 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_log(LOG_ERROR, "Could not construct callee duration limits. Bridge canceled.\n");
ast_bridge_features_limits_destroy(&call_duration_limits_chan); 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; return -1;
} }
bridge_config_set_limits(config, &call_duration_limits_chan, &call_duration_limits_peer); 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; abandon_call = 1;
} }
if (ast_bridge_features_set_limits(peer_features, &call_duration_limits_peer, 0)) { 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) { if (abandon_call) {
ast_log(LOG_ERROR, "Could not set duration limits on one or more sides of the call. Bridge canceled.\n"); 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 -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 */ /* Create bridge */
bridge = ast_bridge_basic_new(); bridge = ast_bridge_basic_new();
if (!bridge) { if (!bridge) {
@ -6594,45 +6601,101 @@ static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast
} }
/*! /*!
* \brief Actual bridge * \internal
* \param chan * \brief Add an arbitrary channel to a bridge
* \param tmpchan *
* * The channel that is being added to the bridge can be in any state: unbridged,
* Stop hold music, lock both channels, masq channels, * bridged, answered, unanswered, etc. The channel will be added asynchronously,
* after bridge return channel to next priority. * 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.
* \retval 0 on success. *
* \retval -1 on error. * 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; RAII_VAR(struct ast_bridge *, chan_bridge, NULL, ao2_cleanup);
const char *exten; struct ast_channel *bridge_chan = NULL;
int priority;
ast_moh_stop(chan); ast_channel_lock(chan);
ast_channel_lock_both(chan, tmpchan); chan_bridge = ast_channel_get_bridge(chan);
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_unlock(chan); ast_channel_unlock(chan);
ast_channel_unlock(tmpchan);
/* Masquerade setup and execution must be done without any channel locks held */ if (chan_bridge) {
if (ast_channel_masquerade(tmpchan, chan)) { if (ast_bridge_move(bridge, chan_bridge, chan, NULL, 1)) {
return -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 */ if (play_tone && !ast_strlen_zero(xfersound)) {
ast_explicit_goto(tmpchan, context, exten, priority + 1); 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; 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 * \brief Bridge channels together
* \param s * \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 *channela = astman_get_header(m, "Channel1");
const char *channelb = astman_get_header(m, "Channel2"); const char *channelb = astman_get_header(m, "Channel2");
const char *playtone = astman_get_header(m, "Tone"); enum play_tone_action playtone = parse_playtone(astman_get_header(m, "Tone"));
struct ast_channel *chana = NULL, *chanb = NULL, *chans[2]; RAII_VAR(struct ast_channel *, chana, NULL, ao2_cleanup);
struct ast_channel *tmpchana = NULL, *tmpchanb = NULL; RAII_VAR(struct ast_channel *, chanb, NULL, ao2_cleanup);
struct ast_bridge_thread_obj *tobj = NULL; 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]; char buf[256];
/* make sure valid channels were specified */ /* make sure valid channels were specified */
@ -6665,97 +6736,58 @@ static int action_bridge(struct mansession *s, const struct message *m)
/* Start with chana */ /* Start with chana */
chana = ast_channel_get_by_name_prefix(channela, strlen(channela)); chana = ast_channel_get_by_name_prefix(channela, strlen(channela));
if (!chana) { 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); astman_send_error(s, m, buf);
return 0; return 0;
} }
ast_channel_lock(chana);
/* Answer the channels if needed */ chana_name = ast_strdupa(ast_channel_name(chana));
if (ast_channel_state(chana) != AST_STATE_UP) chana_exten = ast_strdupa(ast_channel_exten(chana));
ast_answer(chana); chana_context = ast_strdupa(ast_channel_context(chana));
chana_priority = ast_channel_priority(chana);
/* create the placeholder channels and grab the other channels */ if (!ast_test_flag(ast_channel_flags(chana), AST_FLAG_IN_AUTOLOOP)) {
if (!(tmpchana = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, chana_priority++;
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_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)); chanb = ast_channel_get_by_name_prefix(channelb, strlen(channelb));
if (!chanb) { 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); astman_send_error(s, m, buf);
ast_hangup(tmpchana);
return 0; return 0;
} }
ast_channel_lock(chanb);
/* Answer the channels if needed */ chanb_name = ast_strdupa(ast_channel_name(chanb));
if (ast_channel_state(chanb) != AST_STATE_UP) chanb_exten = ast_strdupa(ast_channel_exten(chanb));
ast_answer(chanb); chanb_context = ast_strdupa(ast_channel_context(chanb));
chanb_priority = ast_channel_priority(chanb);
/* create the placeholder channels and grab the other channels */ if (!ast_test_flag(ast_channel_flags(chanb), AST_FLAG_IN_AUTOLOOP)) {
if (!(tmpchanb = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, chanb_priority++;
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_unlock(chanb);
if (do_bridge_masquerade(chanb, tmpchanb)) { bridge = ast_bridge_basic_new();
snprintf(buf, sizeof(buf), "Unable to masquerade channel %s!", channelb); if (!bridge) {
astman_send_error(s, m, buf); astman_send_error(s, m, "Unable to create bridge\n");
ast_hangup(tmpchana);
ast_hangup(tmpchanb);
chanb = ast_channel_unref(chanb);
return 0; return 0;
} }
chanb = ast_channel_unref(chanb); ast_after_bridge_set_go_on(chana, chana_context, chana_exten, chana_priority, NULL);
if (add_to_bridge(bridge, chana, NULL, playtone & PLAYTONE_CHANNEL1)) {
/* make the channels compatible, send error if we fail doing so */ snprintf(buf, sizeof(buf), "Unable to add Channel1 to bridge: %s", ast_channel_name(chana));
if (ast_channel_make_compatible(tmpchana, tmpchanb)) { astman_send_error(s, m, buf);
ast_log(LOG_WARNING, "Could not make channels %s and %s compatible for manager bridge\n", ast_channel_name(tmpchana), ast_channel_name(tmpchanb)); ast_bridge_destroy(bridge);
astman_send_error(s, m, "Could not make channels compatible for manager bridge");
ast_hangup(tmpchana);
ast_hangup(tmpchanb);
return 0; return 0;
} }
/* setup the bridge thread object and start the bridge */ ast_after_bridge_set_go_on(chanb, chanb_context, chanb_exten, chanb_priority, NULL);
if (!(tobj = ast_calloc(1, sizeof(*tobj)))) { if (add_to_bridge(bridge, chanb, NULL, playtone & PLAYTONE_CHANNEL2)) {
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)); snprintf(buf, sizeof(buf), "Unable to add Channel2 to bridge: %s", ast_channel_name(chanb));
astman_send_error(s, m, "Unable to spawn a new bridge thread"); astman_send_error(s, m, buf);
ast_hangup(tmpchana); ast_bridge_destroy(bridge);
ast_hangup(tmpchanb);
return 0; 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 /*** DOCUMENTATION
<managerEventInstance> <managerEventInstance>
<synopsis>Raised when a bridge is successfully created due to a manager action.</synopsis> <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> </see-also>
</managerEventInstance> </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" "Response: Success\r\n"
"Channel1: %s\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. */ astman_send_ack(s, m, "Channels have been bridged");
bridge_call_thread_launch(tobj);
astman_send_ack(s, m, "Launched bridge thread with success");
return 0; 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 */ /* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */
ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE); 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, ast_log(LOG_WARNING, "Unable to masquerade '%s' into '%s'\n", chan_name,
target_name); target_name);
goto pickup_failed; goto pickup_failed;
@ -7130,8 +7160,6 @@ int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target)
"TargetChannel: %s\r\n", "TargetChannel: %s\r\n",
chan_name, target_name); chan_name, target_name);
/* Do the masquerade manually to make sure that it is completed. */
ast_do_masquerade(target);
res = 0; res = 0;
pickup_failed: 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) static int bridge_exec(struct ast_channel *chan, const char *data)
{ {
struct ast_channel *current_dest_chan; RAII_VAR(struct ast_channel *, current_dest_chan, NULL, ao2_cleanup);
struct ast_channel *final_dest_chan;
struct ast_channel *chans[2]; struct ast_channel *chans[2];
char *tmp_data = NULL; char *tmp_data = NULL;
struct ast_flags opts = { 0, }; 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 *context;
const char *extension; const char *extension;
int priority; int priority;
struct ast_bridge_features chan_features;
struct ast_bridge_features *peer_features;
struct ast_bridge *bridge;
AST_DECLARE_APP_ARGS(args, AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(dest_chan); 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)) if (!ast_strlen_zero(args.options))
ast_app_parse_options(bridge_exec_options, &opts, opt_args, 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 */ /* make sure we have a valid end point */
if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan, if (!(current_dest_chan = ast_channel_get_by_name_prefix(args.dest_chan,
strlen(args.dest_chan)))) { strlen(args.dest_chan)))) {
@ -7371,17 +7380,24 @@ static int bridge_exec(struct ast_channel *chan, const char *data)
return 0; return 0;
} }
/* try to allocate a place holder where current_dest_chan will be placed */ /* avoid bridge with ourselves */
if (!(final_dest_chan = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, if (chan == current_dest_chan) {
NULL, NULL, ast_channel_linkedid(current_dest_chan), 0, "Bridge/%s", ast_channel_name(current_dest_chan)))) { ast_log(LOG_WARNING, "Unable to bridge channel %s with itself\n", ast_channel_name(chan));
ast_log(LOG_WARNING, "Cannot create placeholder channel for chan %s\n", args.dest_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", ast_manager_event(chan, EVENT_FLAG_CALL, "BridgeExec",
"Response: Failed\r\n" "Response: Failed\r\n"
"Reason: Cannot create placeholder channel\r\n" "Reason: Unable to bridge channel to itself\r\n"
"Channel1: %s\r\n" "Channel1: %s\r\n"
"Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan); "Channel2: %s\r\n",
pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE"); ast_channel_name(chan), args.dest_chan);
ast_channel_unref(current_dest_chan); pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "LOOP");
return 0; 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" "Reason: Cannot setup bridge time limit\r\n"
"Channel1: %s\r\n" "Channel1: %s\r\n"
"Channel2: %s\r\n", ast_channel_name(chan), args.dest_chan); "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"); pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "FAILURE");
current_dest_chan = ast_channel_unref(current_dest_chan);
goto done; goto done;
} }
/* answer the channel if needed */ chans[0] = chan;
if (ast_channel_state(final_dest_chan) != AST_STATE_UP) { chans[1] = current_dest_chan;
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;
}
/* Report that the bridge will be successfull */ /* Report that the bridge will be successfull */
/*** DOCUMENTATION /*** 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, ast_manager_event_multichan(EVENT_FLAG_CALL, "BridgeExec", 2, chans,
"Response: Success\r\n" "Response: Success\r\n"
"Channel1: %s\r\n" "Channel1: %s\r\n"
"Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(final_dest_chan)); "Channel2: %s\r\n", ast_channel_name(chan), ast_channel_name(current_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));
}
}
if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER))
ast_set_flag(&(bconfig.features_callee), AST_FEATURE_REDIRECT); 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)); extension = ast_strdupa(ast_channel_exten(chan));
priority = ast_channel_priority(chan); priority = ast_channel_priority(chan);
ast_channel_unlock(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]); opt_args[OPT_ARG_CALLEE_GO_ON]);
} else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) { } else if (!ast_test_flag(&opts, OPT_CALLEE_KILL)) {
ast_channel_lock(final_dest_chan); ast_channel_lock(current_dest_chan);
context = ast_strdupa(ast_channel_context(final_dest_chan)); context = ast_strdupa(ast_channel_context(current_dest_chan));
extension = ast_strdupa(ast_channel_exten(final_dest_chan)); extension = ast_strdupa(ast_channel_exten(current_dest_chan));
priority = ast_channel_priority(final_dest_chan); priority = ast_channel_priority(current_dest_chan);
ast_channel_unlock(final_dest_chan); ast_channel_unlock(current_dest_chan);
ast_after_bridge_set_goto(final_dest_chan, context, extension, priority); 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. */ /* The bridge has ended, set BRIDGERESULT to SUCCESS. */
pbx_builtin_setvar_helper(chan, "BRIDGERESULT", "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 ast_async_goto(struct ast_channel *chan, const char *context, const char *exten, int priority)
{ {
int res = 0; struct ast_channel *newchan;
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, };
ast_channel_lock(chan); ast_channel_lock(chan);
/* Channels in a bridge or running a PBX can be sent directly to the specified destination */ /* 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_explicit_goto(chan, context, exten, priority);
ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO); ast_softhangup_nolock(chan, AST_SOFTHANGUP_ASYNCGOTO);
ast_channel_unlock(chan); 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); ast_channel_unlock(chan);
/* Do not hold any channel locks while calling channel_alloc() since the function /* Otherwise, we need to gain control of the channel first */
* locks the channel container when linking the new channel in. */ newchan = ast_channel_yank(chan);
if (!(tmpchan = ast_channel_alloc(0, tmpvars.state, 0, 0, tmpvars.accountcode, tmpvars.exten, tmpvars.context, tmpvars.linkedid, tmpvars.amaflags, "AsyncGoto/%s", tmpvars.name))) { if (!newchan) {
ast_cdr_discard(tmpvars.cdr); ast_log(LOG_WARNING, "Unable to gain control of channel %s\n", ast_channel_name(chan));
return -1; return -1;
} }
ast_explicit_goto(newchan, context, exten, priority);
/* copy the cdr info over */ if (ast_pbx_start(newchan)) {
if (tmpvars.cdr) { ast_hangup(newchan);
ast_cdr_discard(ast_channel_cdr(tmpchan)); ast_log(LOG_WARNING, "Unable to start PBX on %s\n", ast_channel_name(newchan));
ast_channel_cdr_set(tmpchan, tmpvars.cdr); return -1;
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;
}
} }
return res; return 0;
} }
int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority) int ast_async_goto_by_name(const char *channame, const char *context, const char *exten, int priority)

Loading…
Cancel
Save