Refactor chan_dahdi/sig_analog/sig_pri and chan_misdn to use the common transfer...
authorRichard Mudgett <rmudgett@digium.com>
Fri, 7 Jun 2013 01:06:49 +0000 (01:06 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Fri, 7 Jun 2013 01:06:49 +0000 (01:06 +0000)
(closes issue ASTERISK-21523)
Reported by: Matt Jordan

(closes issue ASTERISK-21524)
Reported by: Matt Jordan

Review: https://reviewboard.asterisk.org/r/2600/

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390804 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_dahdi.c
channels/chan_misdn.c
channels/sig_analog.c
channels/sig_pri.c
channels/sig_pri.h
main/channel.c

index 92de023..109fac0 100644 (file)
@@ -131,6 +131,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/ccss.h"
 #include "asterisk/data.h"
 #include "asterisk/features_config.h"
+#include "asterisk/bridging.h"
 
 /*** DOCUMENTATION
        <application name="DAHDISendKeypadFacility" language="en_US">
@@ -7841,55 +7842,49 @@ static int dahdi_ring_phone(struct dahdi_pvt *p)
 
 static void *analog_ss_thread(void *data);
 
+/*!
+ * \internal
+ * \brief Attempt to transfer 3-way call.
+ *
+ * \param p DAHDI private structure.
+ *
+ * \note On entry these locks are held: real-call, private, 3-way call.
+ * \note On exit these locks are held: real-call, private.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
 static int attempt_transfer(struct dahdi_pvt *p)
 {
-       /* In order to transfer, we need at least one of the channels to
-          actually be in a call bridge.  We can't conference two applications
-          together (but then, why would we want to?) */
-       if (ast_bridged_channel(p->subs[SUB_REAL].owner)) {
-               /* The three-way person we're about to transfer to could still be in MOH, so
-                  stop it now */
-               ast_queue_unhold(p->subs[SUB_THREEWAY].owner);
-               if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RINGING) {
-                       ast_queue_control(p->subs[SUB_REAL].owner, AST_CONTROL_RINGING);
-               }
-               if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RING) {
-                       tone_zone_play_tone(p->subs[SUB_THREEWAY].dfd, DAHDI_TONE_RINGTONE);
-               }
-                if (ast_channel_masquerade(p->subs[SUB_THREEWAY].owner, ast_bridged_channel(p->subs[SUB_REAL].owner))) {
-                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                                       ast_channel_name(ast_bridged_channel(p->subs[SUB_REAL].owner)), ast_channel_name(p->subs[SUB_THREEWAY].owner));
-                       return -1;
-               }
-               /* Orphan the channel after releasing the lock */
-               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-               unalloc_sub(p, SUB_THREEWAY);
-       } else if (ast_bridged_channel(p->subs[SUB_THREEWAY].owner)) {
-               ast_queue_unhold(p->subs[SUB_REAL].owner);
-               if (ast_channel_state(p->subs[SUB_THREEWAY].owner) == AST_STATE_RINGING) {
-                       ast_queue_control(p->subs[SUB_THREEWAY].owner, AST_CONTROL_RINGING);
-               }
-               if (ast_channel_state(p->subs[SUB_REAL].owner) == AST_STATE_RING) {
-                       tone_zone_play_tone(p->subs[SUB_REAL].dfd, DAHDI_TONE_RINGTONE);
-               }
-               if (ast_channel_masquerade(p->subs[SUB_REAL].owner, ast_bridged_channel(p->subs[SUB_THREEWAY].owner))) {
-                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                                       ast_channel_name(ast_bridged_channel(p->subs[SUB_THREEWAY].owner)), ast_channel_name(p->subs[SUB_REAL].owner));
-                       return -1;
-               }
-               /* Three-way is now the REAL */
-               swap_subs(p, SUB_THREEWAY, SUB_REAL);
-               ast_channel_unlock(p->subs[SUB_REAL].owner);
-               unalloc_sub(p, SUB_THREEWAY);
-               /* Tell the caller not to hangup */
-               return 1;
-       } else {
-               ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
-                       ast_channel_name(p->subs[SUB_REAL].owner), ast_channel_name(p->subs[SUB_THREEWAY].owner));
-               ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
-               return -1;
+       struct ast_channel *owner_real;
+       struct ast_channel *owner_3way;
+       enum ast_transfer_result xfer_res;
+       int res = 0;
+
+       owner_real = ast_channel_ref(p->subs[SUB_REAL].owner);
+       owner_3way = ast_channel_ref(p->subs[SUB_THREEWAY].owner);
+
+       ast_verb(3, "TRANSFERRING %s to %s\n",
+               ast_channel_name(owner_3way), ast_channel_name(owner_real));
+
+       ast_channel_unlock(owner_real);
+       ast_channel_unlock(owner_3way);
+       ast_mutex_unlock(&p->lock);
+
+       xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
+       if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
+               ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
+               res = -1;
        }
