[asterisk-commits] rmudgett: branch 1.8 r292704 - in /branches/1.8: channels/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Oct 22 10:47:13 CDT 2010


Author: rmudgett
Date: Fri Oct 22 10:47:08 2010
New Revision: 292704

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=292704
Log:
Connected line is not updated when chan_dahdi/sig_pri or chan_misdn transfers a call.

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

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

JIRA ABE-2589
JIRA SWP-2296

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

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

Modified:
    branches/1.8/channels/chan_misdn.c
    branches/1.8/channels/sig_pri.c
    branches/1.8/main/channel.c

Modified: branches/1.8/channels/chan_misdn.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.8/channels/chan_misdn.c?view=diff&rev=292704&r1=292703&r2=292704
==============================================================================
--- branches/1.8/channels/chan_misdn.c (original)
+++ branches/1.8/channels/chan_misdn.c Fri Oct 22 10:47:08 2010
@@ -8497,6 +8497,40 @@
 
 /*!
  * \internal
+ * \brief Copy the source connected line information to the destination for a transfer.
+ * \since 1.8
+ *
+ * \param dest Destination connected line
+ * \param src Source connected line
+ *
+ * \return Nothing
+ */
+static void misdn_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src)
+{
+	struct ast_party_connected_line connected;
+
+	connected = *src;
+	connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+
+	/* Make sure empty strings will be erased. */
+	if (!connected.id.name.str) {
+		connected.id.name.str = "";
+	}
+	if (!connected.id.number.str) {
+		connected.id.number.str = "";
+	}
+	if (!connected.id.subaddress.str) {
+		connected.id.subaddress.str = "";
+	}
+	if (!connected.id.tag) {
+		connected.id.tag = "";
+	}
+
+	ast_party_connected_line_copy(dest, &connected);
+}
+
+/*!
+ * \internal
  * \brief Attempt to transfer the active channel party to the held channel party.
  *
  * \param active_ch Channel currently connected.
@@ -8508,7 +8542,10 @@
 static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list *held_ch)
 {
 	int retval;
-	struct ast_channel *bridged;
+	struct ast_channel *target;
+	struct ast_channel *transferee;
+	struct ast_party_connected_line target_colp;
+	struct ast_party_connected_line transferee_colp;
 
 	switch (active_ch->state) {
 	case MISDN_PROCEEDING:
@@ -8520,23 +8557,116 @@
 		return -1;
 	}
 
-	bridged = ast_bridged_channel(held_ch->ast);
-	if (bridged) {
-		ast_queue_control(held_ch->ast, AST_CONTROL_UNHOLD);
-		held_ch->hold.state = MISDN_HOLD_TRANSFER;
-
-		chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
-			held_ch->ast->name, active_ch->ast->name);
-		retval = ast_channel_masquerade(active_ch->ast, bridged);
-	} else {
+	ast_channel_lock(held_ch->ast);
+	while (ast_channel_trylock(active_ch->ast)) {
+		CHANNEL_DEADLOCK_AVOIDANCE(held_ch->ast);
+	}
+
+	transferee = ast_bridged_channel(held_ch->ast);
+	if (!transferee) {
 		/*
 		 * Could not transfer.  Held channel is not bridged anymore.
 		 * Held party probably got tired of waiting and hung up.
 		 */
