src/modules/uac/replace.c
9fd7fc4e
 /*
  * Copyright (C) 2005 Voice Sistem SRL
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
9fd7fc4e
  *
9b64b9f3
  * UAC Kamailio-module is free software; you can redistribute it and/or
9fd7fc4e
  * 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.
  *
9b64b9f3
  * UAC Kamailio-module is distributed in the hope that it will be useful,
9fd7fc4e
  * 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
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
9fd7fc4e
  *
  */
 
7d7ed6ad
 /*!
  * \file
  * \brief Kamailio uac :: header replacement/retrieval functions
  * \ingroup uac
  * Module: \ref uac
  */
9fd7fc4e
 
 #include <ctype.h>
 
cf83221d
 #include "../../core/parser/parse_from.h"
 #include "../../core/mem/mem.h"
 #include "../../core/data_lump.h"
 #include "../../core/route.h"
eb38b3df
 #include "../../modules/tm/h_table.h"
 #include "../../modules/tm/tm_load.h"
bb2c8330
 #include "../rr/api.h"
0f702f6e
 #include "../dialog/dlg_load.h"
 #include "../dialog/dlg_hash.h"
 
9fd7fc4e
 
a14d3a92
 #include "replace.h"
9fd7fc4e
 
2a65cc55
 extern str uac_passwd;
a14d3a92
 extern int restore_mode;
 extern str rr_from_param;
 extern str rr_to_param;
9fd7fc4e
 extern struct tm_binds uac_tmb;
bb2c8330
 extern struct rr_binds uac_rrb;
9fd7fc4e
 
a14d3a92
 extern str restore_from_avp;
 extern str restore_to_avp;
 extern unsigned short restore_from_avp_type;
 extern int_str restore_from_avp_name;
 extern unsigned short restore_to_avp_type;
 extern int_str restore_to_avp_name;
 
aa7dee1a
 struct dlg_binds dlg_api;
79de6121
 static str from_dlgvar[] = {str_init("_uac_fu"), str_init("_uac_funew"), str_init("_uac_fdp"), str_init("_uac_fdpnew")};
 static str to_dlgvar[] = {str_init("_uac_to"), str_init("_uac_tonew"), str_init("_uac_tdp"), str_init("_uac_tdpnew")};
a14d3a92
 
9fd7fc4e
 static char enc_table64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
 		"abcdefghijklmnopqrstuvwxyz0123456789+/";
 
 static int dec_table64[256];
 
a14d3a92
 static void restore_uris_reply(struct cell* t, int type, struct tmcb_params *p);
9fd7fc4e
 
0f702f6e
 static void replace_callback(struct dlg_cell *dlg, int type,
 		struct dlg_cb_params *_params);
 
081af2f3
 #define text3B64_len(_l)   ( ( ((_l)+2)/3 ) << 2 )
9fd7fc4e
 
f48da229
 /* The reply, were the From-Line was replaced. */
9fd7fc4e
 
351fc577
 void init_from_replacer(void)
9fd7fc4e
 {
 	int i;
 
 	for( i=0 ; i<256 ; i++)
 		dec_table64[i] = -1;
 	for ( i=0 ; i<64; i++)
 		dec_table64[(unsigned char)enc_table64[i]] = i;
 	}
 
 
a14d3a92
 static inline int encode_uri( str *src, str *dst )
9fd7fc4e
 {
 	static char buf[text3B64_len(MAX_URI_SIZE)];
 	int  idx;
 	int  left;
 	int  block;
 	int  i,r;
 	char *p;
 
 	dst->len = text3B64_len( src->len );
 	dst->s = buf;
 	if (dst->len>text3B64_len(MAX_URI_SIZE))
 	{
43f9ea0d
 		LM_ERR("uri too long\n");
9fd7fc4e
 		return -1;
 	}
 
 	for ( idx=0, p=buf ; idx<src->len ; idx+=3)
 	{
bb2c8330
 		left = src->len - idx -1 ;
9fd7fc4e
 		left = (left>1? 2 : left);
 
 		/* Collect 1 to 3 bytes to encode */
 		block = 0;
 		for ( i=0,r= 16 ; i<=left ; i++,r-=8 )
 		{
 			block += ((unsigned char)src->s[idx+i]) << r;
 		}
 
 		/* Encode into 2-4 chars appending '=' if not enough data left.*/
 		*(p++) = enc_table64[(block >> 18) & 0x3f];
 		*(p++) = enc_table64[(block >> 12) & 0x3f];
 		*(p++) = left > 0 ? enc_table64[(block >> 6) & 0x3f] : '-';
 		*(p++) = left > 1 ? enc_table64[block & 0x3f] : '-';
 	}
bb2c8330
 
9fd7fc4e
 	return 0;
 }
 
 
a14d3a92
 static inline int decode_uri( str *src , str *dst)
9fd7fc4e
 {
 	static char buf[MAX_URI_SIZE];
 	int block;
 	int n;
 	int idx;
 	int end;
 	int i,j;
a5af9ecc
 	signed char c;
9fd7fc4e
 
d956f397
 	/* sanity checks */
 	if (!src) {
 		LM_ERR("NULL src\n");
 		return -1;
 	}
 
 	if (!dst) {
 		LM_ERR("NULL dst\n");
 		return -1;
 	}
 
 	if (!src->s || src->len == 0) {
 		LM_ERR("empty src\n");
 		return -1;
 	}
 
9fd7fc4e
 	/* Count '-' at end and disregard them */
 	for( n=0,i=src->len-1; src->s[i]=='-'; i--)
 		n++;
 
 	dst->len = ((src->len * 6) >> 3) - n;
 	dst->s = buf;
 	if (dst->len>MAX_URI_SIZE)
 	{
43f9ea0d
 		LM_ERR("uri too long\n");
9fd7fc4e
 		return -1;
 	}
 
 	end = src->len - n;
 	for ( i=0,idx=0 ; i<end ; idx+=3 )
 	{
 		/* Assemble three bytes into an int from four "valid" characters */
 		block = 0;
 		for ( j=0; j<4 && i<end ; j++)
 		{
 			c = dec_table64[(unsigned char)src->s[i++]];
 			if ( c<0 )
 			{
43f9ea0d
 				LM_ERR("invalid base64 string\"%.*s\"\n",src->len,src->s);
9fd7fc4e
 				return -1;
 			}
 			block += c << (18 - 6*j);
 		}
 
 		/* Add the bytes */
 		for ( j=0,n=16 ; j<3 && idx+j< dst->len; j++,n-=8 )
 			buf[idx+j] = (char) ((block >> n) & 0xff);
 	}
 
 	return 0;
 }
 
 