-       return 0;
+
+       /* Must leave with these locked. */
+       ast_channel_lock(owner_real);
+       ast_mutex_lock(&p->lock);
+
+       ast_channel_unref(owner_real);
+       ast_channel_unref(owner_3way);
+
+       return res;
 }
 
 static int check_for_conference(struct dahdi_pvt *p)
@@ -8399,17 +8394,13 @@ static struct ast_frame *dahdi_handle_event(struct ast_channel *ast)
                                                                p->owner = NULL;
                                                                /* Ring the phone */
                                                                dahdi_ring_phone(p);
-                                                       } else {
-                                                               if ((res = attempt_transfer(p)) < 0) {
-                                                                       ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
-                                                                       if (p->subs[SUB_THREEWAY].owner)
-                                                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-                                                               } else if (res) {
-                                                                       /* Don't actually hang up at this point */
-                                                                       if (p->subs[SUB_THREEWAY].owner)
-                                                                               ast_channel_unlock(p->subs[SUB_THREEWAY].owner);
-                                                                       break;
-                                                               }
+                                                       } else if (!attempt_transfer(p)) {
+                                                               /*
+                                                                * Transfer successful.  Don't actually hang up at this point.
+                                                                * Let our channel legs of the calls die off as the transfer
+                                                                * percolates through the core.
+                                                                */
+                                                               break;
                                                        }
                                                } else {
                                                        ast_channel_softhangup_internal_flag_add(p->subs[SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
index 1a8f0d9..38387d3 100644 (file)
@@ -103,6 +103,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/format.h"
 #include "asterisk/format_cap.h"
 #include "asterisk/features_config.h"
+#include "asterisk/bridging.h"
 
 #include "chan_misdn_config.h"
 #include "isdn_lib.h"
@@ -8592,10 +8593,9 @@ static void release_chan_early(struct chan_list *ch)
 static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list *held_ch)
 {
        int retval;
-       struct ast_channel *target;
-       struct ast_channel *transferee;
-       struct ast_party_connected_line target_colp;
-       struct ast_party_connected_line transferee_colp;
+       enum ast_transfer_result xfer_res;
+       struct ast_channel *to_target;
+       struct ast_channel *to_transferee;
 
        switch (active_ch->state) {
        case MISDN_PROCEEDING:
@@ -8608,59 +8608,24 @@ static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list
        }
 
        ast_channel_lock_both(held_ch->ast, active_ch->ast);
-
-       transferee = ast_bridged_channel(held_ch->ast);
-       if (!transferee) {
-               /*
-                * Could not transfer.  Held channel is not bridged anymore.
-                * Held party probably got tired of waiting and hung up.
-                */
-               ast_channel_unlock(held_ch->ast);
-               ast_channel_unlock(active_ch->ast);
-               return -1;
-       }
-
-       target = active_ch->ast;
+       to_target = active_ch->ast;
+       to_transferee = held_ch->ast;
        chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
-               ast_channel_name(held_ch->ast), ast_channel_name(target));
-
-       ast_party_connected_line_init(&target_colp);
-       ast_party_connected_line_copy(&target_colp, ast_channel_connected(target));
-
-       /* Reset any earlier private connected id representation */
-       ast_party_id_reset(&target_colp.priv);
-
-       ast_party_connected_line_init(&transferee_colp);
-       ast_party_connected_line_copy(&transferee_colp, ast_channel_connected(held_ch->ast));
-
-       /* Reset any earlier private connected id representation*/
-       ast_party_id_reset(&transferee_colp.priv);
-
+               ast_channel_name(to_transferee), ast_channel_name(to_target));
        held_ch->hold.state = MISDN_HOLD_TRANSFER;
+       ast_channel_ref(to_target);
+       ast_channel_ref(to_transferee);
+       ast_channel_unlock(to_target);
+       ast_channel_unlock(to_transferee);
 
-       /*
-        * Before starting a masquerade, all channel and pvt locks must
-        * be unlocked.  Any recursive channel locks held before
-        * ast_channel_transfer_masquerade() invalidates deadlock
-        * avoidance.  Since we are unlocking both the pvt and its owner
-        * channel it is possible for "target" and "transferee" to be
-        * destroyed by their pbx threads.  To prevent this we must give
-        * "target" and "transferee" a reference before any unlocking
-        * takes place.
-        */
-       ao2_ref(target, +1);
-       ao2_ref(transferee, +1);
-       ast_channel_unlock(held_ch->ast);
-       ast_channel_unlock(active_ch->ast);
-
-       /* Setup transfer masquerade. */
-       retval = ast_channel_transfer_masquerade(target, &target_colp, 0,
-               transferee, &transferee_colp, 1);
-
-       ast_party_connected_line_free(&target_colp);
-       ast_party_connected_line_free(&transferee_colp);
-       ao2_ref(target, -1);
-       ao2_ref(transferee, -1);
+       retval = 0;
+       xfer_res = ast_bridge_transfer_attended(to_transferee, to_target);
+       if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
+               retval = -1;
+       }
+
+       ast_channel_unref(to_target);
+       ast_channel_unref(to_transferee);
        return retval;
 }
 