-		retval = -1;
-	}
-
-	return retval;
+		ast_channel_unlock(held_ch->ast);
+		ast_channel_unlock(active_ch->ast);
+		return -1;
+	}
+
+	target = active_ch->ast;
+	chan_misdn_log(1, held_ch->hold.port, "TRANSFERRING %s to %s\n",
+		held_ch->ast->name, target->name);
+
+	ast_party_connected_line_init(&target_colp);
+	misdn_connected_line_copy_transfer(&target_colp, &target->connected);
+	ast_party_connected_line_init(&transferee_colp);
+	misdn_connected_line_copy_transfer(&transferee_colp, &held_ch->ast->connected);
+	held_ch->hold.state = MISDN_HOLD_TRANSFER;
+
+	/*
+	 * Before starting a masquerade, all channel and pvt locks must
+	 * be unlocked.  Any recursive channel locks held before
+	 * ast_channel_masquerade() invalidates deadlock avoidance.  Any
+	 * recursive channel locks held before ast_do_masquerade()
+	 * invalidates channel container locking order.  Since we are
+	 * unlocking both the pvt and its owner channel it is possible
+	 * for "target" and "transferee" to be destroyed by their pbx
+	 * threads.  To prevent this we must give "target" and
+	 * "transferee" a reference before any unlocking takes place.
+	 */
+	ao2_ref(target, +1);
+	ao2_ref(transferee, +1);
+	ast_channel_unlock(held_ch->ast);
+	ast_channel_unlock(active_ch->ast);
+
+	/* Release hold on the transferee channel. */
+	ast_indicate(transferee, AST_CONTROL_UNHOLD);
+
+	/* Setup transfer masquerade. */
+	retval = ast_channel_masquerade(target, transferee);
+	if (retval) {
+		/* Masquerade setup failed. */
+		ast_party_connected_line_free(&target_colp);
+		ast_party_connected_line_free(&transferee_colp);
+		ao2_ref(target, -1);
+		ao2_ref(transferee, -1);
+		return -1;
+	}
+	ao2_ref(transferee, -1);
+
+	/*
+	 * Make sure masquerade is complete.
+	 *
+	 * After the masquerade, the "target" channel pointer actually
+	 * points to the new transferee channel and the bridged channel
+	 * is still the intended target of the transfer.
+	 *
+	 * By manually completing the masquerade, we can send connected
+	 * line updates where they need to go.
+	 */
+	ast_do_masquerade(target);
+
+	/* Transfer COLP between target and transferee channels. */
+	{
+		/*
+		 * Since "target" may not actually be bridged to another
+		 * channel, there is no way for us to queue a frame so that its
+		 * connected line status will be updated.  Instead, we use the
+		 * somewhat hackish approach of using a special control frame
+		 * type that instructs ast_read() to perform a specific action.
+		 * In this case, the frame we queue tells ast_read() to call the
+		 * connected line interception macro configured for "target".
+		 */
+		struct ast_control_read_action_payload *frame_payload;
+		int payload_size;
+		int frame_size;
+		unsigned char connected_line_data[1024];
+
+		payload_size = ast_connected_line_build_data(connected_line_data,
+			sizeof(connected_line_data), &target_colp, NULL);
+		if (payload_size != -1) {
+			frame_size = payload_size + sizeof(*frame_payload);
+			frame_payload = alloca(frame_size);
+			frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
+			frame_payload->payload_size = payload_size;
+			memcpy(frame_payload->payload, connected_line_data, payload_size);
+			ast_queue_control_data(target, AST_CONTROL_READ_ACTION, frame_payload,
+				frame_size);
+		}
+		/*
+		 * In addition to queueing the read action frame so that the
+		 * connected line info on "target" will be updated, we also
+		 * are going to queue a plain old connected line update on
+		 * "target" to update the target channel.
+		 */
+		ast_channel_queue_connected_line_update(target, &transferee_colp, NULL);
+	}
+
+	ast_party_connected_line_free(&target_colp);
+	ast_party_connected_line_free(&transferee_colp);
+
+	ao2_ref(target, -1);
+	return 0;
 }
 
 