a14d3a92
 static inline struct lump* get_display_anchor(struct sip_msg *msg,
6a362f03
 		struct hdr_field *hdr, struct to_body *body, str *dsp)
2a65cc55
 {
 	struct lump* l;
 	char *p1;
 	char *p2;
 
 	/* is URI quoted or not? */
a14d3a92
 	p1 = hdr->name.s + hdr->name.len;
 	for( p2=body->uri.s-1 ; p2>=p1 && *p2!='<' ; p2--);
2a65cc55
 
 	if (*p2=='<') {
 		/* is quoted */
 		l = anchor_lump( msg, p2 - msg->buf, 0, 0);
 		if (l==0) {
43f9ea0d
 			LM_ERR("unable to build lump anchor\n");
2a65cc55
 			return 0;
 		}
 		dsp->s[dsp->len++] = ' ';
 		return l;
 	}
 
 	/* not quoted - more complicated....must place the closing bracket */
a14d3a92
 	l = anchor_lump( msg, (body->uri.s+body->uri.len) - msg->buf, 0, 0);
2a65cc55
 	if (l==0) {
43f9ea0d
 		LM_ERR("unable to build lump anchor\n");
2a65cc55
 		return 0;
 	}
 	p1 = (char*)pkg_malloc(1);
 	if (p1==0) {
9a132d30
 		PKG_MEM_ERROR;
2a65cc55
 		return 0;
 	}
 	*p1 = '>';
 	if (insert_new_lump_after( l, p1, 1, 0)==0) {
43f9ea0d
 		LM_ERR("insert lump failed\n");
2a65cc55
 		pkg_free(p1);
 		return 0;
 	}
 	/* build anchor for display */
a14d3a92
 	l = anchor_lump( msg, body->uri.s - msg->buf, 0, 0);
2a65cc55
 	if (l==0) {
43f9ea0d
 		LM_ERR("unable to build lump anchor\n");
2a65cc55
 		return 0;
 	}
 	dsp->s[dsp->len++] = ' ';
 	dsp->s[dsp->len++] = '<';
 	return l;
 }
 
 
bb2c8330
 /*
a14d3a92
  * replace uri and/or display name in FROM / TO header
9fd7fc4e
  */
a14d3a92
 int replace_uri( struct sip_msg *msg, str *display, str *uri,
6a362f03
 	struct hdr_field *hdr, str *rr_param, str* restore_avp, int check_from)
