diff --git a/channels/chan_brcm.c b/channels/chan_brcm.c index 756a8621070a8b5f3c2af7ea54155f06c60be384..663a59d1f90ebe3e21d48cde2b7bb030fbd8d945 100644 --- a/channels/chan_brcm.c +++ b/channels/chan_brcm.c @@ -350,7 +350,7 @@ static struct ast_channel_tech brcm_tech = { .send_digit_end = brcm_senddigit_end, //Channel is NOT locked .indicate = brcm_indicate, //Channel is locked .transfer = brcm_transfer, // Channel is locked - .devicestate = brcm_devicestate // Channel is NOT locked + .devicestate = brcm_devicestate // Channel is NOT locked }; static struct brcm_channel_tech fxs_tech = { @@ -574,6 +574,10 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat case AST_CONTROL_UPDATE_RTP_PEER: case AST_CONTROL_SRCUPDATE: case AST_CONTROL_SRCCHANGE: + sub->codec = -1; + if (sub->channel_state == RINGBACK) + endpt_signal(sub->parent->line_id, "ringback", "off", NULL); + res = 0; break; case AST_CONTROL_RINGING: ast_debug(4, "Got AST_CONTROL_RINGING on %s, sub->codec = %d\n", ast_channel_name(ast), sub->codec); @@ -587,6 +591,29 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat } res = 0; break; + case AST_CONTROL_UNHOLD_FOR_TRANSFER: + res = -1; + if (sub->channel_state == TRANSFERING) { + struct ast_channel *bridged_chan = ast_channel_bridge_peer(sub->owner); + if (bridged_chan) { + ast_debug(4, "Start unattended transfer to [%s] on channel %s\n", sub->blind_xfer_target, ast_channel_name(bridged_chan)); + ast_channel_lock(bridged_chan); + if (!ast_test_flag(ast_channel_flags(bridged_chan), AST_FLAG_ZOMBIE) && + !ast_check_hangup(bridged_chan)) { + if (ast_channel_tech(bridged_chan)->transfer) { + res = ast_channel_tech(bridged_chan)->transfer(bridged_chan, (const char *)sub->blind_xfer_target); + if (res != 0) + ast_log(LOG_ERROR, "ast_transfer() failed\n"); + } else + ast_log(LOG_ERROR, "ast_transfer() is not supported on the peer channel\n"); + } + ast_channel_unlock(bridged_chan); + } else { + ast_log(LOG_ERROR, "can't get the peer channel, unattended call transfer will not be proceeded\n"); + } + + } + break; case AST_CONTROL_TRANSFER: res = -1; if (datalen != sizeof(enum ast_control_transfer)) { @@ -595,7 +622,10 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat enum ast_control_transfer *message = (enum ast_control_transfer *) data; struct brcm_subchannel* peer_sub; peer_sub = brcm_subchannel_get_peer(sub); - brcm_finish_transfer(ast, peer_sub, *message); + if (peer_sub->channel_state == TRANSFERING) + brcm_finish_transfer(ast, peer_sub, *message); + else + brcm_finish_transfer(ast, sub, *message); } break; case AST_CONTROL_CONGESTION: @@ -668,6 +698,7 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat const struct ast_control_pvt_cause_code *cause_code = data; int ast_cause = cause_code->ast_cause; ast_debug(2, "AST_CONTROL_PVT_CAUSE_CODE = %d, chan_name = %s\n", ast_cause, cause_code->chan_name); + switch (ast_cause) { case AST_CAUSE_NO_USER_RESPONSE: case AST_CAUSE_NO_ANSWER: @@ -681,7 +712,7 @@ static int brcm_indicate(struct ast_channel *ast, int condition, const void *dat res = -1; break; case AST_CAUSE_NORMAL_CLEARING: - // This is just fine. + // This is just fine. break; case AST_CAUSE_USER_BUSY: endpt_signal(sub->parent->line_id, "ringback", "off", NULL); @@ -1942,7 +1973,6 @@ static int handle_autodial_timeout(const void *data) */ static int handle_dialtone_timeout(const void *data) { - ast_debug(9, "Dialtone timeout\n"); struct brcm_pvt *p = (struct brcm_pvt *) data; pvt_lock(p, "dialtone timeout"); @@ -1951,6 +1981,9 @@ static int handle_dialtone_timeout(const void *data) p->dialtone_timeout_timer_id = -1; struct brcm_subchannel *sub = brcm_get_active_subchannel(p); + + ast_debug(9, "Dialtone timeout, sub->channel_state: %s\n", state2str(sub->channel_state)); + if (sub && (sub->channel_state == OFFHOOK || sub->channel_state == AWAITONHOOK || sub->channel_state == CALLENDED)) { /* Enter state where nothing else other than ONHOOK is accepted and play series of tones */ sub->channel_state = AWAITONHOOK; @@ -2168,8 +2201,7 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel sub_peer->channel_state = INCALL; } /* Switch back to old call (remote hung up) */ - } else if ((sub->channel_state == ONHOOK || sub->channel_state == CALLENDED ||sub->channel_state == RINGBACK) - && sub_peer->channel_state == ONHOLD) { + } else if ((sub->channel_state == ONHOOK || sub->channel_state == CALLENDED) && sub_peer->channel_state == ONHOLD) { ast_debug(2, "R when idle and peer subchannel on hold\n"); brcm_cancel_dialing_timeouts(p); @@ -2234,11 +2266,16 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel sub->channel_state = ONHOLD; } } - } else if (sub->channel_state == DIALING || sub->channel_state == OFFHOOK || sub->channel_state == RINGBACK) { + } else if ((sub->channel_state == DIALING || sub->channel_state == OFFHOOK || sub->channel_state == RINGBACK) && + sub_peer->channel_state != ONHOLD) { ast_log(LOG_NOTICE, "R while offhook/dialing\n"); brcm_cancel_dialing_timeouts(p); brcm_reset_dtmf_buffer(p); p->hf_detected = 0; + } else if (sub->channel_state == RINGBACK && sub_peer->channel_state == ONHOLD) { + ast_debug(4, "R during ringback\n"); + sub->channel_state = INCALL; + sub->conf_timer_id = ast_sched_add(sched, dtmf_wait_timer, setup_conference_call_cb, p); } } @@ -2300,7 +2337,40 @@ static void handle_hookflash(struct brcm_subchannel *sub, struct brcm_subchannel else ast_log(LOG_ERROR,"Late R4 ,Ignoring since conference should be set up by now \n"); break; + case '5': + ast_debug(4 ,"R5 unattended call transfer\n"); + if((sub_peer->conf_timer_id != -1) || (sub->conf_timer_id != -1)) { + if(sub->conf_timer_id != -1) { + if (ast_sched_del(sched, sub->conf_timer_id)) + ast_log(LOG_ERROR, "Failed to remove scheduled conference setup timer\n"); + ast_debug(4, "sub->conf_timer_id deleted\n"); + } + sub->conf_timer_id = -1; + if(sub_peer->conf_timer_id != -1) { + if (ast_sched_del(sched, sub_peer->conf_timer_id)) + ast_log(LOG_ERROR, "Failed to remove scheduled conference setup timer\n"); + ast_debug(4, "sub_peer->conf_timer_id deleted\n"); + } + sub_peer->conf_timer_id = -1; + channel_settings *s = &channel_config[p->line_id]; + if (!s->calleridenable) { + p->tech->stop_ringing(p); + } else { + p->tech->stop_ringing_callerid_pending(p); + } + + // unattended call transfer: cancel the the call to transfer target + if (sub->owner && sub_peer->owner) { + strncpy(sub_peer->blind_xfer_target, ast_channel_exten(owner), sizeof(sub->blind_xfer_target) - 1); + ast_queue_control(sub->owner, AST_CONTROL_HANGUP); + ast_queue_unhold(sub_peer->owner); + sub_peer->channel_state = TRANSFERING; + } + } + else + ast_log(LOG_ERROR,"Late R5 ,Ignoring since conference should be set up by now \n"); + break; default: ast_log(LOG_ERROR, "Unhandled DTMF %c\n", p->dtmfbuf[0]); break; @@ -3082,6 +3152,7 @@ static struct brcm_pvt *brcm_allocate_pvt(void) tmp->sub[i] = sub; sub->jitter_count = 0; sub->farEndInterrivalJitter = 0; + sub->blind_xfer_target[0] = '\0'; ast_debug(2, "subchannel created\n"); } else { ast_log(LOG_ERROR, "no subchannel created\n"); diff --git a/channels/chan_brcm.h b/channels/chan_brcm.h index 84b5203ee911a6d067708812cdbdd1af6af03a80..2e52f85919e7b376c8cca250a203e90ecb87e09b 100644 --- a/channels/chan_brcm.h +++ b/channels/chan_brcm.h @@ -133,6 +133,7 @@ struct brcm_subchannel { rtp_statistics rtp_stats; /* RTP statistics for currently hanging up channel */ unsigned int jitter_count; unsigned long int farEndInterrivalJitter; + char blind_xfer_target[32]; /* Transfer target for unattended call transfer */ }; diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index e8b387ca04f665d3a6918f6b284d1e489e447f19..843ef61b4834bef841cc73905be96e7360b2ac45 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -1806,6 +1806,12 @@ static int chan_pjsip_indicate(struct ast_channel *ast, int condition, const voi ao2_ref(channel->session, -1); } } + struct ast_channel *bridged_chan; + bridged_chan = ast_channel_bridge_peer(ast); + + if (bridged_chan) { + ast_indicate(bridged_chan, AST_CONTROL_UNHOLD_FOR_TRANSFER); + } break; case AST_CONTROL_SRCUPDATE: break; @@ -2060,11 +2066,17 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event) } } + // leaving channel with Ref > 2 cause it is not destroyed during hangup + while (2 < ao2_ref(chan, 0)) + ao2_ref(chan, -1); + struct ast_channel *bridged_chan = ast_channel_bridge_peer(chan); + if (bridged_chan) { + while (2 < ao2_ref(bridged_chan, 0)) + ao2_ref(bridged_chan, -1); + } + if (res) { ast_queue_control_data(chan, AST_CONTROL_TRANSFER, &message, sizeof(message)); - // leaving channel with Ref > 2 cause it is not destroyed during hangup - while (2 < ao2_ref(chan, 0)) - ao2_ref(chan, -1); } } @@ -2133,6 +2145,7 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) char *chan_name; char referto[256]; char replaces[256] = ""; + char target_dialog[256] = ""; pj_bzero(&xfer_cb, sizeof(xfer_cb)); xfer_cb.on_evsub_state = &xfer_client_on_evsub_state; @@ -2168,6 +2181,23 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) goto failure; } + // unattended call transfer + if (!chan_name) { + const char *callid = chan_pjsip_get_uniqueid(session->channel); + const char *totag; + const char *fromtag; + + char local_tag[pj_strlen(&session->inv_session->dlg->local.info->tag) + 1]; + char remote_tag[pj_strlen(&session->inv_session->dlg->remote.info->tag) + 1]; + + ast_copy_pj_str(local_tag, &session->inv_session->dlg->local.info->tag, sizeof(local_tag)); + ast_copy_pj_str(remote_tag, &session->inv_session->dlg->remote.info->tag, sizeof(remote_tag)); + + snprintf(target_dialog, sizeof(target_dialog), "%s;local-tag=%s;remote-tag=%s", + callid, local_tag, remote_tag); + ast_sip_add_header(packet, "Target-Dialog", target_dialog); + } + ref_by_val = pbx_builtin_getvar_helper(chan, "SIPREFERREDBYHDR"); if (!ast_strlen_zero(ref_by_val)) { ast_sip_add_header(packet, "Referred-By", ref_by_val); diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index f6e550f6a51d8ef2bc443dd46fba6f2544b644d2..482c3df82e1fefddc2baa54920e97c6823be608b 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -344,6 +344,7 @@ enum ast_control_frame_type { /* Dial tone chnage */ AST_CONTROL_NORMAL_DIALTONE = 1200, AST_CONTROL_SPECIAL_DIALTONE = 1201, + AST_CONTROL_UNHOLD_FOR_TRANSFER = 1202, }; /*! diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index e2c0ecac4f6853e39db4f5aed53bfcf472c007c5..4a5e44daa4299605d2e8e4288f95aba1644328c3 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -553,11 +553,12 @@ void ast_sip_session_terminate(struct ast_sip_session *session, int response); * \brief Defer local termination of a session until remote side terminates, or an amount of time passes * * \param session The session to defer termination on + * \param timeout Timeout after which session termination will be scheduled * * \retval 0 Success * \retval -1 Failure */ -int ast_sip_session_defer_termination(struct ast_sip_session *session); +int ast_sip_session_defer_termination(struct ast_sip_session *session, int timeout); /*! * \brief Cancel a pending deferred termination. diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c index 2b1c753589eebef602d2050e392987204ff02731..5af0af6229861ed2a0acc29643528fe12d71ba33 100644 --- a/res/res_pjsip_refer.c +++ b/res/res_pjsip_refer.c @@ -823,7 +823,7 @@ static int refer_incoming_attended_request(struct ast_sip_session *session, pjsi refer.refer_to = target_uri; refer.attended = 1; - if (ast_sip_session_defer_termination(session)) { + if (ast_sip_session_defer_termination(session, 1)) { ast_log(LOG_ERROR, "Received REFER for remote session on channel '%s' from endpoint '%s' but could not defer termination, rejecting\n", ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint)); @@ -868,7 +868,7 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), context); } - if (!ast_exists_extension(NULL, context, exten, 1, NULL)) { + if (!ast_exists_extension(NULL, context, "external_replaces", 1, NULL)) { ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer to '%s@%s' but target does not exist\n", ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint), exten, context); return 404; @@ -880,7 +880,7 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r refer.refer_to = target; refer.attended = 0; - if (ast_sip_session_defer_termination(session)) { + if (ast_sip_session_defer_termination(session, 60)) { ast_log(LOG_ERROR, "Channel '%s' from endpoint '%s' attempted blind transfer but could not defer termination, rejecting\n", ast_channel_name(session->channel), ast_sorcery_object_get_id(session->endpoint)); @@ -888,7 +888,7 @@ static int refer_incoming_blind_request(struct ast_sip_session *session, pjsip_r } response = xfer_response_code2sip(ast_bridge_transfer_blind(1, session->channel, - exten, context, refer_blind_callback, &refer)); + "external_replaces", context, refer_blind_callback, &refer)); ast_sip_session_end_if_deferred(session); if (response != 200) { diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 1f69f3de65c060f4169240cff008010a4cf5f17f..e126bfc8c0fcf6a37e2ed9f21c5fe8cd9fc0891e 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -3623,9 +3623,10 @@ static void session_termination_cb(pj_timer_heap_t *timer_heap, struct pj_timer_ } } -int ast_sip_session_defer_termination(struct ast_sip_session *session) +int ast_sip_session_defer_termination(struct ast_sip_session *session, int timeout) { - pj_time_val delay = { .sec = 1, }; + pj_time_val delay; + delay.sec = timeout; int res; /* The session should not have an active deferred termination request. */