Modified: branches/1.8/channels/sig_pri.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.8/channels/sig_pri.c?view=diff&rev=292704&r1=292703&r2=292704
==============================================================================
--- branches/1.8/channels/sig_pri.c (original)
+++ branches/1.8/channels/sig_pri.c Fri Oct 22 10:47:08 2010
@@ -1885,100 +1885,340 @@
 #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
 /*!
  * \internal
+ * \brief Copy the source connected line information to the destination for a transfer.
+ * \since 1.8
+ *
+ * \param dest Destination connected line
+ * \param src Source connected line
+ *
+ * \return Nothing
+ */
+static void sig_pri_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src)
+{
+	struct ast_party_connected_line connected;
+
+	connected = *src;
+	connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+
+	/* Make sure empty strings will be erased. */
+	if (!connected.id.name.str) {
+		connected.id.name.str = "";
+	}
+	if (!connected.id.number.str) {
+		connected.id.number.str = "";
+	}
+	if (!connected.id.subaddress.str) {
+		connected.id.subaddress.str = "";
+	}
+	if (!connected.id.tag) {
+		connected.id.tag = "";
+	}
+
+	ast_party_connected_line_copy(dest, &connected);
+}
+#endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+struct xfer_rsp_data {
+	struct sig_pri_span *pri;
+	/*! Call to send transfer success/fail response over. */
+	q931_call *call;
+	/*! Invocation ID to use when sending a reply to the transfer request. */
+	int invoke_id;
+};
+#endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+/*!
+ * \internal
+ * \brief Send the transfer success/fail response message.
+ * \since 1.8
+ *
+ * \param data Callback user data pointer
+ * \param is_successful TRUE if the transfer was successful.
+ *
+ * \return Nothing
+ */
+static void sig_pri_transfer_rsp(void *data, int is_successful)
+{
+	struct xfer_rsp_data *rsp = data;
+
+	pri_transfer_rsp(rsp->pri->pri, rsp->call, rsp->invoke_id, is_successful);
+}
+#endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+/*!
+ * \brief Protocol callback to indicate if transfer will happen.
+ * \since 1.8
+ *
+ * \param data Callback user data pointer
+ * \param is_successful TRUE if the transfer will happen.
+ *
+ * \return Nothing
+ */
+typedef void (*xfer_rsp_callback)(void *data, int is_successful);
+#endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
+
+#if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
+/*!
+ * \internal
  * \brief Attempt to transfer the two calls to each other.
  * \since 1.8
  *
  * \param pri sig_pri PRI control structure.
- * \param call_1 First call involved in the transfer.
- * \param call_1_held TRUE if call_1 is on hold.
- * \param call_2 Second call involved in the transfer.
- * \param call_2_held TRUE if call_2 is on hold.
+ * \param call_1_pri First call involved in the transfer. (transferee; usually on hold)
+ * \param call_1_held TRUE if call_1_pri is on hold.
+ * \param call_2_pri Second call involved in the transfer. (target; usually active/ringing)
+ * \param call_2_held TRUE if call_2_pri is on hold.
+ * \param rsp_callback Protocol callback to indicate if transfer will happen. NULL if not used.
+ * \param data Callback user data pointer
  *
  * \note Assumes the pri->lock is already obtained.
  *
  * \retval 0 on success.
  * \retval -1 on error.
  */