9fd7fc4e
 {
bb2c8330
 	static char buf_s[MAX_URI_SIZE];
a14d3a92
 	struct to_body *body;
9fd7fc4e
 	struct lump* l;
a14d3a92
 	struct cell *Trans;
9fd7fc4e
 	str replace;
 	char *p;
 	str param;
bb2c8330
 	str buf;
0f702f6e
 	unsigned int uac_flag;
bb2c8330
 	int i;
a14d3a92
 	int_str avp_value;
0f702f6e
 	struct dlg_cell* dlg = 0;
 	str * dlgvar_names;
79de6121
 	str display_tmp;
 	str undefined_display = str_init("");
0f702f6e
 
 	uac_flag = (hdr==msg->from)?FL_USE_UAC_FROM:FL_USE_UAC_TO;
d3a0a8b1
 	if(get_route_type()==REQUEST_ROUTE) {
 		if(msg->msg_flags & uac_flag)
 		{
88612ea4
 			LM_ERR("called uac_replace_%s() multiple times on the message\n",
 					(hdr==msg->from)?"from":"to");
d3a0a8b1
 			return -1;
 		}
0f702f6e
 	}
bb2c8330
 
a14d3a92
 	/* consistency check! in AUTO mode, do NOT allow URI changing
0089cf90
 	 * in sequential request */
a14d3a92
 	if (restore_mode==UAC_AUTO_RESTORE && uri && uri->len) {
0089cf90
 		if ( msg->to==0 && (parse_headers(msg,HDR_TO_F,0)!=0 || msg->to==0) ) {
43f9ea0d
 			LM_ERR("failed to parse TO hdr\n");
0089cf90
 			goto error;
 		}
 		if (get_to(msg)->tag_value.len!=0) {
43f9ea0d
 			LM_ERR("decline FROM replacing in sequential request in auto mode (has TO tag)\n");
0089cf90
 			goto error;
 		}
bb2c8330
 	}
9fd7fc4e
 
a14d3a92
 	body = (struct to_body*)hdr->parsed;
 
 	if(restore_avp->s) {
 		/* backup data in avp (if avp is set) */
 		/* catch whitespace characters after uri */
 		for( p = body->uri.s+body->uri.len, i=0; isspace(p[i]) ; i++ );
 		/* if ">" present after uri (and whitespace), catch it */
 		avp_value.s.len =  p - body->body.s + ((p[i]=='>') ? (i+1) : 0) ;
 		avp_value.s.s=body->body.s;
 
00571fd1
 		LM_DBG("value to store is is '%.*s' and len is '%d'\n",
 				avp_value.s.len, avp_value.s.s,avp_value.s.len);
a14d3a92
 
 		if(check_from) {
00571fd1
 			LM_DBG("Storing in FROM-AVP (for use in reply): '%.*s' with len '%d'\n",
 					avp_value.s.len, avp_value.s.s,avp_value.s.len);
a14d3a92
 			add_avp(restore_from_avp_type, restore_from_avp_name, avp_value);
 		} else {
00571fd1
 			LM_DBG("Storing in TO-AVP (for use in reply): '%.*s' with len '%d'\n",
 					avp_value.s.len, avp_value.s.s,avp_value.s.len);
a14d3a92
 			add_avp(restore_to_avp_type, restore_to_avp_name, avp_value);
 		}
9fd7fc4e
 	}
 
 	/* first deal with display name */
a14d3a92
 	if (display)
9fd7fc4e
 	{
 		/* must be replaced/ removed */
2a65cc55
 		l = 0;
f7291e91
 		/* first remove the existing display */
a14d3a92
 		if ( body->display.len)
9fd7fc4e
 		{
00571fd1
 			LM_DBG("removing display [%.*s]\n",
 					body->display.len, body->display.s);
2a65cc55
 			/* build del lump */
00571fd1
 			l = del_lump(msg, body->display.s-msg->buf, body->display.len, 0);
2a65cc55
 			if (l==0)
 			{
43f9ea0d
 				LM_ERR("display del lump failed\n");
2a65cc55
 				goto error;
 			}
9fd7fc4e
 		}
 		/* some new display to set? */
a14d3a92
 		if (display->len)
9fd7fc4e
 		{
951570df
 			LM_DBG("adding new display [%.*s]\n", display->len, display->s);
bb6961e9
 			/* add the new display exactly over the deleted one */
a14d3a92
 			buf.s = pkg_malloc( display->len + 2 );
2a65cc55
 			if (buf.s==0)
9fd7fc4e
 			{
9a132d30
 				PKG_MEM_ERROR;
9fd7fc4e
 				goto error;
 			}
6a362f03
 			memcpy( buf.s, display->s, display->len);
 			buf.len =  display->len;
 			if (l==0 && (l=get_display_anchor(msg,hdr,body,&buf))==0)
2a65cc55
 			{
43f9ea0d
 				LM_ERR("failed to insert anchor\n");
79de6121
 				pkg_free(buf.s);
2a65cc55
 				goto error;
 			}
 			if (insert_new_lump_after( l, buf.s, buf.len, 0)==0)
9fd7fc4e
 			{
43f9ea0d
 				LM_ERR("insert new display lump failed\n");
2a65cc55
 				pkg_free(buf.s);
9fd7fc4e
 				goto error;
 			}
 		}
 	}
 
 	/* now handle the URI */
a14d3a92
 	if (uri==0 || uri->len==0 )
d9ec5530
 		/* do not touch URI part */
 		return 0;
 
a14d3a92
 	LM_DBG("uri to replace [%.*s]\n",body->uri.len, body->uri.s);
 	LM_DBG("replacement uri is [%.*s]\n",uri->len, uri->s);
9fd7fc4e
 
 	/* build del/add lumps */
a14d3a92
 	if ((l=del_lump( msg, body->uri.s-msg->buf, body->uri.len, 0))==0)
9fd7fc4e
 	{
43f9ea0d
 		LM_ERR("del lump failed\n");
9fd7fc4e
 		goto error;
 	}
a14d3a92
 	p = pkg_malloc( uri->len);
9fd7fc4e
 	if (p==0)
 	{
9a132d30
 		PKG_MEM_ERROR;
9fd7fc4e
 		goto error;
 	}
a14d3a92
 	memcpy( p, uri->s, uri->len);
 	if (insert_new_lump_after( l, p, uri->len, 0)==0)
9fd7fc4e
 	{
43f9ea0d
 		LM_ERR("insert new lump failed\n");
9fd7fc4e
 		pkg_free(p);
 		goto error;
 	}
 
a14d3a92
 	if (restore_mode==UAC_NO_RESTORE)
9fd7fc4e
 		return 0;
 
0f702f6e
 	/* trying to get dialog */
 	if (dlg_api.get_dlg) {
 		dlg = dlg_api.get_dlg(msg);
 	}
 
 	if (dlg) {
 		dlgvar_names = (uac_flag==FL_USE_UAC_FROM)?from_dlgvar:to_dlgvar;
 		if(dlg_api.get_dlg_var(dlg, &dlgvar_names[0])) {
 
 			LM_INFO("Already called uac_replace for this dialog\n");
 			/* delete the from_new dlg var */
 
 			if (dlg_api.set_dlg_var(dlg, &dlgvar_names[1], 0) < 0) {
 				LM_ERR("cannot store new uri value\n");
 				dlg_api.release_dlg(dlg);
 				goto error;
 			}
 			LM_INFO("Deleted <%.*s> var in dialog\n",
 					dlgvar_names[1].len, dlgvar_names[1].s);
 		}
 		else {
 			/* the first time uac_replace is called for this dialog */
79de6121
 			/* store old URI value */
0f702f6e
 			if (dlg_api.set_dlg_var(dlg, &dlgvar_names[0], &body->uri) < 0) {
 				LM_ERR("cannot store value\n");
 				dlg_api.release_dlg(dlg);
 				goto error;
 			}
 			LM_DBG("Stored <%.*s> var in dialog with value %.*s\n",
00571fd1
 					dlgvar_names[0].len, dlgvar_names[0].s,
 					body->uri.len, body->uri.s);
0f702f6e
 
00571fd1
 			if (dlg_api.register_dlgcb(dlg,
 						DLGCB_REQ_WITHIN|DLGCB_CONFIRMED|DLGCB_TERMINATED,
 						(void*)(unsigned long)replace_callback,
 						(void*)(unsigned long)uac_flag, 0) != 0) {
0f702f6e
 				LM_ERR("cannot register callback\n");
 				dlg_api.release_dlg(dlg);
 				goto error;
 			}
 		}
79de6121
 		/* store new URI value */
0f702f6e
 		if (dlg_api.set_dlg_var(dlg, &dlgvar_names[1], uri) < 0) {
 			LM_ERR("cannot store new uri value\n");
 			dlg_api.release_dlg(dlg);
bb2c8330
 			goto error;
 		}
0f702f6e
 		LM_DBG("Stored <%.*s> var in dialog with value %.*s\n",
 				dlgvar_names[1].len, dlgvar_names[1].s, uri->len, uri->s);
79de6121
 
 		/* store the display name as well */
 		if (body->display.s && body->display.len >0) {
 			display_tmp = body->display;
 		} else {
 			display_tmp = undefined_display;
 		}
 		if (dlg_api.set_dlg_var(dlg, &dlgvar_names[2], &display_tmp) < 0) {
 			LM_ERR("cannot store display value\n");
 			dlg_api.release_dlg(dlg);
 			goto error;
 		}
 		LM_DBG("Stored <%.*s> var in dialog with value %.*s\n",
 				dlgvar_names[1].len, dlgvar_names[2].s, display_tmp.len, display_tmp.s);
 
 		if (display && display->s && display->len > 0) {
 			display_tmp.s=display->s;
 			display_tmp.len=display->len;
 		} else {
 			display_tmp = undefined_display;
 		}
 		if (dlg_api.set_dlg_var(dlg, &dlgvar_names[3], &display_tmp) < 0) {
 			LM_ERR("cannot store new display value\n");
 			dlg_api.release_dlg(dlg);
 			goto error;
 		}
 		LM_DBG("Stored <%.*s> var in dialog with value %.*s\n",
 				dlgvar_names[1].len, dlgvar_names[3].s, display_tmp.len, display_tmp.s);
 
0f702f6e
 		dlg_api.release_dlg(dlg);
bb2c8330
 	} else {
0f702f6e
 		if (!uac_rrb.append_fromtag) {
 			LM_ERR("'append_fromtag' RR param is not enabled!"
 			" - required by AUTO restore mode\n");
bb2c8330
 			goto error;
 		}
 
0f702f6e
 		/* build RR parameter */
 		buf.s = buf_s;
 		if ( body->uri.len>uri->len ) {
 			if (body->uri.len>MAX_URI_SIZE) {
6a362f03
 				LM_ERR("old %.*s uri too long\n",hdr->name.len,hdr->name.s);
0f702f6e
 				goto error;
 			}
 			memcpy( buf.s, body->uri.s, body->uri.len);
 			for( i=0 ; i<uri->len ; i++ )
 				buf.s[i] ^=uri->s[i];
 			buf.len = body->uri.len;
 		} else {
 			if (uri->len>MAX_URI_SIZE) {
 				LM_ERR("new %.*s uri too long\n",hdr->name.len,hdr->name.s);
 				goto error;
 			}
 			memcpy( buf.s, uri->s, uri->len);
 			for( i=0 ; i<body->uri.len ; i++ )
 				buf.s[i] ^=body->uri.s[i];
 			buf.len = uri->len;
 		}
2a65cc55
 
0f702f6e
 		/* encrypt parameter ;) */
 		if (uac_passwd.len)
 			for( i=0 ; i<buf.len ; i++)
 				buf.s[i] ^= uac_passwd.s[i%uac_passwd.len];
bb2c8330
 
0f702f6e
 		/* encode the param */
 		if (encode_uri( &buf , &replace)<0 )
 		{
 			LM_ERR("failed to encode uris\n");
 			goto error;
 		}
 		LM_DBG("encode is=<%.*s> len=%d\n",replace.len,replace.s,replace.len);
bb2c8330
 
0f702f6e
 		/* add RR parameter */
 		param.len = 1+rr_param->len+1+replace.len;
 		param.s = (char*)pkg_malloc(param.len);
 		if (param.s==0)
 		{
9a132d30
 			PKG_MEM_ERROR;
0f702f6e
 			goto error;
 		}
 		p = param.s;
 		*(p++) = ';';
 		memcpy( p, rr_param->s, rr_param->len);
 		p += rr_param->len;
 		*(p++) = '=';
 		memcpy( p, replace.s, replace.len);
 
 		if (uac_rrb.add_rr_param( msg, &param)!=0)
 		{
 			LM_ERR("add_RR_param failed\n");
 			goto error1;
 		}
 		pkg_free(param.s);
9fd7fc4e
 	}
 
0f702f6e
 	if ((msg->msg_flags&(FL_USE_UAC_FROM|FL_USE_UAC_TO))==0) {
 		/* add TM callback to restore the FROM/TO hdr in reply */
 		if (uac_tmb.register_tmcb( msg, 0, TMCB_RESPONSE_IN,
 			restore_uris_reply,0,0)!=1) {
 			LM_ERR("failed to install TM callback\n");
 			goto error;
 		}
 	}
 	msg->msg_flags |= uac_flag;
a14d3a92
 
0f702f6e
 	if ( (Trans=uac_tmb.t_gett())!=NULL && Trans!=T_UNDEFINED &&
 		Trans->uas.request) {
 		Trans->uas.request->msg_flags |= uac_flag;
bb2c8330
 	}
 
9fd7fc4e
 	return 0;
bb2c8330
 error1:
 	pkg_free(param.s);
9fd7fc4e
 error:
 	return -1;
 }
 
 
 /*
bb2c8330
  * return  0 - restored
  *        -1 - not restored or error
9fd7fc4e
  */