index 956c4a6..9660d3f 100644 (file)
@@ -44,6 +44,7 @@
 #include "asterisk/cel.h"
 #include "asterisk/causes.h"
 #include "asterisk/features_config.h"
+#include "asterisk/bridging.h"
 
 #include "sig_analog.h"
 
@@ -688,87 +689,44 @@ static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index)
  * \brief Attempt to transfer 3-way call.
  *
  * \param p Analog private structure.
- * \param inthreeway TRUE if the 3-way call is conferenced.
  *
- * \note
- * On entry these locks are held: real-call, private, 3-way call.
+ * \note On entry these locks are held: real-call, private, 3-way call.
+ * \note On exit these locks are held: real-call, private.
  *
- * \retval 1 Transfer successful.  3-way call is unlocked and subchannel is unalloced.
- *         Swapped real and 3-way subchannel.
- * \retval 0 Transfer successful.  3-way call is unlocked and subchannel is unalloced.
- * \retval -1 on error.  Caller must unlock 3-way call.
+ * \retval 0 on success.
+ * \retval -1 on error.
  */
-static int analog_attempt_transfer(struct analog_pvt *p, int inthreeway)
+static int analog_attempt_transfer(struct analog_pvt *p)
 {
        struct ast_channel *owner_real;
        struct ast_channel *owner_3way;
-       struct ast_channel *bridge_real;
-       struct ast_channel *bridge_3way;
-
-       owner_real = p->subs[ANALOG_SUB_REAL].owner;
-       owner_3way = p->subs[ANALOG_SUB_THREEWAY].owner;
-       bridge_real = ast_bridged_channel(owner_real);
-       bridge_3way = ast_bridged_channel(owner_3way);
-
-       /*
-        * In order to transfer, we need at least one of the channels to
-        * actually be in a call bridge.  We can't conference two
-        * applications together.  Why would we want to?
-        */
-       if (bridge_3way) {
-               ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(owner_3way), ast_channel_name(owner_real));
-               ast_cel_report_event(owner_3way,
-                       (ast_channel_state(owner_real) == AST_STATE_RINGING
-                               || ast_channel_state(owner_3way) == AST_STATE_RINGING)
-                               ? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER,
-                       NULL, ast_channel_linkedid(owner_3way), NULL);
+       enum ast_transfer_result xfer_res;
+       int res = 0;
 
-               /*
-                * The three-way party we're about to transfer is on hold if he
-                * is not in a three way conference.
-                */
-               if (ast_channel_transfer_masquerade(owner_real, ast_channel_connected(owner_real), 0,
-                       bridge_3way, ast_channel_connected(owner_3way), !inthreeway)) {
-                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                               ast_channel_name(bridge_3way), ast_channel_name(owner_real));
-                       return -1;
-               }
+       owner_real = ast_channel_ref(p->subs[ANALOG_SUB_REAL].owner);
+       owner_3way = ast_channel_ref(p->subs[ANALOG_SUB_THREEWAY].owner);
 
