diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c index 8785bfa9fb1ba054a0635ed9220cf48ae06979b6..5d879410695cd5b0f8fcd7193d2143043af779cc 100644 --- a/channels/chan_pjsip.c +++ b/channels/chan_pjsip.c @@ -2041,6 +2041,58 @@ static void xfer_client_on_evsub_state(pjsip_evsub *sub, pjsip_event *event) } } +/*! \brief Build the Replace parameter in the URI of Refer-To header field */ +static void sip_build_replaces_param(const struct ast_channel *ast, const char *chan_name, char *replaces, size_t len) +{ + struct ast_channel *replaces_chan = ast_channel_get_by_name(chan_name); + + if (replaces_chan == NULL) { + ast_log(LOG_NOTICE, "Unable to fetch channel [%s] to be replaced. Refer-To header will be missing " + "Replaces parameter\n", chan_name); + return; + } + + // Make sure this is a SIP channel too + const char *type = ast_channel_tech(replaces_chan)->type; + if (strcmp(type, ast_channel_tech(ast)->type) == 0) { + struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(replaces_chan); + + if (!channel) { + ast_log(LOG_ERROR, "Channel %s has no pvt!\n", ast_channel_name(replaces_chan)); + return -1; + } + + //Get callid and tags from SIP dialog to replace + const char *callid = chan_pjsip_get_uniqueid(replaces_chan); + const char *totag; + const char *fromtag; + char tmp[len]; + char local_tag[pj_strlen(&channel->session->inv_session->dlg->local.info->tag) + 1]; + char remote_tag[pj_strlen(&channel->session->inv_session->dlg->remote.info->tag) + 1]; + + ast_copy_pj_str(local_tag, &channel->session->inv_session->dlg->local.info->tag, sizeof(local_tag)); + ast_copy_pj_str(remote_tag, &channel->session->inv_session->dlg->remote.info->tag, sizeof(remote_tag)); + + if(channel->session->call_direction == AST_SIP_SESSION_OUTGOING_CALL) { + totag = remote_tag; + fromtag = local_tag; + } + else { + totag = local_tag; + fromtag = remote_tag; + } + + // Create URI encoded Replaces parameter + // ?Replaces=<callid>;to-tag=<totag>;from-tag=<fromtag> + snprintf(replaces, len - 1, "%s;to-tag=%s;from-tag=%s", callid, totag, fromtag); + ast_uri_encode(replaces, tmp, sizeof(tmp), ast_uri_http); + snprintf(replaces, len - 1, "?Replaces=%s", tmp); + + } else { + ast_log(LOG_NOTICE, "Asked to replace a call on channel type %s\n", type); + } +} + static void transfer_refer(struct ast_sip_session *session, const char *target) { pjsip_evsub *sub; @@ -2051,6 +2103,9 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) char local_info[pj_strlen(&session->inv_session->dlg->local.info_str) + 1]; struct pjsip_evsub_user xfer_cb; struct ast_channel *chan = session->channel; + char *chan_name; + char referto[256]; + char replaces[256] = ""; pj_bzero(&xfer_cb, sizeof(xfer_cb)); xfer_cb.on_evsub_state = &xfer_client_on_evsub_state; @@ -2068,7 +2123,21 @@ static void transfer_refer(struct ast_sip_session *session, const char *target) pjsip_evsub_set_mod_data(sub, refer_callback_module.id, chan); ao2_ref(chan, +1); - if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, target), &packet) != PJ_SUCCESS) { + /* Get the channel name if any */ + chan_name = strcasestr(target, "?Replaces="); + if (chan_name) { + *chan_name = '\0'; + chan_name += strlen("?Replaces="); + if (session->channel) + sip_build_replaces_param(session->channel, chan_name, replaces, sizeof(replaces)); + ast_debug(1, "Channel name is [%s], new dest is [%s], final replaces param is [%s]\n", + chan_name, target, replaces); + } + + snprintf(referto, sizeof(referto), "<sip:%s@%s%s>", target, session->endpoint->fromdomain, replaces); + ast_debug(1, "Refer-To: %s\n", referto); + + if (pjsip_xfer_initiate(sub, pj_cstr(&tmp, referto), &packet) != PJ_SUCCESS) { goto failure; }