00571fd1
 int restore_uri( struct sip_msg *msg, str *rr_param, str* restore_avp,
 		int check_from)
9fd7fc4e
 {
 	struct lump* l;
bb2c8330
 	str param_val;
f6a5a285
 	str add_to_rr = {0, 0};
a14d3a92
 	struct to_body* old_body;
49dd3315
 	str old_uri = {0, 0};
 	str new_uri = {0, 0};
9fd7fc4e
 	char *p;
bb2c8330
 	int i;
a14d3a92
 	int_str avp_value;
 	int flag;
0666abf8
 	int bsize;
9fd7fc4e
 
a14d3a92
 	/* we should process only sequential request, but since we are looking
bb2c8330
 	 * for Route param, the test is not really required -bogdan */
 
985f4318
 	LM_DBG("getting '%.*s' Route param\n",
a14d3a92
 		rr_param->len,rr_param->s);
bb2c8330
 	/* is there something to restore ? */
a14d3a92
 	if (uac_rrb.get_route_param( msg, rr_param, &param_val)!=0) {
985f4318
 		LM_DBG("route param '%.*s' not found\n",
a14d3a92
 			rr_param->len,rr_param->s);
bb2c8330
 		goto failed;
 	}
985f4318
 	LM_DBG("route param is '%.*s' (len=%d)\n",
bb2c8330
 		param_val.len,param_val.s,param_val.len);
 
 	/* decode the parameter val to a URI */
a14d3a92
 	if (decode_uri( &param_val, &new_uri)<0 ) {
43f9ea0d
 		LM_ERR("failed to decode uri\n");
bb2c8330
 		goto failed;
 	}
 
0666abf8
 	bsize = 3+rr_param->len+param_val.len;
 	add_to_rr.s = pkg_malloc(bsize);
f6a5a285
 	if ( add_to_rr.s==0 ) {
 		add_to_rr.len = 0;
9a132d30
 		PKG_MEM_ERROR;
f6a5a285
 		goto failed;
 	}
0666abf8
 	add_to_rr.len = snprintf(add_to_rr.s, bsize, ";%.*s=%.*s",
00571fd1
 			rr_param->len, rr_param->s, param_val.len, param_val.s);
f6a5a285
 
0666abf8
 	if(add_to_rr.len<0 || add_to_rr.len>=bsize) {
 		LM_ERR("printing rr param failed\n");
 		goto failed;
 	}
f6a5a285
 	if ( uac_rrb.add_rr_param(msg, &add_to_rr)!=0 ) {
27026291
 		LM_ERR("add rr param failed\n");
f6a5a285
 		goto failed;
 	}
 	pkg_free(add_to_rr.s);
6a362f03
 	add_to_rr.s = NULL;
f6a5a285
 
49dd3315
 	/* decrypt parameter */
 	if (uac_passwd.len) {
2a65cc55
 		for( i=0 ; i<new_uri.len ; i++)
 			new_uri.s[i] ^= uac_passwd.s[i%uac_passwd.len];
49dd3315
 	}
2a65cc55
 
bb2c8330
 	/* check the request direction */
49dd3315
 	if ( (check_from && uac_rrb.is_direction(msg, RR_FLOW_UPSTREAM)==0)
 			|| (!check_from && uac_rrb.is_direction(msg, RR_FLOW_DOWNSTREAM)==0)
 				) {
bb2c8330
 		/* replace the TO URI */
 		if ( msg->to==0 && (parse_headers(msg,HDR_TO_F,0)!=0 || msg->to==0) ) {
43f9ea0d
 			LM_ERR("failed to parse TO hdr\n");
9fd7fc4e
 			goto failed;
 		}
a14d3a92
 		old_body = (struct to_body*) msg->to->parsed;
 		flag = FL_USE_UAC_TO;
7a4c490a
 		LM_DBG("replacing in To header\n");
9fd7fc4e
 	} else {
bb2c8330
 		/* replace the FROM URI */
34967758
 		if ( parse_from_header(msg)<0 ) {
43f9ea0d
 			LM_ERR("failed to find/parse FROM hdr\n");
9fd7fc4e
 			goto failed;
 		}
a14d3a92
 		old_body = (struct to_body*) msg->from->parsed;
 		flag = FL_USE_UAC_FROM;
7a4c490a
 		LM_DBG("replacing in From header\n");
9fd7fc4e
 	}
 
a14d3a92
 	if(restore_avp->s) {
 		/* backup data to avp (if avp is set) */
 
 		/* catch whitespace characters after uri */
 		for( p = old_body->uri.s+old_body->uri.len, i=0; isspace(p[i]) ; i++ );
 		/* if ">" present after uri (and whitespace), catch it */
 		avp_value.s.len =  p - old_body->body.s + ((p[i]=='>') ? (i+1) : 0) ;
 		avp_value.s.s=old_body->body.s;
 
 		if(flag==FL_USE_UAC_FROM) {
00571fd1
 			LM_DBG("Storing in FROM-AVP (for use in reply): '%.*s' with len '%d'\n",
 					avp_value.s.len, avp_value.s.s,avp_value.s.len);
a14d3a92
 			add_avp(restore_from_avp_type, restore_from_avp_name, avp_value);
 		} else {
00571fd1
 			LM_DBG("Storing in TO-AVP (for use in reply): '%.*s' with len '%d'\n",
 					avp_value.s.len, avp_value.s.s,avp_value.s.len);
a14d3a92
 			add_avp(restore_to_avp_type, restore_to_avp_name, avp_value);
 		}
 	}
 
 	old_uri= old_body->uri;
 
bb2c8330
 	/* get new uri */
 	if ( new_uri.len<old_uri.len ) {
6feeb886
 		LM_ERR("new URI [%.*s] shorter than old URI [%.*s]\n",
 				new_uri.len, new_uri.s, old_uri.len, old_uri.s);
9fd7fc4e
 		goto failed;
 	}
e1d1c774
 	for( i=0 ; i<old_uri.len ; i++ ) {
bb2c8330
 		new_uri.s[i] ^= old_uri.s[i];
e1d1c774
 		if (new_uri.s[i] == 0) {
 			new_uri.len = i;
 			break;
bb2c8330
 		}
9fd7fc4e
 	}
e1d1c774
 	if (new_uri.len==0) {
 		LM_ERR("new URI got 0 len\n");
 		goto failed;
 	}
9fd7fc4e
 
49dd3315
 	/* check if new uri has valid characters */
 	for(i=0; i<new_uri.len; i++) {
 		if(!isprint(new_uri.s[i])) {
 			LM_WARN("invalid char found in the new uri at pos %d (%c) [%.*s]\n",
 					i, new_uri.s[i], new_uri.len, new_uri.s);
 			LM_WARN("this can happen when URI values are altered by end points"
 					" - skipping the update\n");
 			goto failed;
 		}
 	}
985f4318
 	LM_DBG("decoded uris are: new=[%.*s] old=[%.*s]\n",
bb2c8330
 		new_uri.len, new_uri.s, old_uri.len, old_uri.s);
 
 	/* duplicate the decoded value */
 	p = pkg_malloc( new_uri.len);
 	if (p==0) {
9a132d30
 		PKG_MEM_ERROR;
9fd7fc4e
 		goto failed;
 	}
bb2c8330
 	memcpy( p, new_uri.s, new_uri.len);
 	new_uri.s = p;
9fd7fc4e
 
bb2c8330
 	/* build del/add lumps */
 	l = del_lump( msg, old_uri.s-msg->buf, old_uri.len, 0);
 	if (l==0) {
43f9ea0d
 		LM_ERR("del lump failed\n");
bb2c8330
 		goto failed1;
 	}
 
 	if (insert_new_lump_after( l, new_uri.s, new_uri.len, 0)==0) {
43f9ea0d
 		LM_ERR("insert new lump failed\n");
bb2c8330
 		goto failed1;
9fd7fc4e
 	}
 
a14d3a92
 	msg->msg_flags |= flag;
 
9fd7fc4e
 	return 0;
bb2c8330
 failed1:
 	pkg_free(new_uri.s);
9fd7fc4e
 failed:
f6a5a285
 	if(add_to_rr.s)
 		pkg_free(add_to_rr.s);
9fd7fc4e
 	return -1;
 }
 
 
 