-               /* Three-way is now the REAL */
-               analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
-               ast_channel_unlock(owner_3way);
-               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
-               /* Tell the caller not to hangup */
-               return 1;
-       } else if (bridge_real) {
-               /* Try transferring the other way. */
-               ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(owner_real), ast_channel_name(owner_3way));
-               ast_cel_report_event(owner_3way,
-                       (ast_channel_state(owner_real) == AST_STATE_RINGING
-                               || ast_channel_state(owner_3way) == AST_STATE_RINGING)
-                               ? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER,
-                       NULL, ast_channel_linkedid(owner_3way), NULL);
+       ast_verb(3, "TRANSFERRING %s to %s\n",
+               ast_channel_name(owner_3way), ast_channel_name(owner_real));
 
-               /*
-                * The three-way party we're about to transfer is on hold if he
-                * is not in a three way conference.
-                */
-               if (ast_channel_transfer_masquerade(owner_3way, ast_channel_connected(owner_3way),
-                       !inthreeway, bridge_real, ast_channel_connected(owner_real), 0)) {
-                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                               ast_channel_name(bridge_real), ast_channel_name(owner_3way));
-                       return -1;
-               }
+       ast_channel_unlock(owner_real);
+       ast_channel_unlock(owner_3way);
+       analog_unlock_private(p);
 
-               /* Orphan the channel after releasing the lock */
-               ast_channel_unlock(owner_3way);
-               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
-               return 0;
-       } else {
-               ast_debug(1, "Neither %s nor %s are in a bridge, nothing to transfer\n",
-                       ast_channel_name(owner_real), ast_channel_name(owner_3way));
-               return -1;
+       xfer_res = ast_bridge_transfer_attended(owner_3way, owner_real);
+       if (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) {
+               ast_softhangup(owner_3way, AST_SOFTHANGUP_DEV);
+               res = -1;
        }
+
+       /* Must leave with these locked. */
+       ast_channel_lock(owner_real);
+       analog_lock_private(p);
+
+       ast_channel_unref(owner_real);
+       ast_channel_unref(owner_3way);
+
+       return res;
 }
 
 static int analog_update_conf(struct analog_pvt *p)
