[asterisk-commits] rmudgett: branch 1.8 r289054 - in /branches/1.8/channels: chan_sip.c sig_pri.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Sep 27 19:32:25 CDT 2010


Author: rmudgett
Date: Mon Sep 27 19:32:18 2010
New Revision: 289054

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=289054
Log:
Break up long ast_manager_event_multichan() event lines.

Modified:
    branches/1.8/channels/chan_sip.c
    branches/1.8/channels/sig_pri.c

Modified: branches/1.8/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/branches/1.8/channels/chan_sip.c?view=diff&rev=289054&r1=289053&r2=289054
==============================================================================
--- branches/1.8/channels/chan_sip.c (original)
+++ branches/1.8/channels/chan_sip.c Mon Sep 27 19:32:18 2010
@@ -21722,7 +21722,14 @@
 	/* Perform the transfer */
 	chans[0] = transferer->owner;
 	chans[1] = target.chan1;
-	ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Attended\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\n",
+	ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
+		"TransferMethod: SIP\r\n"
+		"TransferType: Attended\r\n"
+		"Channel: %s\r\n"
+		"Uniqueid: %s\r\n"
+		"SIP-Callid: %s\r\n"
+		"TargetChannel: %s\r\n"
+		"TargetUniqueid: %s\r\n",
 		transferer->owner->name,
 		transferer->owner->uniqueid,
 		transferer->callid,
@@ -22060,7 +22067,16 @@
 		ast_clear_flag(&p->flags[0], SIP_GOTREFER);
 		p->refer->status = REFER_200OK;
 		append_history(p, "Xfer", "REFER to call parking.");
-		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransfer2Parking: Yes\r\n",
+		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
+			"TransferMethod: SIP\r\n"
+			"TransferType: Blind\r\n"
+			"Channel: %s\r\n"
+			"Uniqueid: %s\r\n"
+			"SIP-Callid: %s\r\n"
+			"TargetChannel: %s\r\n"
+			"TargetUniqueid: %s\r\n"
+			"TransferExten: %s\r\n"
+			"Transfer2Parking: Yes\r\n",
 			current.chan1->name,
 			current.chan1->uniqueid,
 			p->callid,
@@ -22144,7 +22160,16 @@
 	res = ast_async_goto(current.chan2, p->refer->refer_to_context, p->refer->refer_to, 1);
 
 	if (!res) {
-		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans, "TransferMethod: SIP\r\nTransferType: Blind\r\nChannel: %s\r\nUniqueid: %s\r\nSIP-Callid: %s\r\nTargetChannel: %s\r\nTargetUniqueid: %s\r\nTransferExten: %s\r\nTransferContext: %s\r\n",
+		ast_manager_event_multichan(EVENT_FLAG_CALL, "Transfer", 2, chans,
+			"TransferMethod: SIP\r\n"
+			"TransferType: Blind\r\n"
+			"Channel: %s\r\n"
+			"Uniqueid: %s\r\n"
+			"SIP-Callid: %s\r\n"
+			"TargetChannel: %s\r\n"
+			"TargetUniqueid: %s\r\n"
+			"TransferExten: %s\r\n"
+			"TransferContext: %s\r\n",
 			current.chan1->name,
 			current.chan1->uniqueid,
 			p->callid,

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=289054&r1=289053&r2=289054
==============================================================================
--- branches/1.8/channels/sig_pri.c (original)
+++ branches/1.8/channels/sig_pri.c Mon Sep 27 19:32:18 2010
@@ -1885,6 +1885,84 @@
 #if defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER)
 /*!
  * \internal
+ * \brief Copy the source connected line information to the destination connected line 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 was successful.
+ *
+ * \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
  *
@@ -1893,24 +1971,36 @@
  * \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 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, int call_1_held, q931_call *call_2, int call_2_held, xfer_rsp_callback callback, void *data)
 {
 	int retval;
 	int call_1_chanpos;
 	int call_2_chanpos;
+	int transferer_is_held;
+	int bridged_is_held;
 	struct ast_channel *call_1_ast;
 	struct ast_channel *call_2_ast;
+	struct ast_channel *transferer;
 	struct ast_channel *bridged;
+	struct ast_party_connected_line transferer_colp;
+	struct ast_party_connected_line bridged_colp;
 
 	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) {
+		/* Calls not found in span control. */
+		if (callback) {
+			/* Transfer failed. */
+			callback(data, 0);
+		}
 		return -1;
 	}
 
@@ -1923,6 +2013,7 @@
 	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);
 		}