bb2c8330
 /************************** RRCB functions ******************************/
9fd7fc4e
 
bb2c8330
 void rr_checker(struct sip_msg *msg, str *r_param, void *cb_param)
9fd7fc4e
 {
bb2c8330
 	/* check if the request contains the route param */
6a362f03
 	if ( (restore_uri( msg, &rr_from_param, &restore_from_avp, 1/*from*/) +
 			restore_uri( msg, &rr_to_param, &restore_to_avp, 0/*to*/) )!= -2 ) {
bb2c8330
 		/* restore in req performed -> replace in reply */
6a362f03
 		/* in callback we need TO/FROM to be parsed- it's already done
a14d3a92
 		 * by restore_from_to() function */
bb2c8330
 		if ( uac_tmb.register_tmcb( msg, 0, TMCB_RESPONSE_IN,
a14d3a92
 		restore_uris_reply, 0, 0)!=1 ) {
43f9ea0d
 			LM_ERR("failed to install TM callback\n");
9fd7fc4e
 				return;
 		}
 	}
 }
 
 
bb2c8330
 /************************** TMCB functions ******************************/
 
a14d3a92
 /* replace the entire HDR with the original request */
 static inline int restore_uri_reply(struct sip_msg *rpl,
6a362f03
 		struct hdr_field *rpl_hdr, struct hdr_field *req_hdr, str* stored_value)
