Adds support for a core attended transfer function plus adds some hiding of masquerades.
authorMark Michelson <mmichelson@digium.com>
Tue, 28 May 2013 14:45:31 +0000 (14:45 +0000)
committerMark Michelson <mmichelson@digium.com>
Tue, 28 May 2013 14:45:31 +0000 (14:45 +0000)
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
apps/confbridge/confbridge_manager.c
bridges/bridge_builtin_features.c
channels/chan_mgcp.c
channels/chan_sip.c
include/asterisk/bridging.h
include/asterisk/channel.h
main/bridging.c
main/channel.c
main/features.c
main/pbx.c

diff --git a/CHANGES b/CHANGES
index ec01719..38b5cae 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -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.
index c25edc3..56fedb9 100644 (file)
@@ -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>
index 493b5c8..0ae1e19 100644 (file)
@@ -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;
index a6c161d..df82061 100644 (file)
@@ -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
index 987a973..5a4dec2 100644 (file)
@@ -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;
 
index 77fb54e..e6d6623 100644 (file)
@@ -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
@@ -879,6 +881,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.
  *
  * \param bridge_channel What to try locking
@@ -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
@@ -1512,6 +1567,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
  *
index efd1ac8..aa5fe8d 100644 (file)
@@ -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 */
index f332dfa..35cb859 100644 (file)
@@ -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,45 +3944,76 @@ 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;
@@ -3916,6 +4021,53 @@ static int check_swap_optimize_out(struct ast_bridge *chan_bridge,
 
 /*!
  * \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.
  * \since 12.0.0
  *
@@ -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)) {
@@ -5014,6 +5194,67 @@ static enum ast_transfer_result blind_transfer_bridge(struct ast_channel *transf
 }
 
 /*!
+ * \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;
 }
 
index f7aa0bf..8d62625 100644 (file)
@@ -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;
+}
index 69355e9..cd0f05a 100644 (file)
@@ -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");
index 1c26a9c..97cd1d1 100644 (file)
@@ -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)