Merged revisions 294349 via svnmerge from
authorRichard Mudgett <rmudgett@digium.com>
Tue, 9 Nov 2010 17:00:07 +0000 (17:00 +0000)
committerRichard Mudgett <rmudgett@digium.com>
Tue, 9 Nov 2010 17:00:07 +0000 (17:00 +0000)
https://origsvn.digium.com/svn/asterisk/branches/1.8

........
  r294349 | rmudgett | 2010-11-09 10:55:32 -0600 (Tue, 09 Nov 2010) | 17 lines

  Analog lines do not transfer CONNECTED LINE or execute the interception macros.

  Add connected line update for sig_analog transfers and simplify the
  corresponding sig_pri and chan_misdn transfer code.

  Note that if you create a three-way call in sig_analog before transferring
  the call, the distinction of the caller/callee interception macros make
  little sense.  The interception macro writer needs to be prepared for
  either caller/callee macro to be executed.  The current implementation
  swaps which caller/callee interception macro is executed after a three-way
  call is created.

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

  JIRA ABE-2589
  JIRA SWP-2372
........

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

channels/chan_misdn.c
channels/sig_analog.c
channels/sig_pri.c
include/asterisk/channel.h
main/channel.c

index a47d1aa..fe80333 100644 (file)
@@ -8504,40 +8504,6 @@ 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.
@@ -8585,95 +8551,35 @@ static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list
                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_copy(&target_colp, &target->connected);
        ast_party_connected_line_init(&transferee_colp);
-       misdn_connected_line_copy_transfer(&transferee_colp, &held_ch->ast->connected);
+       ast_party_connected_line_copy(&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.
+        * 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);
 
-       /* 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);
-       }
+       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);
-       return 0;
+       ao2_ref(transferee, -1);
+       return retval;
 }
 
 
index 30a1de4..ac1723e 100644 (file)
@@ -652,6 +652,7 @@ 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.
@@ -661,74 +662,77 @@ static int analog_is_dialing(struct analog_pvt *p, enum analog_sub index)
  * \retval 0 Transfer successful.  3-way call is unlocked and subchannel is unalloced.
  * \retval -1 on error.  Caller must unlock 3-way call.
  */
