[asterisk-commits] rmudgett: trunk r294351 - in /trunk: ./ channels/ include/asterisk/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Nov 9 11:00:22 CST 2010


Author: rmudgett
Date: Tue Nov  9 11:00:07 2010
New Revision: 294351

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=294351
Log:
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
........

Modified:
    trunk/   (props changed)
    trunk/channels/chan_misdn.c
    trunk/channels/sig_analog.c
    trunk/channels/sig_pri.c
    trunk/include/asterisk/channel.h
    trunk/main/channel.c

Propchange: trunk/
------------------------------------------------------------------------------
Binary property 'branch-1.8-merged' - no diff available.

Modified: trunk/channels/chan_misdn.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_misdn.c?view=diff&rev=294351&r1=294350&r2=294351
==============================================================================
--- trunk/channels/chan_misdn.c (original)
+++ trunk/channels/chan_misdn.c Tue Nov  9 11:00:07 2010
@@ -8504,40 +8504,6 @@
 
 /*!
  * \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 @@
 		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;
 }
 
 

Modified: trunk/channels/sig_analog.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sig_analog.c?view=diff&rev=294351&r1=294350&r2=294351
==============================================================================
--- trunk/channels/sig_analog.c (original)
+++ trunk/channels/sig_analog.c Tue Nov  9 11:00:07 2010
@@ -652,6 +652,7 @@
  * \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 @@
  * \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))) {
+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_REAL].owner)->name, p->subs[ANALOG_SUB_THREEWAY].owner->name);
+				bridge_3way->name, owner_real->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);
-			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 @@
 						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 @@
 								/* 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. */

Modified: trunk/channels/sig_pri.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sig_pri.c?view=diff&rev=294351&r1=294350&r2=294351
==============================================================================
--- trunk/channels/sig_pri.c (original)
+++ trunk/channels/sig_pri.c Tue Nov  9 11:00:07 2010
@@ -1882,42 +1882,6 @@
 }
 #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 @@
 		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 @@
 		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 @@
 	 * 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 @@
 
 	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);
-	}
-
-	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;
+		rsp_callback(data, retval ? 0 : 1);
+	}
+	return retval;
 }
 #endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
 

Modified: trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=294351&r1=294350&r2=294351
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Tue Nov  9 11:00:07 2010
@@ -1875,6 +1875,52 @@
 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

Modified: trunk/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/channel.c?view=diff&rev=294351&r1=294350&r2=294351
==============================================================================
--- trunk/main/channel.c (original)
+++ trunk/main/channel.c Tue Nov  9 11:00:07 2010
@@ -5589,7 +5589,7 @@
 	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 @@
 	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 @@
 	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 @@
 }
 
 /*!
-  \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.
-*/
+ * \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];
+
+	/* 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 @@
 	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];
@@ -6013,6 +6178,23 @@
 	 * until we unlock the container. */
 	while (ast_channel_trylock(clonechan)) {
 		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 */
@@ -6301,10 +6483,21 @@
 		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);




More information about the asterisk-commits mailing list