-static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1, int call_1_held, q931_call *call_2, int call_2_held)
-{
+static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1_pri, int call_1_held, q931_call *call_2_pri, int call_2_held, xfer_rsp_callback rsp_callback, void *data)
+{
+	struct attempt_xfer_call {
+		q931_call *pri;
+		struct ast_channel *ast;
+		int held;
+		int chanpos;
+	};
 	int retval;
-	int call_1_chanpos;
-	int call_2_chanpos;
-	struct ast_channel *call_1_ast;
-	struct ast_channel *call_2_ast;
-	struct ast_channel *bridged;
-
-	call_1_chanpos = pri_find_pri_call(pri, call_1);
-	call_2_chanpos = pri_find_pri_call(pri, call_2);
-	if (call_1_chanpos < 0 || call_2_chanpos < 0) {
+	int transferee_is_held;
+	struct ast_channel *transferee;
+	struct attempt_xfer_call *call_1;
+	struct attempt_xfer_call *call_2;
+	struct attempt_xfer_call *target;
+	struct attempt_xfer_call *swap_call;
+	struct attempt_xfer_call c1;
+	struct attempt_xfer_call c2;
+	struct ast_party_connected_line target_colp;
+	struct ast_party_connected_line transferee_colp;
+
+	c1.pri = call_1_pri;
+	c1.held = call_1_held;
+	call_1 = &c1;
+
+	c2.pri = call_2_pri;
+	c2.held = call_2_held;
+	call_2 = &c2;
+
+	call_1->chanpos = pri_find_pri_call(pri, call_1->pri);
+	call_2->chanpos = pri_find_pri_call(pri, call_2->pri);
+	if (call_1->chanpos < 0 || call_2->chanpos < 0) {
+		/* Calls not found in span control. */
+		if (rsp_callback) {
+			/* Transfer failed. */
+			rsp_callback(data, 0);
+		}
 		return -1;
 	}
 
+	/* Attempt to make transferee and target consistent. */
+	if (!call_1->held && call_2->held) {
+		/*
+		 * Swap call_1 and call_2 to make call_1 the transferee(held call)
+		 * and call_2 the target(active call).
+		 */
+		swap_call = call_1;
+		call_1 = call_2;
+		call_2 = swap_call;
+	}
+
 	/* Deadlock avoidance is attempted. */
-	sig_pri_lock_private(pri->pvts[call_1_chanpos]);
-	sig_pri_lock_owner(pri, call_1_chanpos);
-	sig_pri_lock_private(pri->pvts[call_2_chanpos]);
-	sig_pri_lock_owner(pri, call_2_chanpos);
-
-	call_1_ast = pri->pvts[call_1_chanpos]->owner;
-	call_2_ast = pri->pvts[call_2_chanpos]->owner;
-	if (!call_1_ast || !call_2_ast) {
-		if (call_1_ast) {
-			ast_channel_unlock(call_1_ast);
-		}
-		if (call_2_ast) {
-			ast_channel_unlock(call_2_ast);
-		}
-		sig_pri_unlock_private(pri->pvts[call_1_chanpos]);
-		sig_pri_unlock_private(pri->pvts[call_2_chanpos]);
+	sig_pri_lock_private(pri->pvts[call_1->chanpos]);
+	sig_pri_lock_owner(pri, call_1->chanpos);
+	sig_pri_lock_private(pri->pvts[call_2->chanpos]);
+	sig_pri_lock_owner(pri, call_2->chanpos);
+
+	call_1->ast = pri->pvts[call_1->chanpos]->owner;
+	call_2->ast = pri->pvts[call_2->chanpos]->owner;
+	if (!call_1->ast || !call_2->ast) {
+		/* At least one owner is not present. */
+		if (call_1->ast) {
+			ast_channel_unlock(call_1->ast);
+		}
+		if (call_2->ast) {
+			ast_channel_unlock(call_2->ast);
+		}
+		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;
 	}
 
-	bridged = ast_bridged_channel(call_2_ast);
-	if (bridged) {
-		if (call_1_held) {
-			ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
-		}
-		if (call_2_held) {
-			ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
-		}
-
-		ast_verb(3, "TRANSFERRING %s to %s\n", call_2_ast->name, call_1_ast->name);
-		retval = ast_channel_masquerade(call_1_ast, bridged);
-	} else {
+	for (;;) {
+		transferee = ast_bridged_channel(call_1->ast);
+		if (transferee) {
+			break;
+		}
+
 		/* Try masquerading the other way. */
-		bridged = ast_bridged_channel(call_1_ast);
-		if (bridged) {
-			if (call_1_held) {
-				ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
-			}
-			if (call_2_held) {
-				ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
-			}
-
-			ast_verb(3, "TRANSFERRING %s to %s\n", call_1_ast->name, call_2_ast->name);
-			retval = ast_channel_masquerade(call_2_ast, bridged);
-		} else {
-			/* Could not transfer. */
-			retval = -1;
-		}
-	}
-	if (bridged && retval) {
-		/* Restore HOLD on held calls because masquerade failed. */
-		if (call_1_held) {
-			ast_queue_control(call_1_ast, AST_CONTROL_HOLD);
-		}
-		if (call_2_held) {
-			ast_queue_control(call_2_ast, AST_CONTROL_HOLD);
-		}
-	}
-
-	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]);
-
-	return retval;
+		swap_call = call_1;
+		call_1 = call_2;
+		call_2 = swap_call;
+
+		transferee = ast_bridged_channel(call_1->ast);
+		if (transferee) {
+			break;
+		}
+
+		/* Could not transfer.  Neither call is bridged. */
+		ast_channel_unlock(call_1->ast);
+		ast_channel_unlock(call_2->ast);
+		sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+		sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+
+		if (rsp_callback) {
+			/* Transfer failed. */
+			rsp_callback(data, 0);
+		}
+		return -1;
+	}
+
+	target = call_2;
+	ast_verb(3, "TRANSFERRING %s to %s\n", call_1->ast->name, target->ast->name);
+
+	ast_party_connected_line_init(&target_colp);
+	sig_pri_connected_line_copy_transfer(&target_colp, &target->ast->connected);
+	ast_party_connected_line_init(&transferee_colp);
+	sig_pri_connected_line_copy_transfer(&transferee_colp, &call_1->ast->connected);
+	transferee_is_held = call_1->held;
+
+	/*
+	 * Setup transfer masquerade.
+	 *
+	 * Note:  There is an extremely nasty deadlock avoidance issue
+	 * with ast_channel_masquerade().  Deadlock may be possible if
+	 * the channels involved are proxies (chan_agent channels) and
+	 * it is called with locks.  Unfortunately, there is no simple
+	 * or even merely difficult way to guarantee deadlock avoidance
+	 * and still be able to send an ECT success response without the
+	 * possibility of the bridged channel hanging up on us.
+	 */
+	ast_mutex_unlock(&pri->lock);
+	retval = ast_channel_masquerade(target->ast, transferee);
+	if (retval) {
+		/* Masquerade setup failed. */
+		ast_party_connected_line_free(&target_colp);
+		ast_party_connected_line_free(&transferee_colp);
+
+		ast_mutex_lock(&pri->lock);
+
+		ast_channel_unlock(call_1->ast);
+		ast_channel_unlock(call_2->ast);
+		sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+		sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+
+		if (rsp_callback) {
+			/* Transfer failed. */
+			rsp_callback(data, 0);
+		}
+		return -1;
+	}
+
+	/*
+	 * Release any hold on the transferee channel before allowing
+	 * the masquerade to happen.
+	 */
+	if (transferee_is_held) {
+		ast_indicate(transferee, AST_CONTROL_UNHOLD);
+	}
+	ast_mutex_lock(&pri->lock);
+
+	/*
+	 * Before manually completing a masquerade, all channel and pvt
+	 * locks must be unlocked.  Any recursive channel locks held
+	 * before ast_do_masquerade() invalidates channel container
+	 * locking order.  Since we are unlocking both the pvt and its
+	 * owner channel it is possible for "target" to be destroyed
+	 * in the pbx thread.  To prevent this we must give "target"
+	 * a reference before any unlocking takes place.
+	 */
+	ao2_ref(target->ast, +1);
+	ast_channel_unlock(call_1->ast);
+	ast_channel_unlock(call_2->ast);
+	sig_pri_unlock_private(pri->pvts[call_1->chanpos]);
+	sig_pri_unlock_private(pri->pvts[call_2->chanpos]);
+
+	if (rsp_callback) {
+		/*
+		 * Transfer successful.
+		 *
+		 * Must do the callback before releasing the pri->lock to ensure
+		 * that the protocol message goes out before the call leg is
+		 * disconnected.
+		 */
+		rsp_callback(data, 1);
+	}
+
+	/*
+	 * Make sure masquerade is complete.
+	 *
+	 * After the masquerade, the "target" channel pointer actually
+	 * points to the new transferee channel and the bridged channel
+	 * is still the intended target of the transfer.
+	 *
+	 * By manually completing the masquerade, we can send the unhold
+	 * and connected line updates where they need to go.
+	 */
+	ast_mutex_unlock(&pri->lock);
+	ast_do_masquerade(target->ast);
+
+	/* Release any hold on the target. */
+	if (target->held) {
+		ast_queue_control(target->ast, AST_CONTROL_UNHOLD);
+	}
+
+	/* Transfer COLP between target and transferee channels. */
+	{
+		/*
+		 * Since "target" may not actually be bridged to another
+		 * channel, there is no way for us to queue a frame so that its
+		 * connected line status will be updated.  Instead, we use the
+		 * somewhat hackish approach of using a special control frame
+		 * type that instructs ast_read() to perform a specific action.
+		 * In this case, the frame we queue tells ast_read() to call the
+		 * connected line interception macro configured for "target".
+		 */
+		struct ast_control_read_action_payload *frame_payload;
+		int payload_size;
+		int frame_size;
+		unsigned char connected_line_data[1024];
+
+		payload_size = ast_connected_line_build_data(connected_line_data,
+			sizeof(connected_line_data), &target_colp, NULL);
+		if (payload_size != -1) {
+			frame_size = payload_size + sizeof(*frame_payload);
+			frame_payload = alloca(frame_size);
+			frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
+			frame_payload->payload_size = payload_size;
+			memcpy(frame_payload->payload, connected_line_data, payload_size);
+			ast_queue_control_data(target->ast, AST_CONTROL_READ_ACTION, frame_payload,
+				frame_size);
+		}
+		/*
+		 * In addition to queueing the read action frame so that the
+		 * connected line info on "target" will be updated, we also
+		 * are going to queue a plain old connected line update on
+		 * "target" to update the target channel.
+		 */
+		ast_channel_queue_connected_line_update(target->ast, &transferee_colp, NULL);
+	}
+
+	ast_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;
 }
 #endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
 