@@ -1931,54 +2022,158 @@
 		}
 		sig_pri_unlock_private(pri->pvts[call_1_chanpos]);
 		sig_pri_unlock_private(pri->pvts[call_2_chanpos]);
+		if (callback) {
+			/* Transfer failed. */
+			callback(data, 0);
+		}
 		return -1;
 	}
 
+	ast_party_connected_line_init(&transferer_colp);
+	ast_party_connected_line_init(&bridged_colp);
+	transferer_is_held = 0;
+	bridged_is_held = 0;
+	transferer = NULL;
+
+	/* Setup transfer masquerade. */
 	bridged = ast_bridged_channel(call_2_ast);
 	if (bridged) {
+		transferer = call_1_ast;
+		sig_pri_connected_line_copy_transfer(&transferer_colp, &call_1_ast->connected);
+		sig_pri_connected_line_copy_transfer(&bridged_colp, &call_2_ast->connected);
 		if (call_1_held) {
-			ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
+			transferer_is_held = 1;
 		}
 		if (call_2_held) {
-			ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD);
+			bridged_is_held = 1;
 		}
 
 		ast_verb(3, "TRANSFERRING %s to %s\n", call_2_ast->name, call_1_ast->name);
-		retval = ast_channel_masquerade(call_1_ast, bridged);
+		retval = ast_channel_masquerade(transferer, bridged);
 	} else {
 		/* Try masquerading the other way. */
 		bridged = ast_bridged_channel(call_1_ast);
 		if (bridged) {
+			transferer = call_2_ast;
+			sig_pri_connected_line_copy_transfer(&transferer_colp, &call_2_ast->connected);
+			sig_pri_connected_line_copy_transfer(&bridged_colp, &call_1_ast->connected);
+			if (call_2_held) {
+				transferer_is_held = 1;
+			}
 			if (call_1_held) {
-				ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD);
+				bridged_is_held = 1;
 			}
-			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);
+			retval = ast_channel_masquerade(transferer, bridged);
 		} else {
-			/* Could not transfer. */
+			/* Could not transfer.  Neither call is bridged. */
 			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);
-		}
-	}
-
+
+	/*
+	 * 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 "transferer" to be destroyed
+	 * in the pbx thread. To prevent this we must give "transferer"
+	 * a reference before any unlocking takes place.
+	 */
+	if (transferer) {
+		ao2_ref(transferer, +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]);
 
-	return retval;
+	if (retval) {
+		if (transferer) {
+			ao2_ref(transferer, -1);
+		}
+		ast_party_connected_line_free(&transferer_colp);
+		ast_party_connected_line_free(&bridged_colp);
+		if (callback) {
+			/* Transfer failed. */
+			callback(data, 0);
+		}
+		return -1;
+	}
+
+	if (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.
+		 */
+		callback(data, 1);
+	}
+
+	/*
+	 * Make sure masquerade is complete.
+	 *
+	 * 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(transferer);
+
+	/* Release any hold on the channels. */
+	if (transferer_is_held) {
+		ast_queue_control(transferer, AST_CONTROL_UNHOLD);
+	}
+	if (bridged_is_held) {
+		ast_indicate(transferer, AST_CONTROL_UNHOLD);
+	}
+
+	/* Transfer COLP */
+	{
+		/*
+		 * Since "transferer" 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 "transferer".
+		 */
+		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), &transferer_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(transferer, AST_CONTROL_READ_ACTION, frame_payload,
+				frame_size);
+		}
+		/*
+		 * In addition to queueing the read action frame so that the
+		 * connected line info on "transferer" will be updated, we also
+		 * are going to queue a plain old connected line update on
+		 * "transferer" to update the "bridged" channel.
+		 */
+		ast_channel_queue_connected_line_update(transferer, &bridged_colp, NULL);
+	}
+
+	ast_party_connected_line_free(&transferer_colp);
+	ast_party_connected_line_free(&bridged_colp);
+
+	ao2_ref(transferer, -1);
+	ast_mutex_lock(&pri->lock);
+	return 0;
 }
 #endif	/* defined(HAVE_PRI_CALL_HOLD) || defined(HAVE_PRI_TRANSFER) */
 
