dect
/
asterisk
Archived
13
0
Fork 0

Merged revisions 294349 via svnmerge from

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: http://svn.digium.com/svn/asterisk/trunk@294351 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
rmudgett 2010-11-09 17:00:07 +00:00
parent 5631e8109b
commit 89f74fccfe
5 changed files with 340 additions and 326 deletions

View File

@ -8502,40 +8502,6 @@ static void release_chan_early(struct chan_list *ch)
ast_mutex_unlock(&release_lock);
}
/*!
* \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.
@ -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;
}

View 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)
static int analog_attempt_transfer(struct analog_pvt *p, int inthreeway)
{
/* 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))) {
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_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))) {
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. */

View 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);
retval = ast_channel_transfer_masquerade(
call_2->ast,
&call_2->ast->connected,
call_2->held,
transferee,
&call_1->ast->connected,
call_1->held);
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);
}
/* 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);
rsp_callback(data, retval ? 0 : 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_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) */

View File

@ -1874,6 +1874,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.
*

View 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);