@@ -2892,10 +2850,6 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
                                                ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
                                        } else if ((ast_channel_pbx(ast)) || (ast_channel_state(ast) == AST_STATE_UP)) {
                                                if (p->transfer) {
-                                                       int inthreeway;
-
-                                                       inthreeway = p->subs[ANALOG_SUB_THREEWAY].inthreeway;
-
                                                        /* In any case this isn't a threeway call anymore */
                                                        analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
                                                        analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 0);
@@ -2909,16 +2863,13 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
                                                                analog_set_new_owner(p, NULL);
                                                                /* Ring the phone */
                                                                analog_ring(p);
-                                                       } else {
-                                                               res = analog_attempt_transfer(p, inthreeway);
-                                                               if (res < 0) {
-                                                                       /* Transfer attempt failed. */
-                                                                       ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
-                                                                       ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
-                                                               } else if (res) {
-                                                                       /* Don't actually hang up at this point */
-                                                                       break;
-                                                               }
+                                                       } else if (!analog_attempt_transfer(p)) {
+                                                               /*
+                                                                * Transfer successful.  Don't actually hang up at this point.
+                                                                * Let our channel legs of the calls die off as the transfer
+                                                                * percolates through the core.
+                                                                */
+                                                               break;
                                                        }
                                                } else {
                                                        ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
index 461ea51..d5d6f49 100644 (file)
@@ -50,6 +50,7 @@
 #include "asterisk/transcap.h"
 #include "asterisk/features.h"
 #include "asterisk/aoc.h"
+#include "asterisk/bridging.h"
 
 #include "sig_pri.h"
 #ifndef PRI_EVENT_FACILITY
@@ -1587,6 +1588,10 @@ static int pri_fixup_principle(struct sig_pri_span *pri, int principle, q931_cal
                strcpy(new_chan->moh_suggested, old_chan->moh_suggested);
                new_chan->moh_state = old_chan->moh_state;
                old_chan->moh_state = SIG_PRI_MOH_STATE_IDLE;
+#if defined(HAVE_PRI_TRANSFER)
+               new_chan->xfer_data = old_chan->xfer_data;
+               old_chan->xfer_data = NULL;
+#endif /* defined(HAVE_PRI_TRANSFER) */
 
 #if defined(HAVE_PRI_AOC_EVENTS)
                new_chan->aoc_s_request_invoke_id = old_chan->aoc_s_request_invoke_id;
@@ -2396,6 +2401,8 @@ struct xfer_rsp_data {
        q931_call *call;
        /*! Invocation ID to use when sending a reply to the transfer request. */
        int invoke_id;
+       /*! TRUE if the transfer response has been made. */
+       int responded;
 };
 #endif /* defined(HAVE_PRI_TRANSFER) */
 
@@ -2405,14 +2412,19 @@ struct xfer_rsp_data {
  * \brief Send the transfer success/fail response message.
  * \since 1.8
  *
- * \param data Callback user data pointer
+ * \param rsp Transfer response data.
  * \param is_successful TRUE if the transfer was successful.
  *
+ * \note Assumes the rsp->pri->lock is already obtained.
+ *
  * \return Nothing
  */
-static void sig_pri_transfer_rsp(void *data, int is_successful)
+static void sig_pri_transfer_rsp(struct xfer_rsp_data *rsp, int is_successful)
 {
-       struct xfer_rsp_data *rsp = data;
+       if (rsp->responded) {
+               return;
+       }
+       rsp->responded = 1;
 
        pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful);
 }
@@ -2420,19 +2432,6 @@ static void sig_pri_transfer_rsp(void *data, int is_successful)
 
 #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
 /*!
- * \brief Protocol callback to indicate if transfer will happen.
- * \since 1.8
- *
- * \param data Callback user data pointer
- * \param is_successful TRUE if the transfer will happen.
- *
- * \return Nothing
- */
-typedef void (*xfer_rsp_callback)(void *data, int is_successful);
-#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
-
-#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
-/*!
  * \internal
  * \brief Attempt to transfer the two calls to each other.
  * \since 1.8
@@ -2442,15 +2441,14 @@ typedef void (*xfer_rsp_callback)(void *data, int is_successful);
  * \param call_1_held TRUE if call_1_pri is on hold.
  * \param call_2_pri Second call involved in the transfer. (target; usually active/ringing)
  * \param call_2_held TRUE if call_2_pri is on hold.
- * \param rsp_callback Protocol callback to indicate if transfer will happen. NULL if not used.
- * \param data Callback user data pointer
+ * \param xfer_data Transfer response data if non-NULL.
  *
  * \note Assumes the pri->lock is already obtained.
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, xfer_rsp_callback rsp_callback, void *data)
+static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, struct xfer_rsp_data *xfer_data)
 {
        struct attempt_xfer_call {
                q931_call *pri;
@@ -2459,10 +2457,9 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_
                int chanpos;
        };
        int retval;
-       struct ast_channel *transferee;
+       enum ast_transfer_result xfer_res;
        struct attempt_xfer_call *call_1;
        struct attempt_xfer_call *call_2;
-       struct attempt_xfer_call *swap_call;
        struct attempt_xfer_call c1;
        struct attempt_xfer_call c2;
 
@@ -2478,118 +2475,104 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_
        call_2->chanpos = pri_find_principle_by_call(pri, call_2->pri);
        if (call_1->chanpos < 0 || call_2->chanpos < 0) {
                /* Calls not found in span control. */
-               if (rsp_callback) {
+#if defined(HAVE_PRI_TRANSFER)
+               if (xfer_data) {
                        /* Transfer failed. */
-                       rsp_callback(data, 0);
+                       sig_pri_transfer_rsp(xfer_data, 0);
                }
+#endif /* defined(HAVE_PRI_TRANSFER) */
                return -1;
        }
 
-       /* Attempt to make transferee and target consistent. */
-       if (!call_1->held && call_2->held) {
-               /*
-                * Swap call_1 and call_2 to make call_1 the transferee(held call)
-                * and call_2 the target(active call).
-                */
-               swap_call = call_1;
-               call_1 = call_2;
-               call_2 = swap_call;
-       }
-
-       /* Deadlock avoidance is attempted. */
+       /* Get call_1 owner. */
        sig_pri_lock_private(pri->pvts[call_1->chanpos]);
        sig_pri_lock_owner(pri, call_1->chanpos);
+       call_1->ast = pri->pvts[call_1->chanpos]->owner;
+       if (call_1->ast) {
+               ast_channel_ref(call_1->ast);
+               ast_channel_unlock(call_1->ast);
+       }
+       sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+
+       /* Get call_2 owner. */
        sig_pri_lock_private(pri->pvts[call_2->chanpos]);
        sig_pri_lock_owner(pri, call_2->chanpos);
-
-       call_1->ast = pri->pvts[call_1->chanpos]->owner;
        call_2->ast = pri->pvts[call_2->chanpos]->owner;
+       if (call_2->ast) {
+               ast_channel_ref(call_2->ast);
+               ast_channel_unlock(call_2->ast);
+       }
+       sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+
        if (!call_1->ast || !call_2->ast) {
                /* At least one owner is not present. */
                if (call_1->ast) {
-                       ast_channel_unlock(call_1->ast);
+                       ast_channel_unref(call_1->ast);
                }
                if (call_2->ast) {
-                       ast_channel_unlock(call_2->ast);
+                       ast_channel_unref(call_2->ast);
                }
-               sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
-               sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
-               if (rsp_callback) {
+#if defined(HAVE_PRI_TRANSFER)
+               if (xfer_data) {
                        /* Transfer failed. */
-                       rsp_callback(data, 0);
+                       sig_pri_transfer_rsp(xfer_data, 0);
                }
+#endif /* defined(HAVE_PRI_TRANSFER) */
                return -1;
        }
 
-       for (;;) {
-               transferee = ast_bridged_channel(call_1->ast);
-               if (transferee) {
-                       break;
-               }
-
-               /* Try masquerading the other way. */
-               swap_call = call_1;
-               call_1 = call_2;
-               call_2 = swap_call;
+       ast_verb(3, "TRANSFERRING %s to %s\n",
+               ast_channel_name(call_1->ast), ast_channel_name(call_2->ast));
 
-               transferee = ast_bridged_channel(call_1->ast);
-               if (transferee) {
-                       break;
-               }
-
-               /* Could not transfer.  Neither call is bridged. */
-               ast_channel_unlock(call_1->ast);
-               ast_channel_unlock(call_2->ast);
+#if defined(HAVE_PRI_TRANSFER)
+       if (xfer_data) {
+               /*
+                * Add traps on the transferer channels in case threading causes
+                * them to hangup before ast_bridge_transfer_attended() returns
+                * and we can get the pri->lock back.
+                */
+               sig_pri_lock_private(pri->pvts[call_1->chanpos]);
+               pri->pvts[call_1->chanpos]->xfer_data = xfer_data;
                sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+               sig_pri_lock_private(pri->pvts[call_2->chanpos]);
+               pri->pvts[call_2->chanpos]->xfer_data = xfer_data;
                sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
-
-               if (rsp_callback) {
-                       /* Transfer failed. */
-                       rsp_callback(data, 0);
-               }
-               return -1;
        }
+#endif /* defined(HAVE_PRI_TRANSFER) */
 
-       ast_verb(3, "TRANSFERRING %s to %s\n", ast_channel_name(call_1->ast), ast_channel_name(call_2->ast));
-
-       /*
-        * Setup transfer masquerade.
-        *
-        * Note:  There is an extremely nasty deadlock avoidance issue
-        * with ast_channel_transfer_masquerade().  Deadlock may be possible if
-        * the channels involved are proxies (chan_agent channels) and
-        * it is called with locks.  Unfortunately, there is no simple
-        * or even merely difficult way to guarantee deadlock avoidance
-        * and still be able to send an ECT success response without the
-        * possibility of the bridged channel hanging up on us.
-        */
        ast_mutex_unlock(&pri->lock);
-       retval = ast_channel_transfer_masquerade(
-               call_2->ast,
-               ast_channel_connected(call_2->ast),
-               call_2->held,
-               transferee,
-               ast_channel_connected(call_1->ast),
-               call_1->held);
-
-       /* Reacquire the pri->lock to hold off completion of the transfer masquerade. */
+       xfer_res = ast_bridge_transfer_attended(call_1->ast, call_2->ast);
        ast_mutex_lock(&pri->lock);
+       retval = (xfer_res != AST_BRIDGE_TRANSFER_SUCCESS) ? -1 : 0;
 
-       ast_channel_unlock(call_1->ast);
-       ast_channel_unlock(call_2->ast);
-       sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
-       sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+#if defined(HAVE_PRI_TRANSFER)
+       if (xfer_data) {
+               int rsp_chanpos;
 
-       if (rsp_callback) {
                /*
-                * Report transfer status.
+                * Remove the transferrer channel traps.
                 *
-                * Must do the callback before the masquerade completes to ensure
-                * that the protocol message goes out before the call leg is
-                * disconnected.
+                * We must refind chanpos because we released pri->lock.
                 */
-               rsp_callback(data, retval ? 0 : 1);
+               rsp_chanpos = pri_find_principle_by_call(pri, call_1->pri);
+               if (0 <= rsp_chanpos) {
+                       sig_pri_lock_private(pri->pvts[rsp_chanpos]);
+                       pri->pvts[rsp_chanpos]->xfer_data = NULL;
+                       sig_pri_unlock_private(pri->pvts[rsp_chanpos]);
+               }
+               rsp_chanpos = pri_find_principle_by_call(pri, call_2->pri);
+               if (0 <= rsp_chanpos) {
+                       sig_pri_lock_private(pri->pvts[rsp_chanpos]);
+                       pri->pvts[rsp_chanpos]->xfer_data = NULL;
+                       sig_pri_unlock_private(pri->pvts[rsp_chanpos]);
+               }
+
+               /* Report transfer status. */
+               sig_pri_transfer_rsp(xfer_data, retval ? 0 : 1);
        }
+#endif /* defined(HAVE_PRI_TRANSFER) */
+       ast_channel_unref(call_1->ast);
+       ast_channel_unref(call_2->ast);
        return retval;
 }
 #endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
@@ -4457,10 +4440,11 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
                        xfer_rsp.pri = pri;
                        xfer_rsp.call = call_rsp;
                        xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id;
+                       xfer_rsp.responded = 0;
                        sig_pri_attempt_transfer(pri,
                                subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held,
                                subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held,
-                               sig_pri_transfer_rsp, &xfer_rsp);
+                               &xfer_rsp);
                        sig_pri_lock_private(pri->pvts[chanpos]);
                        break;
 #endif /* defined(HAVE_PRI_TRANSFER) */
@@ -7114,7 +7098,7 @@ static void *pri_dchannel(void *vpri)
                                        /* We are to transfer the call instead of simply hanging up. */
                                        sig_pri_unlock_private(pri->pvts[chanpos]);
                                        if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1,
-                                               e->hangup.call_active, 0, NULL, NULL)) {
+                                               e->hangup.call_active, 0, NULL)) {
                                                break;
                                        }
                                        sig_pri_lock_private(pri->pvts[chanpos]);