@@ -3604,6 +3844,9 @@
 	int index;
 	struct ast_channel *owner;
 	struct ast_party_redirecting ast_redirecting;
+#if defined(HAVE_PRI_TRANSFER)
+	struct xfer_rsp_data xfer_rsp;
+#endif	/* defined(HAVE_PRI_TRANSFER) */
 
 	if (!subcmds) {
 		return;
@@ -3822,11 +4065,13 @@
 			}
 
 			sig_pri_unlock_private(pri->pvts[chanpos]);
-			pri_transfer_rsp(pri->pri, call_rsp, subcmd->u.transfer.invoke_id,
-				sig_pri_attempt_transfer(pri,
-					subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held,
-					subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held)
-				? 0 : 1);
+			xfer_rsp.pri = pri;
+			xfer_rsp.call = call_rsp;
+			xfer_rsp.invoke_id = subcmd->u.transfer.invoke_id;
+			sig_pri_attempt_transfer(pri,
+				subcmd->u.transfer.call_1, subcmd->u.transfer.is_call_1_held,
+				subcmd->u.transfer.call_2, subcmd->u.transfer.is_call_2_held,
+				sig_pri_transfer_rsp, &xfer_rsp);
 			sig_pri_lock_private(pri->pvts[chanpos]);
 			break;
 #endif	/* defined(HAVE_PRI_TRANSFER) */
