diff options
author | Richard Mudgett <rmudgett@digium.com> | 2010-11-09 17:00:07 +0000 |
---|---|---|
committer | Richard Mudgett <rmudgett@digium.com> | 2010-11-09 17:00:07 +0000 |
commit | 3adb425b2536c826c66ef5cfc9b2490fd2dbfc28 (patch) | |
tree | 59aa08f9a0bb52e1cd858c5490afa1fee85c6a49 /main/channel.c | |
parent | 9a257b9f9708d9efb542bf989215236b5bbbf94b (diff) |
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: https://origsvn.digium.com/svn/asterisk/trunk@294351 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/channel.c')
-rw-r--r-- | main/channel.c | 207 |
1 files changed, 200 insertions, 7 deletions
diff --git a/main/channel.c b/main/channel.c index 7c1e998af..f4b34a26a 100644 --- a/main/channel.c +++ b/main/channel.c @@ -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); |