Merged revisions 292704 via svnmerge from
authorRichard Mudgett <rmudgett@digium.com>
Fri, 22 Oct 2010 15:47:56 +0000 (15:47 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Fri, 22 Oct 2010 15:47:56 +0000 (15:47 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.8

........
  r292704 | rmudgett | 2010-10-22 10:47:08 -0500 (Fri, 22 Oct 2010) | 19 lines

  Connected line is not updated when chan_dahdi/sig_pri or chan_misdn transfers a call.

  When a call is transfered by ECT or implicitly by disconnect in sig_pri or
  implicitly by disconnect in chan_misdn, the connected line information is
  not exchanged.  The connected line interception macros also need to be
  executed if defined.

  The CALLER interception macro is executed for the held call.
  The CALLEE interception macro is executed for the active/ringing call.

  JIRA ABE-2589
  JIRA SWP-2296

  Patches:
        abe_2589_c3bier.patch uploaded by rmudgett (license 664)
        abe_2589_v1.8_v2.patch uploaded by rmudgett (license 664)

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

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

channels/chan_misdn.c
channels/sig_pri.c
main/channel.c

index a6d623e..35ac69d 100644 (file)
@@ -8497,6 +8497,40 @@ static void release_chan_early(struct chan_list *ch)
 
 /*!
  * \internal
+ * \brief Copy the source connected line information to the destination for a transfer.
+ * \since 1.8
+ *
+ * \param dest Destination connected line
+ * \param src Source connected line
+ *
+ * \return Nothing
+ */
+static void misdn_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src)
+{
+       struct ast_party_connected_line connected;
+
+       connected = *src;
+       connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+
+       /* Make sure empty strings will be erased. */
+       if (!connected.id.name.str) {
+               connected.id.name.str = "";
+       }
+       if (!connected.id.number.str) {
+               connected.id.number.str = "";
+       }
+       if (!connected.id.subaddress.str) {
+               connected.id.subaddress.str = "";
+       }
+       if (!connected.id.tag) {
+               connected.id.tag = "";
+       }
+
+       ast_party_connected_line_copy(dest, &connected);
+}
+
+/*!
+ * \internal
  * \brief Attempt to transfer the active channel party to the held channel party.
  *
  * \param active_ch Channel currently connected.
@@ -8508,7 +8542,10 @@ 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 *bridged;
+       struct ast_channel *target;
+       struct ast_channel *transferee;
+       struct ast_party_connected_line target_colp;
+       struct ast_party_connected_line transferee_colp;
 
        switch (active_ch->state) {
        case MISDN_PROCEEDING:
@@ -8520,23 +8557,116 @@ static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list
                return -1;
        }
 
-       bridged = ast_bridged_channel(held_ch->ast);
-       if (bridged) {
-               ast_queue_control(held_ch->ast, AST_CONTROL_UNHOLD);
-               held_ch->hold.state = MISDN_HOLD_TRANSFER;
+       ast_channel_lock(held_ch->ast);
+       while (ast_channel_trylock(active_ch->ast)) {
+               CHANNEL_DEADLOCK_AVOIDANCE(held_ch->ast);
+       }
 
-               chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
-                       held_ch->ast->name, active_ch->ast->name);
-               retval = ast_channel_masquerade(active_ch->ast, bridged);
-       } else {
+       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.
                 */
-               retval = -1;
+               ast_channel_unlock(held_ch->ast);
+               ast_channel_unlock(active_ch->ast);
+               return -1;
        }
 