a14d3a92
 
9fd7fc4e
 {
 	struct lump* l;
a14d3a92
 	struct to_body *body;
bb2c8330
 	str new_val;
a14d3a92
 	int len;
 	char *p;
9fd7fc4e
 
a14d3a92
 	if(stored_value->len) {
a4432c32
 		LM_DBG("stored AVP value is '%.*s'with len '%d'\n",
 				stored_value->len, stored_value->s, stored_value->len);
a14d3a92
 		len=stored_value->len;
 		p = stored_value->s;
 	} else {
 		/* duplicate the new hdr value */
 		body = (struct to_body*)req_hdr->parsed;
 		/* catch whitespace characters after uri */
 		for( p = body->uri.s+body->uri.len, len=0; isspace(p[len]) ; len++ );
 		/* if ">" present after uri (and whitespace), catch it */
 		len =  p - body->body.s + ((p[len]=='>') ? (len+1) : 0) ;
 		p = body->body.s;
bb2c8330
 	}
9fd7fc4e
 
a14d3a92
 	new_val.s = pkg_malloc( len );
 	if (new_val.s==0) {
9a132d30
 		PKG_MEM_ERROR;
a14d3a92
 		return -1;
9fd7fc4e
 	}
a14d3a92
 	memcpy( new_val.s, p, len);
 	new_val.len = len;
9fd7fc4e
 
a14d3a92
 	body = (struct to_body*)rpl_hdr->parsed;
 
 	/* catch whitespace characters after uri */
 	for( p = body->uri.s+body->uri.len, len=0; isspace(p[len]) ; len++ );
 	/* if ">" present after uri (and whitespace), catch it */
 	len =  p - body->body.s + ((p[len]=='>') ? (len+1) : 0) ;
 	LM_DBG("removing <%.*s>\n", len,body->body.s);
 	l = del_lump( rpl, body->body.s-rpl->buf, len, 0);
bb2c8330
 	if (l==0) {
43f9ea0d
 		LM_ERR("del lump failed\n");
a14d3a92
 		pkg_free( new_val.s );
 		return -1;
9fd7fc4e
 	}
bb2c8330
 
985f4318
 	LM_DBG("inserting <%.*s>\n",
bb2c8330
 			new_val.len,new_val.s);
 	if (insert_new_lump_after( l, new_val.s, new_val.len, 0)==0) {
43f9ea0d
 		LM_ERR("insert new lump failed\n");
a14d3a92
 		pkg_free( new_val.s );
 		l->len = 0;
 		return -1;
9fd7fc4e
 	}
a14d3a92
 
 	return 0;
bb2c8330
 }
 
 
 