@@ -3256,20 +3451,29 @@
  * \brief send an AOC-E termination request on ast_channel and set
  * hangup delay.
  *
- * \param sig_pri_chan private
+ * \param pri sig_pri PRI control structure.
+ * \param chanpos Channel position in the span.
  * \param ms to delay hangup
  *
- * \note assumes pvt is locked
+ * \note Assumes the pri->lock is already obtained.
+ * \note Assumes the sig_pri_lock_private(pri->pvts[chanpos]) is already obtained.
  *
  * \return Nothing
  */
-static void sig_pri_send_aoce_termination_request(struct sig_pri_chan *pvt, unsigned int ms)
-{
+static void sig_pri_send_aoce_termination_request(struct sig_pri_span *pri, int chanpos, unsigned int ms)
+{
+	struct sig_pri_chan *pvt;
 	struct ast_aoc_decoded *decoded = NULL;
 	struct ast_aoc_encoded *encoded = NULL;
 	size_t encoded_size;
 	struct timeval whentohangup = { 0, };
 
+	sig_pri_lock_owner(pri, chanpos);
+	pvt = pri->pvts[chanpos];
+	if (!pvt->owner) {
+		return;
+	}
+
 	if (!(decoded = ast_aoc_create(AST_AOC_REQUEST, 0, AST_AOC_REQUEST_E))) {
 		ast_softhangup_nolock(pvt->owner, AST_SOFTHANGUP_DEV);
 		goto cleanup_termination_request;
@@ -3296,6 +3500,7 @@
 	ast_log(LOG_DEBUG, "Delaying hangup on %s for aoc-e msg\n", pvt->owner->name);
 
 cleanup_termination_request:
+	ast_channel_unlock(pvt->owner);
 	ast_aoc_destroy_decoded(decoded);
 	ast_aoc_destroy_encoded(encoded);
 }
@@ -3594,6 +3799,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;
@@ -3812,11 +4020,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) */
@@ -5257,7 +5467,7 @@
 									if (detect_aoc_e_subcmd(e->hangup.subcmds)) {
 										/* If a AOC-E msg was sent during the release, we must use a
 										 * AST_CONTROL_HANGUP frame to guarantee that frame gets read before hangup */
-										ast_queue_control(pri->pvts[chanpos]->owner, AST_CONTROL_HANGUP);
+										pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP);
 									} else {
 										pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
 									}
@@ -5342,7 +5552,7 @@
 							/* 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)) {
+								e->hangup.call_held, 1, NULL, NULL)) {
 								break;
 							}
 							sig_pri_lock_private(pri->pvts[chanpos]);
@@ -5395,12 +5605,15 @@
 
 							if (do_hangup) {
 #if defined(HAVE_PRI_AOC_EVENTS)
-								if (!pri->pvts[chanpos]->holding_aoce && pri->aoce_delayhangup && ast_bridged_channel(pri->pvts[chanpos]->owner)) {
-									sig_pri_send_aoce_termination_request(pri->pvts[chanpos], pri_get_timer(pri->pri, PRI_TIMER_T305) / 2);
+								if (!pri->pvts[chanpos]->holding_aoce
+									&& pri->aoce_delayhangup
+									&& ast_bridged_channel(pri->pvts[chanpos]->owner)) {
+									sig_pri_send_aoce_termination_request(pri, chanpos,
+										pri_get_timer(pri->pri, PRI_TIMER_T305) / 2);
 								} else if (detect_aoc_e_subcmd(e->hangup.subcmds)) {
 									/* If a AOC-E msg was sent during the Disconnect, we must use a AST_CONTROL_HANGUP frame
 									 * to guarantee that frame gets read before hangup */
-									ast_queue_control(pri->pvts[chanpos]->owner, AST_CONTROL_HANGUP);
+									pri_queue_control(pri, chanpos, AST_CONTROL_HANGUP);
 								} else {
 									pri->pvts[chanpos]->owner->_softhangup |= AST_SOFTHANGUP_DEV;
 								}




More information about the asterisk-commits mailing list