-       return retval;
+       target = active_ch->ast;
+       chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
+               held_ch->ast->name, target->name);
+
+       ast_party_connected_line_init(&target_colp);
+       misdn_connected_line_copy_transfer(&target_colp, &target->connected);
+       ast_party_connected_line_init(&transferee_colp);
+       misdn_connected_line_copy_transfer(&transferee_colp, &held_ch->ast->connected);
+       held_ch->hold.state = MISDN_HOLD_TRANSFER;
+
+       /*
+        * Before starting a masquerade, all channel and pvt locks must
+        * be unlocked.  Any recursive channel locks held before
+        * ast_channel_masquerade() invalidates deadlock avoidance.  Any
+        * recursive channel locks held before ast_do_masquerade()
+        * invalidates channel container locking order.  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);
+
+       /* Release hold on the transferee channel. */
+       ast_indicate(transferee, AST_CONTROL_UNHOLD);
+
+       /* Setup transfer masquerade. */
+       retval = ast_channel_masquerade(target, transferee);
+       if (retval) {
+               /* Masquerade setup failed. */
+               ast_party_connected_line_free(&target_colp);
+               ast_party_connected_line_free(&transferee_colp);
+               ao2_ref(target, -1);
+               ao2_ref(transferee, -1);
+               return -1;
+       }
+       ao2_ref(transferee, -1);
+
+       /*
+        * Make sure masquerade is complete.
+        *
+        * After the masquerade, the "target" channel pointer actually
+        * points to the new transferee channel and the bridged channel
+        * is still the intended target of the transfer.
+        *
+        * By manually completing the masquerade, we can send connected
+        * line updates where they need to go.
+        */
+       ast_do_masquerade(target);
+
+       /* Transfer COLP between target and transferee channels. */
+       {
+               /*
+                * Since "target" may not actually be bridged to another
+                * channel, there is no way for us to queue a frame so that its
+                * connected line status will be updated.  Instead, we use the
+                * somewhat hackish approach of using a special control frame
+                * type that instructs ast_read() to perform a specific action.
+                * In this case, the frame we queue tells ast_read() to call the
+                * connected line interception macro configured for "target".
+                */
+               struct ast_control_read_action_payload *frame_payload;
+               int payload_size;
+               int frame_size;
+               unsigned char connected_line_data[1024];
+
+               payload_size = ast_connected_line_build_data(connected_line_data,
+                       sizeof(connected_line_data), &target_colp, NULL);
+               if (payload_size != -1) {
+                       frame_size = payload_size + sizeof(*frame_payload);
+                       frame_payload = alloca(frame_size);
+                       frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
+                       frame_payload->payload_size = payload_size;
+                       memcpy(frame_payload->payload, connected_line_data, payload_size);
+                       ast_queue_control_data(target, AST_CONTROL_READ_ACTION, frame_payload,
+                               frame_size);
+               }
+               /*
+                * In addition to queueing the read action frame so that the
+                * connected line info on "target" will be updated, we also
+                * are going to queue a plain old connected line update on
+                * "target" to update the target channel.
+                */
+               ast_channel_queue_connected_line_update(target, &transferee_colp, NULL);
+       }
+
+       ast_party_connected_line_free(&target_colp);
+       ast_party_connected_line_free(&transferee_colp);
+
+       ao2_ref(target, -1);
+       return 0;
 }
 
 
index bab7ae4..d0cfb46 100644 (file)
@@ -1885,100 +1885,340 @@ static void sig_pri_mcid_event(struct sig_pri_span *pri, const struct pri_subcmd
 #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
 /*!
  * \internal
+ * \brief Copy the source connected line information to the destination for a transfer.
+ * \since 1.8
+ *
+ * \param dest Destination connected line
+ * \param src Source connected line
+ *
+ * \return Nothing
+ */
+static void sig_pri_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src)
+{
+       struct ast_party_connected_line connected;
+
+       connected = *src;
+       connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+
+       /* Make sure empty strings will be erased. */
+       if (!connected.id.name.str) {
+               connected.id.name.str = "";
+       }
+       if (!connected.id.number.str) {
+               connected.id.number.str = "";
+       }
+       if (!connected.id.subaddress.str) {
+               connected.id.subaddress.str = "";
+       }
+       if (!connected.id.tag) {
+               connected.id.tag = "";
+       }
+
+       ast_party_connected_line_copy(dest, &connected);
+}
+#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+struct xfer_rsp_data {
+       struct sig_pri_span *pri;
+       /*! Call to send transfer success/fail response over. */
+       q931_call *call;
+       /*! Invocation ID to use when sending a reply to the transfer request. */
+       int invoke_id;
+};
+#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+/*!
+ * \internal
+ * \brief Send the transfer success/fail response message.
+ * \since 1.8
+ *
+ * \param data Callback user data pointer
+ * \param is_successful TRUE if the transfer was successful.
+ *
+ * \return Nothing
+ */
+static void sig_pri_transfer_rsp(void *data, int is_successful)
+{
+       struct xfer_rsp_data *rsp = data;
+
+       pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful);
+}
+#endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#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
  *
  * \param pri sig_pri PRI control structure.