@@ -5355,8 +5600,8 @@
 							&& pri->hold_disconnect_transfer) {
 							/* We are to transfer the call instead of simply hanging up. */
 							sig_pri_unlock_private(pri->pvts[chanpos]);
-							if (!sig_pri_attempt_transfer(pri, e->hangup.call_active, 0,
-								e->hangup.call_held, 1)) {
+							if (!sig_pri_attempt_transfer(pri, e->hangup.call_held, 1,
+								e->hangup.call_active, 0, NULL, NULL)) {
 								break;
 							}
 							sig_pri_lock_private(pri->pvts[chanpos]);

Modified: branches/1.8/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.8/main/channel.c?view=diff&rev=292704&r1=292703&r2=292704
==============================================================================
--- branches/1.8/main/channel.c (original)
+++ branches/1.8/main/channel.c Fri Oct 22 10:47:08 2010
@@ -2789,7 +2789,6 @@
 	}
 
 	ast_indicate(chan, -1);
-	chan->visible_indication = 0;
 
 	return res;
 }
@@ -4154,8 +4153,12 @@
 	case AST_CONTROL_RINGING:
 	case AST_CONTROL_RING:
 	case AST_CONTROL_HOLD:
+		/* You can hear these */
+		return 1;
+
 	case AST_CONTROL_UNHOLD:
