summaryrefslogtreecommitdiff
path: root/channels/chan_misdn.c
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2010-10-22 15:47:56 +0000
committerRichard Mudgett <rmudgett@digium.com>2010-10-22 15:47:56 +0000
commit64845d73c7cce5c3d5493aa0c360dc78561a655e (patch)
treee7e1a4d6df78df9f6c31d168909d057e529e0d11 /channels/chan_misdn.c
parent0bcdff65ecc59b48d74fd9420bb00c509297b440 (diff)
Merged revisions 292704 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.8 ........ r292704 | rmudgett | 2010-10-22 10:47:08 -0500 (Fri, 22 Oct 2010) | 19 lines 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/ ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@292705 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'channels/chan_misdn.c')
-rw-r--r--channels/chan_misdn.c152
1 files changed, 141 insertions, 11 deletions
diff --git a/channels/chan_misdn.c b/channels/chan_misdn.c
index a6d623e7a..35ac69d06 100644
--- a/channels/chan_misdn.c
+++ b/channels/chan_misdn.c
@@ -8497,6 +8497,40 @@ static void release_chan_early(struct chan_list *ch)
/*!
* \internal
+ * \brief Copy the source connected line information to the destination for a transfer.
+ * \since 1.8
+ *
+ * \param dest Destination connected line
+ * \param src Source connected line
+ *
+ * \return Nothing
+ */
+static void misdn_connected_line_copy_transfer(struct ast_party_connected_line *dest, struct ast_party_connected_line *src)
+{
+ struct ast_party_connected_line connected;
+
+ connected = *src;
+ connected.source = AST_CONNECTED_LINE_UPDATE_SOURCE_TRANSFER;
+
+ /* Make sure empty strings will be erased. */
+ if (!connected.id.name.str) {
+ connected.id.name.str = "";
+ }
+ if (!connected.id.number.str) {
+ connected.id.number.str = "";
+ }
+ if (!connected.id.subaddress.str) {
+ connected.id.subaddress.str = "";
+ }
+ if (!connected.id.tag) {
+ connected.id.tag = "";
+ }
+
+ ast_party_connected_line_copy(dest, &connected);
+}
+
+/*!
+ * \internal
* \brief Attempt to transfer the active channel party to the held channel party.
*
* \param active_ch Channel currently connected.
@@ -8508,7 +8542,10 @@ static void release_chan_early(struct chan_list *ch)
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 @@ static int misdn_attempt_transfer(struct chan_list *active_ch, struct chan_list
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;
+ ast_channel_lock(held_ch->ast);
+ while (ast_channel_trylock(active_ch->ast)) {
+ CHANNEL_DEADLOCK_AVOIDANCE(held_ch->ast);
+ }
- 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 {
+ 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;
+ ast_channel_unlock(held_ch->ast);
+ ast_channel_unlock(active_ch->ast);
+ return -1;
}
- return retval;
+ 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;
}