- * \param call_1 First call involved in the transfer.
- * \param call_1_held TRUE if call_1 is on hold.
- * \param call_2 Second call involved in the transfer.
- * \param call_2_held TRUE if call_2 is on hold.
+ * \param call_1_pri First call involved in the transfer. (transferee; usually on hold)
+ * \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
  *
  * \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, int call_1_held, q931_call *call_2, int call_2_held)
+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)
 {
+       struct attempt_xfer_call {
+               q931_call *pri;
+               struct ast_channel *ast;
+               int held;
+               int chanpos;
+       };
        int retval;
-       int call_1_chanpos;
-       int call_2_chanpos;
-       struct ast_channel *call_1_ast;
-       struct ast_channel *call_2_ast;
-       struct ast_channel *bridged;
-
-       call_1_chanpos = pri_find_pri_call(pri, call_1);
-       call_2_chanpos = pri_find_pri_call(pri, call_2);
-       if (call_1_chanpos < 0 || call_2_chanpos < 0) {
+       int transferee_is_held;
+       struct ast_channel *transferee;
+       struct attempt_xfer_call *call_1;
+       struct attempt_xfer_call *call_2;
+       struct attempt_xfer_call *target;
+       struct attempt_xfer_call *swap_call;
+       struct attempt_xfer_call c1;
+       struct attempt_xfer_call c2;
+       struct ast_party_connected_line target_colp;
+       struct ast_party_connected_line transferee_colp;
+
+       c1.pri = call_1_pri;
+       c1.held = call_1_held;
+       call_1 = &c1;
+
+       c2.pri = call_2_pri;
+       c2.held = call_2_held;
+       call_2 = &c2;
+
+       call_1->chanpos = pri_find_pri_call(pri, call_1->pri);
+       call_2->chanpos = pri_find_pri_call(pri, call_2->pri);
+       if (call_1->chanpos < 0 || call_2->chanpos < 0) {
+               /* Calls not found in span control. */
+               if (rsp_callback) {
+                       /* Transfer failed. */
+                       rsp_callback(data, 0);
+               }
                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. */
-       sig_pri_lock_private(pri->pvts[call_1_chanpos]);
-       sig_pri_lock_owner(pri, call_1_chanpos);
-       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_1_ast || !call_2_ast) {
-               if (call_1_ast) {
-                       ast_channel_unlock(call_1_ast);
+       sig_pri_lock_private(pri->pvts[call_1->chanpos]);
+       sig_pri_lock_owner(pri, call_1->chanpos);
+       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_1->ast || !call_2->ast) {
+               /* At least one owner is not present. */
+               if (call_1->ast) {
+                       ast_channel_unlock(call_1->ast);
+               }
+               if (call_2->ast) {
+                       ast_channel_unlock(call_2->ast);
                }
-               if (call_2_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 (rsp_callback) {
+                       /* Transfer failed. */
+                       rsp_callback(data, 0);
                }
-               sig_pri_unlock_private(pri->pvts[call_1_chanpos]);
-               sig_pri_unlock_private(pri->pvts[call_2_chanpos]);
                return -1;
        }
 
-       bridged = ast_bridged_channel(call_2_ast);
-       if (bridged) {
-               if (call_1_held) {
-                       ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
-               }
-               if (call_2_held) {
-                       ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
+       for (;;) {
+               transferee = ast_bridged_channel(call_1->ast);
+               if (transferee) {
+                       break;
                }
 
-               ast_verb(3, "TRANSFERRING %s to %s\n", call_2_ast->name, call_1_ast->name);
-               retval = ast_channel_masquerade(call_1_ast, bridged);
-       } else {
                /* Try masquerading the other way. */
-               bridged = ast_bridged_channel(call_1_ast);
-               if (bridged) {
-                       if (call_1_held) {
-                               ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
-                       }
-                       if (call_2_held) {
-                               ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
-                       }
+               swap_call = call_1;
+               call_1 = call_2;
+               call_2 = swap_call;
 
-                       ast_verb(3, "TRANSFERRING %s to %s\n", call_1_ast->name, call_2_ast->name);
-                       retval = ast_channel_masquerade(call_2_ast, bridged);
-               } else {
-                       /* Could not transfer. */
-                       retval = -1;
+               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);
+               sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+               sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+
+               if (rsp_callback) {
+                       /* Transfer failed. */
+                       rsp_callback(data, 0);
+               }
+               return -1;
        }
-       if (bridged && retval) {
-               /* Restore HOLD on held calls because masquerade failed. */
-               if (call_1_held) {
-                       ast_queue_control(call_1_ast, AST_CONTROL_HOLD);
+
+       target = call_2;
+       ast_verb(3, "TRANSFERRING %s to %s\n", call_1->ast->name, target->ast->name);
+
+       ast_party_connected_line_init(&target_colp);
+       sig_pri_connected_line_copy_transfer(&target_colp, &target->ast->connected);
+       ast_party_connected_line_init(&transferee_colp);
+       sig_pri_connected_line_copy_transfer(&transferee_colp, &call_1->ast->connected);
+       transferee_is_held = call_1->held;
+
+       /*
+        * Setup transfer masquerade.
+        *
+        * Note:  There is an extremely nasty deadlock avoidance issue
+        * with ast_channel_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_masquerade(target->ast, transferee);
+       if (retval) {
+               /* Masquerade setup failed. */
+               ast_party_connected_line_free(&target_colp);
+               ast_party_connected_line_free(&transferee_colp);
+
+               ast_mutex_lock(&pri->lock);
+
+               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 (rsp_callback) {
+                       /* Transfer failed. */
+                       rsp_callback(data, 0);
                }
-               if (call_2_held) {
-                       ast_queue_control(call_2_ast, AST_CONTROL_HOLD);
+               return -1;
+       }
+
+       /*
+        * Release any hold on the transferee channel before allowing
+        * the masquerade to happen.
+        */
+       if (transferee_is_held) {
+               ast_indicate(transferee, AST_CONTROL_UNHOLD);
+       }
+       ast_mutex_lock(&pri->lock);
+
+       /*
+        * Before manually completing a masquerade, all channel and pvt
+        * locks must be unlocked.  Any recursive channel locks held
+        * before ast_do_masquerade() invalidates channel container
+        * locking order.  Since we are unlocking both the pvt and its
+        * owner channel it is possible for "target" to be destroyed
+        * in the pbx thread.  To prevent this we must give "target"
+        * a reference before any unlocking takes place.
+        */
+       ao2_ref(target->ast, +1);
+       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 (rsp_callback) {
+               /*
+                * Transfer successful.
+                *
+                * Must do the callback before releasing the pri->lock to ensure
+                * that the protocol message goes out before the call leg is
+                * disconnected.
+                */
+               rsp_callback(data, 1);
+       }
+
+       /*
+        * Make sure masquerade is complete.
+        *
+        * After the masquerade, the "target" channel pointer actually
+        * points to the new transferee channel and the bridged channel
+        * is still the intended target of the transfer.
+        *
+        * By manually completing the masquerade, we can send the unhold
+        * and connected line updates where they need to go.
+        */
+       ast_mutex_unlock(&pri->lock);
+       ast_do_masquerade(target->ast);
+
+       /* Release any hold on the target. */
+       if (target->held) {
+               ast_queue_control(target->ast, AST_CONTROL_UNHOLD);
+       }
+
+       /* Transfer COLP between target and transferee channels. */
+       {
+               /*
+                * Since "target" may not actually be bridged to another
+                * channel, there is no way for us to queue a frame so that its
+                * connected line status will be updated.  Instead, we use the
+                * somewhat hackish approach of using a special control frame
+                * type that instructs ast_read() to perform a specific action.
+                * In this case, the frame we queue tells ast_read() to call the
+                * connected line interception macro configured for "target".
+                */
+               struct ast_control_read_action_payload *frame_payload;
+               int payload_size;
+               int frame_size;
+               unsigned char connected_line_data[1024];
+
+               payload_size = ast_connected_line_build_data(connected_line_data,
+                       sizeof(connected_line_data), &target_colp, NULL);
+               if (payload_size != -1) {
+                       frame_size = payload_size + sizeof(*frame_payload);
+                       frame_payload = alloca(frame_size);
+                       frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
+                       frame_payload->payload_size = payload_size;
+                       memcpy(frame_payload->payload, connected_line_data, payload_size);
+                       ast_queue_control_data(target->ast, AST_CONTROL_READ_ACTION, frame_payload,
+                               frame_size);
                }
+               /*
+                * In addition to queueing the read action frame so that the
+                * connected line info on "target" will be updated, we also
+                * are going to queue a plain old connected line update on
+                * "target" to update the target channel.
+                */
+               ast_channel_queue_connected_line_update(target->ast, &transferee_colp, NULL);
        }
 
-       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]);
+       ast_party_connected_line_free(&target_colp);
+       ast_party_connected_line_free(&transferee_colp);
 
-       return retval;
+       ao2_ref(target->ast, -1);
+       ast_mutex_lock(&pri->lock);
+       return 0;
 }
 #endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
 
@@ -3604,6 +3844,9 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
        int index;
        struct ast_channel *owner;
        struct ast_party_redirecting ast_redirecting;
+#if defined(HAVE_PRI_TRANSFER)
+       struct xfer_rsp_data xfer_rsp;
+#endif /* defined(HAVE_PRI_TRANSFER) */
 
        if (!subcmds) {
                return;
@@ -3822,11 +4065,13 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev
                        }
 
                        sig_pri_unlock_private(pri->pvts[chanpos]);
-                       pri_transfer_rsp(pri->pri, call_rsp, subcmd->u.transfer.invoke_id,
-                               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)
-                               ? 0 : 1);
+                       xfer_rsp.pri = pri;
+                       xfer_rsp.call = call_rsp;
+                       xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id;
+                       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);
                        sig_pri_lock_private(pri->pvts[chanpos]);
                        break;
 #endif /* defined(HAVE_PRI_TRANSFER) */
@@ -5355,8 +5600,8 @@ static void *pri_dchannel(void *vpri)
                                                        && pri->hold_disconnect_transfer) {
                                                        /* 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_active, 0,
-                                                               e->hangup.call_held, 1)) {
+                                                       if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1,
+                                                               e->hangup.call_active, 0, NULL, NULL)) {
                                                                break;
                                                        }
                                                        sig_pri_lock_private(pri->pvts[chanpos]);
index 135b006..daf5b7a 100644 (file)
@@ -2789,7 +2789,6 @@ int ast_raw_answer(struct ast_channel *chan, int cdr_answer)
        }
 
        ast_indicate(chan, -1);
-       chan->visible_indication = 0;
 
        return res;
 }
@@ -4154,8 +4153,12 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
        case AST_CONTROL_RINGING:
        case AST_CONTROL_RING:
        case AST_CONTROL_HOLD:
-       case AST_CONTROL_UNHOLD:
+               /* You can hear these */
                return 1;
+
+       case AST_CONTROL_UNHOLD:
+               /* This is a special case.  You stop hearing this. */
+               break;
        }
 
        return 0;
@@ -4176,7 +4179,6 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
        /* Don't bother if the channel is about to go away, anyway. */
        if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
-               ast_channel_unlock(chan);
                res = -1;
                goto indicate_cleanup;
        }
@@ -4195,7 +4197,6 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
                /* who knows what we will get back! the anticipation is killing me. */
                if (!(awesome_frame = ast_framehook_list_read_event(chan->framehooks, &frame))) {
-                       ast_channel_unlock(chan);
                        res = 0;
                        goto indicate_cleanup;
                }
@@ -4236,6 +4237,14 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
                break;
        }
 