-static int analog_attempt_transfer(struct analog_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[ANALOG_SUB_REAL].owner)) {
-               /* The three-way person we're about to transfer to could still be in MOH, so
-                  stop it now if appropriate */
-               if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
-                       ast_queue_control(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CONTROL_UNHOLD);
-               }
-               if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RINGING) {
-                       /*
-                        * This may not be safe.
-                        * We currently hold the locks on the real-call, private, and 3-way call.
-                        * We could possibly avoid this here by using an ast_queue_control() instead.
-                        * However, the following ast_channel_masquerade() is going to be locking
-                        * the bridged channel again anyway.
-                        */
-                       ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner), AST_CONTROL_RINGING);
-               }
-               if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RING) {
-                       analog_play_tone(p, ANALOG_SUB_THREEWAY, ANALOG_TONE_RINGTONE);
-               }
-               if (!p->subs[ANALOG_SUB_THREEWAY].inthreeway) {
-                       ast_cel_report_event(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CEL_ATTENDEDTRANSFER, NULL, p->subs[ANALOG_SUB_THREEWAY].owner->linkedid, NULL);
-               }
-               if (ast_channel_masquerade(p->subs[ANALOG_SUB_THREEWAY].owner, ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner))) {
-                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                                       ast_bridged_channel(p->subs[ANALOG_SUB_REAL].owner)->name, p->subs[ANALOG_SUB_THREEWAY].owner->name);
-                       return -1;
-               }
-               /* Orphan the channel after releasing the lock */
-               ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
-               analog_unalloc_sub(p, ANALOG_SUB_THREEWAY);
-       } else if (ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)) {
-               ast_queue_control(p->subs[ANALOG_SUB_REAL].owner, AST_CONTROL_UNHOLD);
-               if (p->subs[ANALOG_SUB_THREEWAY].owner->_state == AST_STATE_RINGING) {
-                       /*
-                        * This may not be safe.
-                        * We currently hold the locks on the real-call, private, and 3-way call.
-                        * We could possibly avoid this here by using an ast_queue_control() instead.
-                        * However, the following ast_channel_masquerade() is going to be locking
-                        * the bridged channel again anyway.
-                        */
-                       ast_indicate(ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner), AST_CONTROL_RINGING);
-               }
-               if (p->subs[ANALOG_SUB_REAL].owner->_state == AST_STATE_RING) {
-                       analog_play_tone(p, ANALOG_SUB_REAL, ANALOG_TONE_RINGTONE);
-               }
-               ast_cel_report_event(p->subs[ANALOG_SUB_THREEWAY].owner, AST_CEL_BLINDTRANSFER, NULL, p->subs[ANALOG_SUB_THREEWAY].owner->linkedid, NULL);
-               if (ast_channel_masquerade(p->subs[ANALOG_SUB_REAL].owner, ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner))) {
+static int analog_attempt_transfer(struct analog_pvt *p, int inthreeway)
+{
+       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", owner_3way->name, owner_real->name);
+               ast_cel_report_event(owner_3way,
+                       (owner_real->_state == AST_STATE_RINGING
+                               || owner_3way->_state == AST_STATE_RINGING)
+                               ? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER,
+                       NULL, owner_3way->linkedid, NULL);
+
+               /*
+                * 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, &owner_real->connected, 0,
+                       bridge_3way, &owner_3way->connected, !inthreeway)) {
                        ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
-                                       ast_bridged_channel(p->subs[ANALOG_SUB_THREEWAY].owner)->name, p->subs[ANALOG_SUB_REAL].owner->name);
+                               bridge_3way->name, owner_real->name);
                        return -1;
                }
+
                /* Three-way is now the REAL */
                analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
-               ast_channel_unlock(p->subs[ANALOG_SUB_REAL].owner); /* unlock REAL because THREEWAY has become 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", owner_real->name, owner_3way->name);
+               ast_cel_report_event(owner_3way,
+                       (owner_real->_state == AST_STATE_RINGING
+                               || owner_3way->_state == AST_STATE_RINGING)
+                               ? AST_CEL_BLINDTRANSFER : AST_CEL_ATTENDEDTRANSFER,
+                       NULL, owner_3way->linkedid, NULL);
+
+               /*
+                * 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, &owner_3way->connected,
+                       !inthreeway, bridge_real, &owner_real->connected, 0)) {
+                       ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+                               bridge_real->name, owner_3way->name);
+                       return -1;
+               }
+
+               /* 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",
-                                       p->subs[ANALOG_SUB_REAL].owner->name, p->subs[ANALOG_SUB_THREEWAY].owner->name);
+                       owner_real->name, owner_3way->name);
                return -1;
        }
-       return 0;
 }
 
 static int analog_update_conf(struct analog_pvt *p)
@@ -2741,6 +2745,14 @@ 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->pbx) || (ast->_state == 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);
+
                                                        /* Only attempt transfer if the phone is ringing; why transfer to busy tone eh? */
                                                        if (!p->transfertobusy && ast->_state == AST_STATE_BUSY) {
                                                                /* Swap subs and dis-own channel */
@@ -2751,29 +2763,21 @@ static struct ast_frame *__analog_handle_event(struct analog_pvt *p, struct ast_
                                                                /* Ring the phone */
                                                                analog_ring(p);
                                                        } else {
-                                                               res = analog_attempt_transfer(p);
+                                                               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) {
-                                                                       /* this isn't a threeway call anymore */
-                                                                       analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
-                                                                       analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 0);
-
                                                                        /* Don't actually hang up at this point */
                                                                        break;
                                                                }
                                                        }
-                                                       /* this isn't a threeway call anymore */
-                                                       analog_set_inthreeway(p, ANALOG_SUB_REAL, 0);
-                                                       analog_set_inthreeway(p, ANALOG_SUB_THREEWAY, 0);
                                                } else {
                                                        ast_softhangup_nolock(p->subs[ANALOG_SUB_THREEWAY].owner, AST_SOFTHANGUP_DEV);
                                                        ast_channel_unlock(p->subs[ANALOG_SUB_THREEWAY].owner);
                                                }
                                        } else {
-                                               ast_cel_report_event(ast, AST_CEL_BLINDTRANSFER, NULL, ast->linkedid, NULL);
                                                /* Swap subs and dis-own channel */
                                                analog_swap_subs(p, ANALOG_SUB_THREEWAY, ANALOG_SUB_REAL);
                                                /* Unlock the 3-way call that we swapped to real-call. */
index fa73ac2..176ab50 100644 (file)
@@ -1882,42 +1882,6 @@ static void sig_pri_mcid_event(struct sig_pri_span *pri, const struct pri_subcmd
 }
 #endif /* defined(HAVE_PRI_MCID) */
 
-#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_TRANSFER)
 struct xfer_rsp_data {
        struct sig_pri_span *pri;
@@ -1988,16 +1952,12 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_
                int chanpos;
        };
        int retval;
-       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;
@@ -2083,20 +2043,13 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_
                return -1;
        }
 