@@ -7595,6 +7579,20 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
                }
 #endif /* defined(SUPPORT_USERUSER) */
 
+#if defined(HAVE_PRI_TRANSFER)
+               if (p->xfer_data) {
+                       /*
+                        * The transferrer call leg is disconnecting.  It must mean that
+                        * the transfer was successful and the core is disconnecting the
+                        * call legs involved.
+                        *
+                        * The transfer protocol response message must go out before the
+                        * call leg is disconnected.
+                        */
+                       sig_pri_transfer_rsp(p->xfer_data, 1);
+               }
+#endif /* defined(HAVE_PRI_TRANSFER) */
+
 #if defined(HAVE_PRI_AOC_EVENTS)
                if (p->holding_aoce) {
                        pri_aoc_e_send(p->pri->pri, p->call, &p->aoc_e);
@@ -7623,6 +7621,9 @@ int sig_pri_hangup(struct sig_pri_chan *p, struct ast_channel *ast)
                        pri_hangup(p->pri->pri, p->call, icause);
                }
        }
+#if defined(HAVE_PRI_TRANSFER)
+       p->xfer_data = NULL;
+#endif /* defined(HAVE_PRI_TRANSFER) */
 #if defined(HAVE_PRI_AOC_EVENTS)
        p->aoc_s_request_invoke_id_valid = 0;
        p->holding_aoce = 0;