+       if (is_visible_indication(condition)) {
+               /* A new visible indication is requested. */
+               chan->visible_indication = condition;
+       } else if (condition == AST_CONTROL_UNHOLD || _condition < 0) {
+               /* Visible indication is cleared/stopped. */
+               chan->visible_indication = 0;
+       }
+
        if (chan->tech->indicate) {
                /* See if the channel driver can handle this condition. */
                res = chan->tech->indicate(chan, condition, data, datalen);
@@ -4243,13 +4252,8 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
                res = -1;
        }
 
-       ast_channel_unlock(chan);
-
        if (!res) {
                /* The channel driver successfully handled this indication */
-               if (is_visible_indication(condition)) {
-                       chan->visible_indication = condition;
-               }
                res = 0;
                goto indicate_cleanup;
        }
@@ -4335,7 +4339,6 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
                ast_playtones_start(chan, 0, ts->data, 1);
                ts = ast_tone_zone_sound_unref(ts);
                res = 0;
-               chan->visible_indication = condition;
        }
 
        if (res) {
@@ -4344,6 +4347,7 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
        }
 
 indicate_cleanup:
+       ast_channel_unlock(chan);
        if (awesome_frame) {
                ast_frfree(awesome_frame);
        }
@@ -5948,6 +5952,7 @@ int ast_do_masquerade(struct ast_channel *original)
        int i;
        int res=0;
        int origstate;
