diff options
author | rmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-11-09 16:55:32 +0000 |
---|---|---|
committer | rmudgett <rmudgett@f38db490-d61c-443f-a65b-d21fe96a405b> | 2010-11-09 16:55:32 +0000 |
commit | 89fdc4f316990d60c22d21f8fd5885a49d710c57 (patch) | |
tree | 85627bda8a8f48ade72ef64c8f71c74618258edc | |
parent | 3f17f7ce08f9b3783e09ec2e23779f55803ffc4a (diff) |
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: http://svn.digium.com/svn/asterisk/branches/1.8@294349 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r-- | channels/chan_misdn.c | 118 | ||||
-rw-r--r-- | channels/sig_analog.c | 134 | ||||
-rw-r--r-- | channels/sig_pri.c | 165 | ||||
-rw-r--r-- | include/asterisk/channel.h | 46 | ||||
-rw-r--r-- | main/channel.c | 207 |
5 files changed, 342 insertions, 328 deletions
diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c index a47d1aa7d..fe8033383 100644 --- a/channels/chan_misdn.c +++ b/channels/chan_misdn.c @@ -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; } diff --git a/channels/sig_analog.c b/channels/sig_analog.c index 30a1de47d..ac1723ebc 100644 --- a/channels/sig_analog.c +++ b/channels/sig_analog.c @@ -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. */ diff --git a/channels/sig_pri.c b/channels/sig_pri.c index fa73ac209..176ab5093 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -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) */ diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index d12d478d3..172f69b03 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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 diff --git a/main/channel.c b/main/channel.c index 7c1e998af..f4b34a26a 100644 --- a/main/channel.c +++ b/main/channel.c @@ -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); |