-		return 1;
+		/* This is a special case.  You stop hearing this. */
+		break;
 	}
 
 	return 0;
@@ -4176,7 +4179,6 @@
 
 	/* Don't bother if the channel is about to go away, anyway. */
 	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
-		ast_channel_unlock(chan);
 		res = -1;
 		goto indicate_cleanup;
 	}
@@ -4195,7 +4197,6 @@
 
 		/* who knows what we will get back! the anticipation is killing me. */
 		if (!(awesome_frame = ast_framehook_list_read_event(chan->framehooks, &frame))) {
-			ast_channel_unlock(chan);
 			res = 0;
 			goto indicate_cleanup;
 		}
@@ -4234,6 +4235,14 @@
 	
 	default:
 		break;
+	}
+
+	if (is_visible_indication(condition)) {
+		/* A new visible indication is requested. */
+		chan->visible_indication = condition;
+	} else if (condition == AST_CONTROL_UNHOLD || _condition < 0) {
+		/* Visible indication is cleared/stopped. */
+		chan->visible_indication = 0;
 	}
 
 	if (chan->tech->indicate) {
@@ -4243,13 +4252,8 @@
 		res = -1;
 	}
 
-	ast_channel_unlock(chan);
-
 	if (!res) {
 		/* The channel driver successfully handled this indication */
-		if (is_visible_indication(condition)) {
-			chan->visible_indication = condition;
-		}
 		res = 0;
 		goto indicate_cleanup;
 	}
@@ -4335,7 +4339,6 @@
 		ast_playtones_start(chan, 0, ts->data, 1);
 		ts = ast_tone_zone_sound_unref(ts);
 		res = 0;
-		chan->visible_indication = condition;
 	}
 
 	if (res) {
@@ -4344,6 +4347,7 @@
 	}
 
 indicate_cleanup:
+	ast_channel_unlock(chan);
 	if (awesome_frame) {
 		ast_frfree(awesome_frame);
 	}
@@ -5948,6 +5952,7 @@
 	int i;
 	int res=0;
 	int origstate;
+	int visible_indication;
 	struct ast_frame *current;
 	const struct ast_channel_tech *t;
 	void *t_pvt;
@@ -6019,10 +6024,21 @@
 	ast_debug(4, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
 		clonechan->name, clonechan->_state, original->name, original->_state);
 
+	/*
+	 * Stop any visible indiction on the original channel so we can
+	 * transfer it to the clonechan taking the original's place.
+	 */
+	visible_indication = original->visible_indication;
+	ast_indicate(original, -1);
+
 	chans[0] = clonechan;
 	chans[1] = original;
-	ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans, "Clone: %s\r\nCloneState: %s\r\nOriginal: %s\r\nOriginalState: %s\r\n",
-		      clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
+	ast_manager_event_multichan(EVENT_FLAG_CALL, "Masquerade", 2, chans,
+		"Clone: %s\r\n"
+		"CloneState: %s\r\n"
+		"Original: %s\r\n"
+		"OriginalState: %s\r\n",
+		clonechan->name, ast_state2str(clonechan->_state), original->name, ast_state2str(original->_state));
 
 	/* Having remembered the original read/write formats, we turn off any translation on either
 	   one */
@@ -6246,8 +6262,8 @@
 	 * of this channel, and the new channel private data needs to be made
 	 * aware of the current visible indication (RINGING, CONGESTION, etc.)
 	 */
-	if (original->visible_indication) {
-		ast_indicate(original, original->visible_indication);
+	if (visible_indication) {
+		ast_indicate(original, visible_indication);
 	}
 
 	/* Now, at this point, the "clone" channel is totally F'd up.  We mark it as




More information about the asterisk-commits mailing list