a14d3a92
 /* replace the entire from HDR with the original FROM request */
 void restore_uris_reply(struct cell* t, int type, struct tmcb_params *p)
bb2c8330
 {
 	struct sip_msg *req;
 	struct sip_msg *rpl;
a14d3a92
 	int_str avp_value;
bb2c8330
 
 	if ( !t || !t->uas.request || !p->rpl )
 		return;
 
 	req = t->uas.request;
 	rpl = p->rpl;
 
a14d3a92
 	if (req->msg_flags & FL_USE_UAC_FROM ) {
9fd7fc4e
 
a14d3a92
 		/* parse FROM in reply */
 		if (parse_from_header( rpl )<0 ) {
6a362f03
 			LM_ERR("failed to find/parse FROM hdr\n");
a14d3a92
 			return;
 		}
 
a4432c32
 		avp_value.s.len=0;
a14d3a92
 		if(restore_from_avp.s) {
00571fd1
 			search_first_avp(restore_from_avp_type, restore_from_avp_name,
 					&avp_value,0);
a14d3a92
 		}
 
 		if (restore_uri_reply( rpl, rpl->from, req->from, &avp_value.s)) {
 			LM_ERR("failed to restore FROM\n");
 		}
bb2c8330
 
 	}
a14d3a92
 	if (req->msg_flags & FL_USE_UAC_TO ) {
 
 		/* parse TO in reply */
6a362f03
 		if ( rpl->to==0 && (parse_headers(rpl,HDR_TO_F,0)!=0 || rpl->to==0) ) {
a14d3a92
 			LM_ERR("failed to parse TO hdr\n");
 			return;
 		}
 
a4432c32
 		avp_value.s.len=0;
a14d3a92
 		if(restore_to_avp.s) {
00571fd1
 			search_first_avp(restore_to_avp_type, restore_to_avp_name,
 					&avp_value, 0);
a14d3a92
 		}
 
 		if (restore_uri_reply( rpl, rpl->to, req->to, &avp_value.s)) {
6a362f03
 			LM_ERR("failed to restore TO\n");
a14d3a92
 		}
 
bb2c8330
 	}
9fd7fc4e
 }
 
0f702f6e
 /************************** DIALOG CB function ******************************/
 
 static void replace_callback(struct dlg_cell *dlg, int type,
 		struct dlg_cb_params *_params)
 {
 	struct lump* l;
 	struct sip_msg *msg;
79de6121
 	struct hdr_field *hdr;
 	struct to_body *body;
0f702f6e
 	str old_uri;
 	str* new_uri;
79de6121
 	str* new_display;
 	str buf;
0f702f6e
 	char *p;
 	unsigned int uac_flag;
 	int dlgvar_index = 0;
79de6121
 	int dlgvar_dpindex = 0;
0f702f6e
 	str* dlgvar_names;
 
 	if (!dlg || !_params || _params->direction == DLG_DIR_NONE || !_params->req)
 		return;
 
 	uac_flag = (unsigned int)(unsigned long)*(_params->param);
 	msg = _params->req;
 	if(msg->msg_flags & uac_flag)
 		return;
 
 	dlgvar_names = (uac_flag==FL_USE_UAC_FROM)?from_dlgvar:to_dlgvar;
 
 	/* check the request direction */
00571fd1
 	if ( ((uac_flag == FL_USE_UAC_TO) && _params->direction == DLG_DIR_DOWNSTREAM)
 			|| ((uac_flag != FL_USE_UAC_TO)
 				&& _params->direction == DLG_DIR_UPSTREAM) ) {
0f702f6e
 		/* replace the TO URI */
 		if ( msg->to==0 && (parse_headers(msg,HDR_TO_F,0)!=0 || msg->to==0) ) {
 			LM_ERR("failed to parse TO hdr\n");
 			return;
 		}
 		old_uri = ((struct to_body*)msg->to->parsed)->uri;
79de6121
 		hdr = (struct hdr_field*)msg->to;
 		body = ((struct to_body*)msg->to->parsed);
0f702f6e
 	} else {
 		/* replace the FROM URI */
 		if ( parse_from_header(msg)<0 ) {
 			LM_ERR("failed to find/parse FROM hdr\n");
 			return;
 		}
 		old_uri = ((struct to_body*)msg->from->parsed)->uri;
79de6121
 		hdr = (struct hdr_field*)msg->from;
 		body = (struct to_body*)msg->from->parsed;
0f702f6e
 	}
 
 	if (_params->direction == DLG_DIR_DOWNSTREAM) {
 		dlgvar_index = 1;
79de6121
 		dlgvar_dpindex = 3;
0f702f6e
 		LM_DBG("DOWNSTREAM direction detected - replacing uri"
 				" with the new uri\n");
 	} else {
79de6121
 		dlgvar_index = 0;
 		dlgvar_dpindex = 2;
0f702f6e
 		LM_DBG("UPSTREAM direction detected - replacing uri"
 				" with the original uri\n");
 	}
 
 	if ((new_uri = dlg_api.get_dlg_var(dlg, &dlgvar_names[dlgvar_index])) == 0) {
 		LM_DBG("<%.*s> param not found\n", dlgvar_names[dlgvar_index].len,
 				dlgvar_names[dlgvar_index].s);
 		return;
 	}
79de6121
 	if ((new_display = dlg_api.get_dlg_var(dlg, &dlgvar_names[dlgvar_dpindex])) == 0) {
 		LM_DBG("<%.*s> param not found\n", dlgvar_names[dlgvar_dpindex].len,
 				dlgvar_names[dlgvar_dpindex].s);
 		return;
 	}
0f702f6e
 
79de6121
 	LM_DBG("Replace [%.*s %.*s] with [%.*s %.*s]\n", body->display.len, body->display.s,
 			old_uri.len, old_uri.s, new_display->len, new_display->s,
0f702f6e
 			new_uri->len, new_uri->s);
 
79de6121
 	/* deal with display name */
 	l = 0;
 	/* first remove the existing display */
 	if (body->display.s && body->display.len > 0) {
 		LM_DBG("removing display [%.*s]\n",
 				body->display.len, body->display.s);
 		/* build del lump */
 		l = del_lump(msg, body->display.s-msg->buf, body->display.len, 0);
 		if (l==0) {
 			LM_ERR("display del lump failed\n");
efa6c6a9
 			return;
 		}
79de6121
 	}
 	if (new_display->s && new_display->len > 0) {
 		LM_DBG("inserting display [%.*s]\n",
 				new_display->len, new_display->s);
 		/* add the new display exactly over the deleted one */
 		buf.s = pkg_malloc(new_display->len + 2);
 		if (buf.s==0) {
9a132d30
 			PKG_MEM_ERROR;
efa6c6a9
 			return;
79de6121
 		}
 		memcpy( buf.s, new_display->s, new_display->len);
 		buf.len = new_display->len;
 		if (l==0 && (l=get_display_anchor(msg, hdr, body, &buf)) == 0) {
 			LM_ERR("failed to insert anchor\n");
efa6c6a9
 			goto free1;
79de6121
 		}
 		if (insert_new_lump_after(l, buf.s, buf.len, 0) == 0) {
 			LM_ERR("insert new display lump failed\n");
efa6c6a9
 			goto free1;
79de6121
 		}
 	}
 
efa6c6a9
 	/* uri update - duplicate the decoded value */
 	p = pkg_malloc( new_uri->len);
 	if (!p) {
 		PKG_MEM_ERROR;
 		goto free1;
 	}
 	memcpy( p, new_uri->s, new_uri->len);
 
 	/* build del/add lumps */
 	l = del_lump( msg, old_uri.s-msg->buf, old_uri.len, 0);
 	if (l==0) {
 		LM_ERR("del lump failed\n");
 		goto free2;
 	}
 
 	if (insert_new_lump_after( l, p, new_uri->len, 0)==0) {
 		LM_ERR("insert new lump failed\n");
 		goto free2;
 	}
 
00571fd1
 	/* register tm callback to change replies,
 	 * but only if not registered earlier */
0f702f6e
 	if (!(msg->msg_flags & (FL_USE_UAC_FROM|FL_USE_UAC_TO)) &&
 			uac_tmb.register_tmcb( msg, 0, TMCB_RESPONSE_IN,
 			restore_uris_reply, 0, 0) != 1 ) {
 		LM_ERR("failed to install TM callback\n");
 		return;
 	}
 	msg->msg_flags |= uac_flag;
 
 	return;
 
79de6121
 free2:
0f702f6e
 	pkg_free(p);
efa6c6a9
 
 free1:
 	pkg_free(buf.s);
0f702f6e
 }