-       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;
+       ast_verb(3, "TRANSFERRING %s to %s\n", call_1->ast->name, call_2->ast->name);
 
        /*
         * Setup transfer masquerade.
         *
         * Note:  There is an extremely nasty deadlock avoidance issue
-        * with ast_channel_masquerade().  Deadlock may be possible if
+        * 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
@@ -2104,45 +2057,17 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_
         * 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);
-               }
-               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);
-       }
+       retval = ast_channel_transfer_masquerade(
+               call_2->ast,
+               &call_2->ast->connected,
+               call_2->held,
+               transferee,
+               &call_1->ast->connected,
+               call_1->held);
+
+       /* Reacquire the pri->lock to hold off completion of the transfer masquerade. */
        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]);
@@ -2150,75 +2075,15 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_
 
        if (rsp_callback) {
                /*
-                * Transfer successful.
+                * Report transfer status.
                 *
-                * Must do the callback before releasing the pri->lock to ensure
+                * Must do the callback before the masquerade completes 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);
+               rsp_callback(data, retval ? 0 : 1);
        }
-
-       ast_party_connected_line_free(&target_colp);
-       ast_party_connected_line_free(&transferee_colp);
-
-       ao2_ref(target->ast, -1);
-       ast_mutex_lock(&pri->lock);
-       return 0;
+       return retval;
 }
 #endif /* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
 
index d12d478..172f69b 100644 (file)
@@ -1875,6 +1875,52 @@ int ast_channel_bridge(struct ast_channel *c0,struct ast_channel *c1,
 int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone);
 
 /*!
+ * \brief Setup a masquerade to transfer a call.
+ * \since 1.8
+ *
+ * \param target_chan Target of the call transfer.  (Masquerade original channel)
+ * \param target_id New connected line information for the target channel.
+ * \param target_held TRUE if the target call is on hold.
+ * \param transferee_chan Transferee of the call transfer. (Masquerade clone channel)
+ * \param transferee_id New connected line information for the transferee channel.
+ * \param transferee_held TRUE if the transferee call is on hold.
+ *
+ * \details
+ * Party A - Transferee
+ * Party B - Transferer
+ * Party C - Target of transfer
+ *
+ * Party B transfers A to C.
+ *
+ * Party A is connected to bridged channel B1.
+ * Party B is connected to channels C1 and C2.
+ * Party C is connected to bridged channel B2.
+ *
+ * Party B -- C1 == B1 -- Party A
+ *               __/
+ *              /
+ * Party B -- C2 == B2 -- Party C
+ *
+ * Bridged channel B1 is masqueraded into channel C2.  Where B1
+ * is the masquerade clone channel and C2 is the masquerade
+ * original channel.
+ *
+ * \see ast_channel_masquerade()
+ *
+ * \note Has the same locking requirements as ast_channel_masquerade().
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_channel_transfer_masquerade(
+       struct ast_channel *target_chan,
+       const struct ast_party_connected_line *target_id,
+       int target_held,
+       struct ast_channel *transferee_chan,
+       const struct ast_party_connected_line *transferee_id,
+       int transferee_held);
+
+/*!
  * \brief Gives the string form of a given cause code.
  *
  * \param state cause to get the description of
index 7c1e998..f4b34a2 100644 (file)
@@ -5589,7 +5589,7 @@ int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *pe
        return rc;
 }
 
-int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clonechan)
+static int __ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clonechan, struct ast_datastore *xfer_ds)
 {
        int res = -1;
        struct ast_channel *final_orig, *final_clone, *base;
@@ -5651,6 +5651,9 @@ retrymasq:
        if (!original->masqr && !original->masq && !clonechan->masq && !clonechan->masqr) {
                original->masq = clonechan;
                clonechan->masqr = original;
+               if (xfer_ds) {
+                       ast_channel_datastore_add(original, xfer_ds);
+               }
                ast_queue_frame(original, &ast_null_frame);
                ast_queue_frame(clonechan, &ast_null_frame);
                ast_debug(1, "Done planning to masquerade channel %s into the structure of %s\n", clonechan->name, original->name);
@@ -5676,6 +5679,115 @@ retrymasq:
        return res;
 }
 
+int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
+{
+       return __ast_channel_masquerade(original, clone, NULL);
+}
+
+/*!
+ * \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 party_connected_line_copy_transfer(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src)
+{
+       struct ast_party_connected_line connected;
+
+       connected = *((struct ast_party_connected_line *) 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);
+}
+
+/*! Transfer masquerade connected line exchange data. */
+struct xfer_masquerade_ds {
+       /*! New ID for the target of the transfer (Masquerade original channel) */
+       struct ast_party_connected_line target_id;
+       /*! New ID for the transferee of the transfer (Masquerade clone channel) */
+       struct ast_party_connected_line transferee_id;
+       /*! TRUE if the target call is held. (Masquerade original channel) */
+       int target_held;
+       /*! TRUE if the transferee call is held. (Masquerade clone channel) */
+       int transferee_held;
+};
+
+/*!
+ * \internal
+ * \brief Destroy the transfer connected line exchange datastore information.
+ * \since 1.8
+ *
+ * \param data The datastore payload to destroy.
+ *
+ * \return Nothing
+ */
+static void xfer_ds_destroy(void *data)
+{
+       struct xfer_masquerade_ds *ds = data;
+
+       ast_party_connected_line_free(&ds->target_id);
+       ast_party_connected_line_free(&ds->transferee_id);
+       ast_free(ds);
+}
+
+static const struct ast_datastore_info xfer_ds_info = {
+       .type = "xfer_colp",
+       .destroy = xfer_ds_destroy,
+};
+
+int ast_channel_transfer_masquerade(
+       struct ast_channel *target_chan,
+       const struct ast_party_connected_line *target_id,
+       int target_held,
+       struct ast_channel *transferee_chan,
+       const struct ast_party_connected_line *transferee_id,
+       int transferee_held)
+{
+       struct ast_datastore *xfer_ds;
+       struct xfer_masquerade_ds *xfer_colp;
+       int res;
+
+       xfer_ds = ast_datastore_alloc(&xfer_ds_info, NULL);
+       if (!xfer_ds) {
+               return -1;
+       }
+
+       xfer_colp = ast_calloc(1, sizeof(*xfer_colp));
+       if (!xfer_colp) {
+               ast_datastore_free(xfer_ds);
+               return -1;
+       }
+       party_connected_line_copy_transfer(&xfer_colp->target_id, target_id);
+       xfer_colp->target_held = target_held;
+       party_connected_line_copy_transfer(&xfer_colp->transferee_id, transferee_id);
+       xfer_colp->transferee_held = transferee_held;
+       xfer_ds->data = xfer_colp;
+
+       res = __ast_channel_masquerade(target_chan, transferee_chan, xfer_ds);
+       if (res) {
+               ast_datastore_free(xfer_ds);
+       }
+       return res;
+}
+
 /*! \brief this function simply changes the name of the channel and issues a manager_event
  *         with out unlinking and linking the channel from the ao2_container.  This should
  *         only be used when the channel has already been unlinked from the ao2_container.
@@ -5942,12 +6054,63 @@ static void report_new_callerid(struct ast_channel *chan)
 }
 
 /*!
-  \brief Masquerade a channel
+ * \internal
+ * \brief Transfer COLP between target and transferee channels.
+ * \since 1.8
+ *
+ * \param transferee Transferee channel to exchange connected line information.
+ * \param colp Connected line information to exchange.
+ *
+ * \return Nothing
+ */
+static void masquerade_colp_transfer(struct ast_channel *transferee, struct xfer_masquerade_ds *colp)
+{
+       struct ast_control_read_action_payload *frame_payload;
+       int payload_size;
+       int frame_size;
+       unsigned char connected_line_data[1024];
 
-  \note Assumes _NO_ channels and _NO_ channel pvt's are locked.  If a channel is locked while calling
-        this function, it invalidates our channel container locking order.  All channels
-               must be unlocked before it is permissible to lock the channels' ao2 container.
-*/
+       /* Release any hold on the target. */
+       if (colp->target_held) {
+               ast_queue_control(transferee, AST_CONTROL_UNHOLD);
+       }
+
+       /*
+        * Since transferee 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 transferee.
+        */
+       payload_size = ast_connected_line_build_data(connected_line_data,
+               sizeof(connected_line_data), &colp->target_id, 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(transferee, AST_CONTROL_READ_ACTION, frame_payload,
+                       frame_size);
+       }
+       /*
+        * In addition to queueing the read action frame so that the
+        * connected line info on transferee will be updated, we also are
+        * going to queue a plain old connected line update on transferee to
+        * update the target.
+        */
+       ast_channel_queue_connected_line_update(transferee, &colp->transferee_id, NULL);
+}
+
+/*!
+ * \brief Masquerade a channel
+ *
+ * \note Assumes _NO_ channels and _NO_ channel pvt's are locked.  If a channel is locked while calling
+ *       this function, it invalidates our channel container locking order.  All channels
+ *       must be unlocked before it is permissible to lock the channels' ao2 container.
+ */
 int ast_do_masquerade(struct ast_channel *original)
 {
        format_t x;
@@ -5967,6 +6130,8 @@ int ast_do_masquerade(struct ast_channel *original)
        struct ast_channel *clonechan, *chans[2];
        struct ast_channel *bridged;
        struct ast_cdr *cdr;
+       struct ast_datastore *xfer_ds;
+       struct xfer_masquerade_ds *xfer_colp;
        format_t rformat = original->readformat;
        format_t wformat = original->writeformat;
        char newn[AST_CHANNEL_NAME];
@@ -6015,6 +6180,23 @@ int ast_do_masquerade(struct ast_channel *original)
                CHANNEL_DEADLOCK_AVOIDANCE(original);
        }
 
+       /* Get any transfer masquerade connected line exchange data. */
+       xfer_ds = ast_channel_datastore_find(original, &xfer_ds_info, NULL);
+       if (xfer_ds) {
+               ast_channel_datastore_remove(original, xfer_ds);
+               xfer_colp = xfer_ds->data;
+       } else {
+               xfer_colp = NULL;
+       }
+
+       /*
+        * Release any hold on the transferee channel before proceeding
+        * with the masquerade.
+        */
+       if (xfer_colp && xfer_colp->transferee_held) {
+               ast_indicate(clonechan, AST_CONTROL_UNHOLD);
+       }
+
        /* clear the masquerade channels */
        original->masq = NULL;
        clonechan->masqr = NULL;
@@ -6301,10 +6483,21 @@ int ast_do_masquerade(struct ast_channel *original)
                ast_indicate(bridged, AST_CONTROL_SRCCHANGE);
                ast_channel_unlock(bridged);
        }
-
        ast_indicate(original, AST_CONTROL_SRCCHANGE);
 
+       if (xfer_colp) {
+               /*
+                * After the masquerade, the original channel pointer actually
+                * points to the new transferee channel and the bridged channel
+                * is still the intended transfer target party.
+                */
+               masquerade_colp_transfer(original, xfer_colp);
+       }
+
 done:
+       if (xfer_ds) {
+               ast_datastore_free(xfer_ds);
+       }
        /* it is possible for the clone channel to disappear during this */
        if (clonechan) {
                ast_channel_unlock(original);