+       int visible_indication;
        struct ast_frame *current;
        const struct ast_channel_tech *t;
        void *t_pvt;
@@ -6019,10 +6024,21 @@ int ast_do_masquerade(struct ast_channel *original)
        ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
                clonechan->name, clonechan->_state, original->name, original->_state);
 
+       /*
+        * Stop any visible indiction on the original channel so we can
+        * transfer it to the clonechan taking the original's place.
+        */
+       visible_indication = original->visible_indication;
+       ast_indicate(original, -1);
+
        chans[0] = clonechan;
        chans[1] = original;
-       ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans, "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n",
-                     clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
+       ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans,
+               "Clone: %s\r\n"
+               "CloneState: %s\r\n"
+               "Original: %s\r\n"
+               "OriginalState: %s\r\n",
+               clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
 
        /* Having remembered the original read/write formats, we turn off any translation on either
           one */
@@ -6246,8 +6262,8 @@ int ast_do_masquerade(struct ast_channel *original)
         * of this channel, and the new channel private data needs to be made
         * aware of the current visible indication (RINGING, CONGESTION, etc.)
         */
-       if (original->visible_indication) {
-               ast_indicate(original, original->visible_indication);
+       if (visible_indication) {
+               ast_indicate(original, visible_indication);
        }
 
        /* Now, at this point, the "clone" channel is totally F'd up.  We mark it as