index 805ed9b..62e0dc9 100644 (file)
@@ -172,6 +172,7 @@ enum sig_pri_reset_state {
 };
 
 struct sig_pri_span;
+struct xfer_rsp_data;
 
 struct sig_pri_callback {
        /* Unlock the private in the signalling private structure.  This is used for three way calling madness. */
@@ -353,6 +354,10 @@ struct sig_pri_chan {
        enum sig_pri_call_level call_level;
        /*! \brief Channel reset/restart state. */
        enum sig_pri_reset_state resetting;
+#if defined(HAVE_PRI_TRANSFER)
+       /*! If non-NULL, send transfer disconnect successfull response to first call disconnecting. */
+       struct xfer_rsp_data *xfer_data;
+#endif /* defined(HAVE_PRI_TRANSFER) */
        int prioffset;                                  /*!< channel number in span */
        int logicalspan;                                /*!< logical span number within trunk group */
        int mastertrunkgroup;                   /*!< what trunk group is our master */
index 62856d4..4adff95 100644 (file)
@@ -6384,6 +6384,12 @@ static const struct ast_datastore_info xfer_ds_info = {
        .destroy = xfer_ds_destroy,
 };
 
+/*
+ * BUGBUG ast_channel_transfer_masquerade() can be deleted when bridging COLP has been reimplemented.
+ *
+ * ast_bridge_transfer_attended() will need to do something like
+ * this when it has to do a masquerade into an application.
+ */
 int ast_channel_transfer_masquerade(
        struct ast_channel *target_chan,
        const struct ast_party_connected_line *target_id,