aa7dee1a
 
 
 /* helper function to avoid code duplication */
 static inline int uac_load_callback_helper(struct dlg_cell* dialog, unsigned int uac_flag) {
 
 	if( dlg_api.register_dlgcb(dialog, DLGCB_REQ_WITHIN,
 			(void*)(unsigned long)replace_callback, (void*)(unsigned long)uac_flag, 0) != 0) {
 		LM_ERR("can't register create dialog REQ_WITHIN callback\n");
 		return -1;
 	}
 
 	if( dlg_api.register_dlgcb(dialog, DLGCB_CONFIRMED,
 			(void*)(unsigned long)replace_callback, (void*)(unsigned long)uac_flag, 0) != 0) {
 		LM_ERR("can't register create dialog CONFIRM callback\n");
 		return -1;
 	}
 
 	if( dlg_api.register_dlgcb(dialog, DLGCB_TERMINATED,
 			(void*)(unsigned long)replace_callback, (void*)(unsigned long)uac_flag, 0) != 0) {
 		LM_ERR("can't register create dialog TERMINATED callback\n");
 		return -1;
 	}
 	return 0;
 }
 
 
 /* callback for loading a dialog from database */
 static void uac_on_load_callback(struct dlg_cell* dialog, int type, struct dlg_cb_params* params) {
 
 	if(!dialog) {
 		LM_ERR("invalid values\n!");
 		return;
 	}
 
 	/* Note:
 	 * We don't have a way to access the real uac flags from the uac_replace_*
 	 * method call at this point in time anymore. Therefore we just install a
 	 * callback for both FROM and TO replace cases. This might be a bit
 	 * inefficient in cases where only one of the functions is used. But as
 	 * this applies only e.g. to a proxy restart with runnning dialogs, it
 	 * does not matter. The replace_callback function will just not find a
 	 * an entry in the dialog variables table and log an error.
 	 */
 	if(uac_load_callback_helper(dialog, FL_USE_UAC_FROM) != 0) {
 		LM_ERR("can't register create callbacks for UAC FROM\n");
 		return;
 	}
 	if(uac_load_callback_helper(dialog, FL_USE_UAC_TO) != 0) {
 		LM_ERR("can't register create callbacks for UAC TO\n");
 		return;
 	}
 
 	LM_DBG("dialog '%p' loaded and callbacks registered\n", dialog);
 }
 
 
 /* initialization of all necessary callbacks to track a dialog */
 int uac_init_dlg(void) {
 
 	memset(&dlg_api, 0, sizeof(struct dlg_binds));
 
 	if( load_dlg_api(&dlg_api) != 0) {
 		LM_ERR("can't load dialog API\n");
 		return -1;
 	}
 
 	if( dlg_api.register_dlgcb( 0, DLGCB_LOADED, uac_on_load_callback, 0, 0) != 0) {
 		LM_ERR("can't register on load callback\n");
 		return -1;
 	}
 	LM_DBG("loaded dialog API and registered on load callback\n");
 	return 0;
 }