From 7221d36d1547e61bbb37ec917d16427ac197af91 Mon Sep 17 00:00:00 2001 From: rmudgett Date: Tue, 28 Sep 2010 00:32:18 +0000 Subject: Break up long ast_manager_event_multichan() event lines. git-svn-id: http://svn.digium.com/svn/asterisk/branches/1.8@289054 f38db490-d61c-443f-a65b-d21fe96a405b --- channels/chan_sip.c | 31 +++++- channels/sig_pri.c | 279 +++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 274 insertions(+), 36 deletions(-) diff --git a/channels/chan_sip.c b/channels/chan_sip.c index cc110c830..d222bda61 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -21722,7 +21722,14 @@ static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual * /* 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 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int 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 @@ static int handle_request_refer(struct sip_pvt *p, struct sip_request *req, int 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, diff --git a/channels/sig_pri.c b/channels/sig_pri.c index 251e6a04a..0ca2f251e 100644 --- a/channels/sig_pri.c +++ b/channels/sig_pri.c @@ -1882,6 +1882,84 @@ static void sig_pri_mcid_event(struct sig_pri_span *pri, const struct pri_subcmd } #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 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 @@ -1893,24 +1971,36 @@ static void sig_pri_mcid_event(struct sig_pri_span *pri, const struct pri_subcmd * \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 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1, 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 @@ static int sig_pri_attempt_transfer(struct sig_pri_span *pri, q931_call *call_1, } 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) { - if (call_1_held) { - ast_queue_control(call_1_ast, AST_CONTROL_UNHOLD); - } + 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) { - ast_queue_control(call_2_ast, AST_CONTROL_UNHOLD); + transferer_is_held = 1; + } + if (call_1_held) { + bridged_is_held = 1; } 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 @@ static void sig_pri_aoc_e_from_ast(struct sig_pri_chan *pvt, struct ast_aoc_deco * \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 @@ static void sig_pri_send_aoce_termination_request(struct sig_pri_chan *pvt, unsi 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 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev 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 @@ static void sig_pri_handle_subcmds(struct sig_pri_span *pri, int chanpos, int ev } 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 @@ static void *pri_dchannel(void *vpri) 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 @@ static void *pri_dchannel(void *vpri) /* 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 @@ static void *pri_dchannel(void *vpri) 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; } -- cgit v1.2.3