mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
471 lines
11 KiB
471 lines
11 KiB
/*
|
|
* Copyright (C) 2009 Daniel-Constantin Mierla (asipto.com)
|
|
*
|
|
* This file is part of kamailio, a free SIP server.
|
|
*
|
|
* kamailio is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* kamailio is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
|
|
/*!
|
|
* \file
|
|
* \brief Call transfer
|
|
* \ingroup dialog
|
|
* Module: \ref dialog
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "../../dprint.h"
|
|
#include "../../ut.h"
|
|
#include "../../trim.h"
|
|
#include "../../mem/mem.h"
|
|
#include "../../mem/shm_mem.h"
|
|
#include "../../parser/parse_from.h"
|
|
#include "../../parser/msg_parser.h"
|
|
|
|
#include "../../modules/tm/tm_load.h"
|
|
|
|
#include "dlg_req_within.h"
|
|
#include "dlg_handlers.h"
|
|
#include "dlg_transfer.h"
|
|
|
|
#define DLG_HOLD_SDP "v=0\r\no=kamailio-bridge 0 0 IN IP4 0.0.0.0\r\ns=kamailio\r\nc=IN IP4 0.0.0.0\r\nt=0 0\r\nm=audio 9 RTP/AVP 8 0\r\na=rtpmap:8 PCMA/8000\r\na=rtpmap:0 PCMU/8000\r\n"
|
|
#define DLG_HOLD_SDP_LEN (sizeof(DLG_HOLD_SDP)-1)
|
|
|
|
/*
|
|
#define DLG_HOLD_CT_HDR "Contact: <sip:kamailio.org:5060>\r\nContent-Type: application/sdp\r\n"
|
|
#define DLG_HOLD_CT_HDR_LEN (sizeof(DLG_HOLD_CT_HDR)-1)
|
|
*/
|
|
|
|
extern str dlg_bridge_controller;
|
|
extern str dlg_bridge_contact;
|
|
|
|
static char *dlg_bridge_hdrs_buf = NULL;
|
|
static str dlg_bridge_inv_hdrs = {0};
|
|
static str dlg_bridge_ref_hdrs = {0};
|
|
|
|
int dlg_bridge_init_hdrs(void)
|
|
{
|
|
if(dlg_bridge_hdrs_buf!=NULL)
|
|
return 0;
|
|
dlg_bridge_hdrs_buf = (char*)pkg_malloc((dlg_bridge_contact.len + 46)
|
|
* sizeof(char));
|
|
if(dlg_bridge_hdrs_buf==NULL) {
|
|
LM_ERR("no more pkg memory\n");
|
|
return -1;
|
|
}
|
|
strncpy(dlg_bridge_hdrs_buf,
|
|
"Contact: <", 10);
|
|
strncpy(dlg_bridge_hdrs_buf + 10,
|
|
dlg_bridge_contact.s, dlg_bridge_contact.len);
|
|
strncpy(dlg_bridge_hdrs_buf + 10 + dlg_bridge_contact.len,
|
|
">\r\nContent-Type: application/sdp\r\n", 34);
|
|
dlg_bridge_hdrs_buf[dlg_bridge_contact.len+44] = '\0';
|
|
dlg_bridge_inv_hdrs.s = dlg_bridge_hdrs_buf;
|
|
dlg_bridge_inv_hdrs.len = dlg_bridge_contact.len + 44;
|
|
dlg_bridge_ref_hdrs.s = dlg_bridge_hdrs_buf;
|
|
dlg_bridge_ref_hdrs.len = dlg_bridge_contact.len + 13;
|
|
return 0;
|
|
}
|
|
|
|
void dlg_bridge_destroy_hdrs(void)
|
|
{
|
|
if(dlg_bridge_hdrs_buf!=NULL)
|
|
pkg_free(dlg_bridge_hdrs_buf);
|
|
}
|
|
|
|
void dlg_transfer_ctx_free(dlg_transfer_ctx_t *dtc)
|
|
{
|
|
struct dlg_cell *dlg;
|
|
|
|
if(dtc==NULL)
|
|
return;
|
|
if(dtc->from.s!=NULL)
|
|
shm_free(dtc->from.s);
|
|
if(dtc->to.s!=NULL)
|
|
shm_free(dtc->to.s);
|
|
|
|
dlg = dtc->dlg;
|
|
if(dlg!=NULL)
|
|
{
|
|
if (dlg->tag[DLG_CALLER_LEG].s)
|
|
shm_free(dlg->tag[DLG_CALLER_LEG].s);
|
|
|
|
if (dlg->tag[DLG_CALLEE_LEG].s)
|
|
shm_free(dlg->tag[DLG_CALLEE_LEG].s);
|
|
|
|
if (dlg->cseq[DLG_CALLER_LEG].s)
|
|
shm_free(dlg->cseq[DLG_CALLER_LEG].s);
|
|
|
|
if (dlg->cseq[DLG_CALLEE_LEG].s)
|
|
shm_free(dlg->cseq[DLG_CALLEE_LEG].s);
|
|
|
|
shm_free(dlg);
|
|
}
|
|
|
|
shm_free(dtc);
|
|
}
|
|
|
|
void dlg_refer_tm_callback(struct cell *t, int type, struct tmcb_params *ps)
|
|
{
|
|
dlg_transfer_ctx_t *dtc = NULL;
|
|
dlg_t* dialog_info = NULL;
|
|
str met = {"BYE", 3};
|
|
int result;
|
|
struct dlg_cell *dlg;
|
|
uac_req_t uac_r;
|
|
|
|
if(ps->param==NULL || *ps->param==0)
|
|
{
|
|
LM_DBG("message id not received\n");
|
|
return;
|
|
}
|
|
dtc = *((dlg_transfer_ctx_t**)ps->param);
|
|
if(dtc==NULL)
|
|
return;
|
|
LM_DBG("REFER completed with status %d\n", ps->code);
|
|
|
|
/* we send the BYE anyhow */
|
|
dlg = dtc->dlg;
|
|
if ((dialog_info = build_dlg_t(dlg, DLG_CALLEE_LEG)) == 0){
|
|
LM_ERR("failed to create dlg_t\n");
|
|
goto error;
|
|
}
|
|
|
|
/* after REFER, the CSeq must be increased */
|
|
dialog_info->loc_seq.value++;
|
|
|
|
set_uac_req(&uac_r, &met, NULL, NULL, dialog_info, 0, NULL, NULL);
|
|
result = d_tmb.t_request_within(&uac_r);
|
|
|
|
if(result < 0) {
|
|
LM_ERR("failed to send the REFER request\n");
|
|
/* todo: clean-up dtc */
|
|
goto error;
|
|
}
|
|
|
|
free_tm_dlg(dialog_info);
|
|
dlg_transfer_ctx_free(dtc);
|
|
|
|
LM_DBG("BYE sent\n");
|
|
return;
|
|
|
|
error:
|
|
dlg_transfer_ctx_free(dtc);
|
|
if(dialog_info)
|
|
free_tm_dlg(dialog_info);
|
|
return;
|
|
|
|
}
|
|
|
|
static int dlg_refer_callee(dlg_transfer_ctx_t *dtc)
|
|
{
|
|
/*verify direction*/
|
|
dlg_t* dialog_info = NULL;
|
|
str met = {"REFER", 5};
|
|
int result;
|
|
str hdrs;
|
|
struct dlg_cell *dlg;
|
|
uac_req_t uac_r;
|
|
|
|
dlg = dtc->dlg;
|
|
|
|
if ((dialog_info = build_dlg_t(dlg, DLG_CALLEE_LEG)) == 0){
|
|
LM_ERR("failed to create dlg_t\n");
|
|
goto error;
|
|
}
|
|
|
|
hdrs.len = 23 + 2*CRLF_LEN + dlg_bridge_controller.len
|
|
+ dtc->to.len + dlg_bridge_ref_hdrs.len;
|
|
LM_DBG("sending REFER [%d] <%.*s>\n", hdrs.len, dtc->to.len, dtc->to.s);
|
|
hdrs.s = (char*)pkg_malloc(hdrs.len*sizeof(char));
|
|
if(hdrs.s == NULL)
|
|
goto error;
|
|
memcpy(hdrs.s, "Referred-By: ", 13);
|
|
memcpy(hdrs.s+13, dlg_bridge_controller.s, dlg_bridge_controller.len);
|
|
memcpy(hdrs.s+13+dlg_bridge_controller.len, CRLF, CRLF_LEN);
|
|
memcpy(hdrs.s+13+dlg_bridge_controller.len+CRLF_LEN, "Refer-To: ", 10);
|
|
memcpy(hdrs.s+23+dlg_bridge_controller.len+CRLF_LEN, dtc->to.s,
|
|
dtc->to.len);
|
|
memcpy(hdrs.s+23+dlg_bridge_controller.len+CRLF_LEN+dtc->to.len,
|
|
CRLF, CRLF_LEN);
|
|
memcpy(hdrs.s+23+dlg_bridge_controller.len+CRLF_LEN+dtc->to.len+CRLF_LEN,
|
|
dlg_bridge_ref_hdrs.s, dlg_bridge_ref_hdrs.len);
|
|
|
|
set_uac_req(&uac_r, &met, &hdrs, NULL, dialog_info, TMCB_LOCAL_COMPLETED,
|
|
dlg_refer_tm_callback, (void*)dtc);
|
|
result = d_tmb.t_request_within(&uac_r);
|
|
|
|
pkg_free(hdrs.s);
|
|
if(result < 0) {
|
|
LM_ERR("failed to send the REFER request\n");
|
|
/* todo: clean-up dtc */
|
|
goto error;
|
|
}
|
|
|
|
free_tm_dlg(dialog_info);
|
|
|
|
LM_DBG("REFER sent\n");
|
|
return 0;
|
|
|
|
error:
|
|
if(dialog_info)
|
|
free_tm_dlg(dialog_info);
|
|
return -1;
|
|
}
|
|
|
|
|
|
void dlg_bridge_tm_callback(struct cell *t, int type, struct tmcb_params *ps)
|
|
{
|
|
struct sip_msg *msg = NULL;
|
|
dlg_transfer_ctx_t *dtc = NULL;
|
|
struct dlg_cell *dlg = NULL;
|
|
str s;
|
|
str cseq;
|
|
str empty = {"", 0};
|
|
|
|
if(ps->param==NULL || *ps->param==0)
|
|
{
|
|
LM_DBG("message id not received\n");
|
|
return;
|
|
}
|
|
dtc = *((dlg_transfer_ctx_t**)ps->param);
|
|
if(dtc==NULL)
|
|
return;
|
|
LM_DBG("completed with status %d\n", ps->code);
|
|
if(ps->code>=300)
|
|
goto error;
|
|
|
|
/* 2xx - build dialog/send refer */
|
|
msg = ps->rpl;
|
|
if((msg->cseq==NULL || parse_headers(msg,HDR_CSEQ_F,0)<0)
|
|
|| msg->cseq==NULL || msg->cseq->parsed==NULL)
|
|
{
|
|
LM_ERR("bad sip message or missing CSeq hdr :-/\n");
|
|
goto error;
|
|
}
|
|
cseq = (get_cseq(msg))->number;
|
|
|
|
if((msg->to==NULL && parse_headers(msg, HDR_TO_F,0)<0) || msg->to==NULL)
|
|
{
|
|
LM_ERR("bad request or missing TO hdr\n");
|
|
goto error;
|
|
}
|
|
if(parse_from_header(msg))
|
|
{
|
|
LM_ERR("bad request or missing FROM hdr\n");
|
|
goto error;
|
|
}
|
|
if((msg->callid==NULL && parse_headers(msg,HDR_CALLID_F,0)<0)
|
|
|| msg->callid==NULL){
|
|
LM_ERR("bad request or missing CALLID hdr\n");
|
|
goto error;
|
|
}
|
|
s = msg->callid->body;
|
|
trim(&s);
|
|
|
|
/* some sanity checks */
|
|
if (s.len==0 || get_from(msg)->tag_value.len==0) {
|
|
LM_ERR("invalid request -> callid (%d) or from TAG (%d) empty\n",
|
|
s.len, get_from(msg)->tag_value.len);
|
|
goto error;
|
|
}
|
|
|
|
dlg = build_new_dlg(&s /*callid*/, &(get_from(msg)->uri) /*from uri*/,
|
|
&(get_to(msg)->uri) /*to uri*/,
|
|
&(get_from(msg)->tag_value)/*from_tag*/,
|
|
&(get_to(msg)->uri) /*use to as r-uri*/ );
|
|
if (dlg==0) {
|
|
LM_ERR("failed to create new dialog\n");
|
|
goto error;
|
|
}
|
|
dtc->dlg = dlg;
|
|
if (dlg_set_leg_info(dlg, &(get_from(msg)->tag_value),
|
|
&empty, &dlg_bridge_controller, &cseq, DLG_CALLER_LEG)!=0) {
|
|
LM_ERR("dlg_set_leg_info failed\n");
|
|
goto error;
|
|
}
|
|
|
|
if (populate_leg_info(dlg, msg, t, DLG_CALLEE_LEG,
|
|
&(get_to(msg)->tag_value)) !=0)
|
|
{
|
|
LM_ERR("could not add further info to the dialog\n");
|
|
shm_free(dlg);
|
|
goto error;
|
|
}
|
|
|
|
if(dlg_refer_callee(dtc)!=0)
|
|
goto error;
|
|
return;
|
|
|
|
error:
|
|
dlg_transfer_ctx_free(dtc);
|
|
return;
|
|
}
|
|
|
|
|
|
int dlg_bridge(str *from, str *to, str *op, str *bd)
|
|
{
|
|
dlg_transfer_ctx_t *dtc;
|
|
int ret;
|
|
str s_method = {"INVITE", 6};
|
|
str s_body;
|
|
uac_req_t uac_r;
|
|
|
|
dtc = (dlg_transfer_ctx_t*)shm_malloc(sizeof(dlg_transfer_ctx_t));
|
|
if(dtc==NULL)
|
|
{
|
|
LM_ERR("no shm\n");
|
|
return -1;
|
|
}
|
|
memset(dtc, 0, sizeof(dlg_transfer_ctx_t));
|
|
dtc->from.s = (char*)shm_malloc((from->len+1)*sizeof(char));
|
|
if(dtc->from.s==NULL)
|
|
{
|
|
LM_ERR("no shm\n");
|
|
shm_free(dtc);
|
|
return -1;
|
|
}
|
|
dtc->to.s = (char*)shm_malloc((to->len+1)*sizeof(char));
|
|
if(dtc->to.s==NULL)
|
|
{
|
|
LM_ERR("no shm\n");
|
|
shm_free(dtc->from.s);
|
|
shm_free(dtc);
|
|
return -1;
|
|
}
|
|
memcpy(dtc->from.s, from->s, from->len);
|
|
dtc->from.len = from->len;
|
|
dtc->from.s[dtc->from.len] = '\0';
|
|
memcpy(dtc->to.s, to->s, to->len);
|
|
dtc->to.len = to->len;
|
|
dtc->to.s[dtc->to.len] = '\0';
|
|
|
|
LM_DBG("bridge <%.*s> to <%.*s>\n", dtc->from.len, dtc->from.s,
|
|
dtc->to.len, dtc->to.s);
|
|
if(bd!=NULL && bd->s!=NULL && bd->len>0) {
|
|
s_body.s = bd->s;
|
|
s_body.len = bd->len;
|
|
} else {
|
|
s_body.s = DLG_HOLD_SDP;
|
|
s_body.len = DLG_HOLD_SDP_LEN;
|
|
}
|
|
|
|
memset(&uac_r, '\0', sizeof(uac_req_t));
|
|
uac_r.method = &s_method;
|
|
uac_r.headers = &dlg_bridge_inv_hdrs;
|
|
uac_r.body = &s_body;
|
|
uac_r.cb_flags = TMCB_LOCAL_COMPLETED;
|
|
uac_r.cb = dlg_bridge_tm_callback;
|
|
uac_r.cbp = (void*)(long)dtc;
|
|
ret = d_tmb.t_request(&uac_r, /* UAC Req */
|
|
&dtc->from, /* Request-URI (To) */
|
|
&dtc->from, /* To */
|
|
&dlg_bridge_controller, /* From */
|
|
(op != NULL && op->len>0)?op:NULL /* Outbound-URI */
|
|
);
|
|
|
|
if(ret<0)
|
|
{
|
|
dlg_transfer_ctx_free(dtc);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dlg_transfer(struct dlg_cell *dlg, str *to, int side)
|
|
{
|
|
dlg_transfer_ctx_t *dtc = NULL;
|
|
struct dlg_cell *ndlg = NULL;
|
|
str from;
|
|
str empty = {"", 0};
|
|
|
|
dtc = (dlg_transfer_ctx_t*)shm_malloc(sizeof(dlg_transfer_ctx_t));
|
|
if(dtc==NULL)
|
|
{
|
|
LM_ERR("no shm\n");
|
|
return -1;
|
|
}
|
|
if(side==DLG_CALLEE_LEG)
|
|
{
|
|
from = dlg->from_uri;
|
|
} else {
|
|
from = dlg->to_uri;
|
|
}
|
|
memset(dtc, 0, sizeof(dlg_transfer_ctx_t));
|
|
dtc->from.s = (char*)shm_malloc((from.len+1)*sizeof(char));
|
|
if(dtc->from.s==NULL)
|
|
{
|
|
LM_ERR("no shm\n");
|
|
shm_free(dtc);
|
|
return -1;
|
|
}
|
|
dtc->to.s = (char*)shm_malloc((to->len+1)*sizeof(char));
|
|
if(dtc->to.s==NULL)
|
|
{
|
|
LM_ERR("no shm\n");
|
|
shm_free(dtc->from.s);
|
|
shm_free(dtc);
|
|
return -1;
|
|
}
|
|
memcpy(dtc->from.s, from.s, from.len);
|
|
dtc->from.len = from.len;
|
|
dtc->from.s[dtc->from.len] = '\0';
|
|
memcpy(dtc->to.s, to->s, to->len);
|
|
dtc->to.len = to->len;
|
|
dtc->to.s[dtc->to.len] = '\0';
|
|
|
|
if(side==DLG_CALLER_LEG)
|
|
ndlg = build_new_dlg(&dlg->callid /*callid*/,
|
|
&dlg->to_uri /*from uri*/, &dlg->from_uri /*to uri*/,
|
|
&dlg->tag[side]/*from_tag*/, &dlg->req_uri /*req uri */ );
|
|
else
|
|
ndlg = build_new_dlg(&dlg->callid /*callid*/,
|
|
&dlg->from_uri /*from uri*/, &dlg->to_uri /*to uri*/,
|
|
&dlg->tag[side]/*from_tag*/, &dlg->req_uri /*req uri */ );
|
|
if (ndlg==0) {
|
|
LM_ERR("failed to create new dialog\n");
|
|
goto error;
|
|
}
|
|
dtc->dlg = ndlg;
|
|
if (dlg_set_leg_info(ndlg, &dlg->tag[side], &empty,
|
|
&dlg->contact[side], &dlg->cseq[side], DLG_CALLER_LEG)!=0)
|
|
{
|
|
LM_ERR("dlg_set_leg_info failed for caller\n");
|
|
goto error;
|
|
}
|
|
if(side==DLG_CALLEE_LEG)
|
|
side = DLG_CALLER_LEG;
|
|
else
|
|
side = DLG_CALLEE_LEG;
|
|
if (dlg_set_leg_info(ndlg, &dlg->tag[side], &dlg->route_set[side],
|
|
&dlg->contact[side], &dlg->cseq[side], DLG_CALLEE_LEG)!=0)
|
|
{
|
|
LM_ERR("dlg_set_leg_info failed for caller\n");
|
|
goto error;
|
|
}
|
|
|
|
if(dlg_refer_callee(dtc)!=0)
|
|
goto error;
|
|
return 0;
|
|
|
|
error:
|
|
dlg_transfer_ctx_free(dtc);
|
|
return -1;
|
|
}
|
|
|