src/modules/tmx/t_var.c
256d0776
 /*
06a6a733
  *
  * Copyright (C) 2008 Elena-Ramona Modroiu (asipto.com)
  *
  * This file is part of kamailio, a free SIP server.
  *
6c780225
  * Kamailio is free software; you can redistribute it and/or modify
06a6a733
  * 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
  *
6c780225
  * Kamailio is distributed in the hope that it will be useful,
06a6a733
  * 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
06a6a733
  */
256d0776
 /*! \file
  * \brief TMX :: var functions
  *
  * \ingroup tm
  * - Module: \ref tm
  */
06a6a733
 
cf83221d
 #include "../../core/mem/mem.h"
 #include "../../core/dset.h"
06a6a733
 
 #include "tmx_mod.h"
 #include "t_var.h"
 
527ba6b4
 struct _pv_tmx_data {
6913d30c
 	unsigned int index;
 	unsigned int label;
0305e277
 	int branch;
527ba6b4
 	struct sip_msg msg;
 	struct sip_msg *tmsgp;
 	char *buf;
 	int buf_size;
 };
 
 static struct _pv_tmx_data _pv_treq;
 static struct _pv_tmx_data _pv_trpl;
4d106ce3
 static struct _pv_tmx_data _pv_tinv;
527ba6b4
 
 void pv_tmx_data_init(void)
 {
 	memset(&_pv_treq, 0, sizeof(struct _pv_tmx_data));
 	memset(&_pv_trpl, 0, sizeof(struct _pv_tmx_data));
4d106ce3
 	memset(&_pv_tinv, 0, sizeof(struct _pv_tmx_data));
527ba6b4
 }
06a6a733
 
 int pv_t_copy_msg(struct sip_msg *src, struct sip_msg *dst)
 {
 	dst->id = src->id;
 	dst->rcv = src->rcv;
 	dst->set_global_address=src->set_global_address;
 	dst->set_global_port=src->set_global_port;
a5891c2e
 	dst->flags = src->flags;
2c3aaa7a
 	dst->fwd_send_flags = src->fwd_send_flags;
 	dst->rpl_send_flags = src->rpl_send_flags;
a5891c2e
 	dst->force_send_socket = src->force_send_socket;
06a6a733
 
 	if (parse_msg(dst->buf, dst->len, dst)!=0)
 	{
 		LM_ERR("parse msg failed\n");
 		return -1;
 	}
 	return 0;
 }
 
 int pv_t_update_req(struct sip_msg *msg)
 {
 	struct cell * t;
 	int branch;
 
 	if(msg==NULL)
 		return 1;
 
 	if(msg!=FAKED_REPLY && msg->first_line.type!=SIP_REPLY)
 		return 1;
 
 	t = _tmx_tmb.t_gett();
 
 	if(t==NULL || t==T_UNDEFINED)
 	{
 		if(msg==FAKED_REPLY)
 			return 1;
 		branch=-1;
 		if (_tmx_tmb.t_check(msg, &branch ) == -1)
 			return 1;
 		t = _tmx_tmb.t_gett();
 		if ((t == 0) || (t == T_UNDEFINED))
 			return 1;
 
 	}
 
 	if(t->uas.request==NULL)
 		return 1;
 
6913d30c
 	if (_pv_treq.label == t->label && _pv_treq.index == t->hash_index)  
06a6a733
 		return 0;
 
 	/* make a copy */
527ba6b4
 	if(_pv_treq.buf==NULL || _pv_treq.buf_size<t->uas.request->len+1)
06a6a733
 	{
527ba6b4
 		if(_pv_treq.buf!=NULL)
 			pkg_free(_pv_treq.buf);
 		if(_pv_treq.tmsgp)
 			free_sip_msg(&_pv_treq.msg);
 		_pv_treq.tmsgp = NULL;
6913d30c
 		_pv_treq.index = 0;
 		_pv_treq.label = 0;
527ba6b4
 		_pv_treq.buf_size = t->uas.request->len+1;
 		_pv_treq.buf = (char*)pkg_malloc(_pv_treq.buf_size*sizeof(char));
 		if(_pv_treq.buf==NULL)
06a6a733
 		{
 			LM_ERR("no more pkg\n");
527ba6b4
 			_pv_treq.buf_size = 0;
06a6a733
 			return -1;
 		}
 	}
527ba6b4
 	if(_pv_treq.tmsgp)
 		free_sip_msg(&_pv_treq.msg);
 	memset(&_pv_treq.msg, 0, sizeof(struct sip_msg));
 	memcpy(_pv_treq.buf, t->uas.request->buf, t->uas.request->len);
 	_pv_treq.buf[t->uas.request->len] = '\0';
 	_pv_treq.msg.len = t->uas.request->len;
 	_pv_treq.msg.buf = _pv_treq.buf;
 	_pv_treq.tmsgp = t->uas.request;
6913d30c
 	_pv_treq.index = t->hash_index;
 	_pv_treq.label = t->label;
527ba6b4
 
 
 	if(pv_t_copy_msg(t->uas.request, &_pv_treq.msg)!=0)
eb34a47f
 	{
527ba6b4
 		pkg_free(_pv_treq.buf);
 		_pv_treq.buf_size = 0;
 		_pv_treq.buf = NULL;
 		_pv_treq.tmsgp = NULL;
6913d30c
 		_pv_treq.index = 0;
 		_pv_treq.label = 0;
eb34a47f
 		return -1;
 	}
06a6a733
 
 	return 0;
 }
 
 int pv_t_update_rpl(struct sip_msg *msg)
 {
 	struct cell * t;
 	int branch;
 	int cancel;
 
 	if(msg==NULL)
 		return 1;
 
 	if(msg==FAKED_REPLY || msg->first_line.type!=SIP_REQUEST)
 		return 1;
 
 	t = _tmx_tmb.t_gett();
 
 	if(t==NULL || t==T_UNDEFINED)
 	{
 		if(_tmx_tmb.t_lookup_request(msg, 0, &cancel)<=0)
 			return 1;
 		t = _tmx_tmb.t_gett();
 
 		if(t==NULL || t==T_UNDEFINED)
 			return 1;
 	}
 	if ( (branch=_tmx_tmb.t_get_picked_branch())<0 )
 		return 1;
 	if(t->uac[branch].reply==NULL || t->uac[branch].reply==FAKED_REPLY)
 		return 1;
 
0305e277
 	if (_pv_trpl.label == t->label && _pv_trpl.index == t->hash_index
 			&& _pv_trpl.branch == branch)
06a6a733
 		return 0;
 
 	/* make a copy */
527ba6b4
 	if(_pv_trpl.buf==NULL || _pv_trpl.buf_size<t->uac[branch].reply->len+1)
06a6a733
 	{
527ba6b4
 		if(_pv_trpl.buf!=NULL)
 			pkg_free(_pv_trpl.buf);
 		if(_pv_trpl.tmsgp)
 			free_sip_msg(&_pv_trpl.msg);
 		_pv_trpl.tmsgp = NULL;
6913d30c
 		_pv_trpl.index = 0;
 		_pv_trpl.label = 0;
0305e277
 		_pv_trpl.branch = 0;
527ba6b4
 		_pv_trpl.buf_size = t->uac[branch].reply->len+1;
 		_pv_trpl.buf = (char*)pkg_malloc(_pv_trpl.buf_size*sizeof(char));
 		if(_pv_trpl.buf==NULL)
06a6a733
 		{
 			LM_ERR("no more pkg\n");
527ba6b4
 			_pv_trpl.buf_size = 0;
06a6a733
 			return -1;
 		}
 	}
527ba6b4
 	if(_pv_trpl.tmsgp)
 		free_sip_msg(&_pv_trpl.msg);
 	memset(&_pv_trpl.msg, 0, sizeof(struct sip_msg));
 	memcpy(_pv_trpl.buf, t->uac[branch].reply->buf, t->uac[branch].reply->len);
 	_pv_trpl.buf[t->uac[branch].reply->len] = '\0';
 	_pv_trpl.msg.len = t->uac[branch].reply->len;
 	_pv_trpl.msg.buf = _pv_trpl.buf;
 	_pv_trpl.tmsgp = t->uac[branch].reply;
6913d30c
 	_pv_trpl.index = t->hash_index;
 	_pv_trpl.label = t->label;
0305e277
 	_pv_trpl.branch = branch;
527ba6b4
 
 	if(pv_t_copy_msg(t->uac[branch].reply, &_pv_trpl.msg)!=0)
eb34a47f
 	{
527ba6b4
 		pkg_free(_pv_trpl.buf);
 		_pv_trpl.buf_size = 0;
 		_pv_trpl.buf = NULL;
 		_pv_trpl.tmsgp = NULL;
6913d30c
 		_pv_trpl.index = 0;
 		_pv_trpl.label = 0;
0305e277
 		_pv_trpl.branch = 0;
eb34a47f
 		return -1;
 	}
06a6a733
 
 	return 0;
 }
 
4d106ce3
 int pv_t_update_inv(struct sip_msg *msg)
 {
 	struct cell * t;
 
 	if(msg==NULL)
 		return 1;
 	if (msg->REQ_METHOD!=METHOD_CANCEL)
 		return 1;
 
 	t = _tmx_tmb.t_lookup_original(msg);
 
 	if(t==NULL || t==T_UNDEFINED)
 		return 1;
 
 	if(t->uas.request==NULL) {
 		_tmx_tmb.unref_cell(t);
 		return 1;
 	}
 
6913d30c
 	if (_pv_tinv.label == t->label && _pv_tinv.index == t->hash_index)  
4d106ce3
 		goto done;
 
 	/* make a copy */
 	if(_pv_tinv.buf==NULL || _pv_tinv.buf_size<t->uas.request->len+1)
 	{
 		if(_pv_tinv.buf!=NULL)
 			pkg_free(_pv_tinv.buf);
 		if(_pv_tinv.tmsgp)
 			free_sip_msg(&_pv_tinv.msg);
 		_pv_tinv.tmsgp = NULL;
6913d30c
 		_pv_tinv.index = 0;
 		_pv_tinv.label = 0;
4d106ce3
 		_pv_tinv.buf_size = t->uas.request->len+1;
 		_pv_tinv.buf = (char*)pkg_malloc(_pv_tinv.buf_size*sizeof(char));
 		if(_pv_tinv.buf==NULL)
 		{
 			LM_ERR("no more pkg\n");
 			_pv_tinv.buf_size = 0;
 			goto error;
 		}
 	}
 	if(_pv_tinv.tmsgp)
 		free_sip_msg(&_pv_tinv.msg);
 	memset(&_pv_tinv.msg, 0, sizeof(struct sip_msg));
 	memcpy(_pv_tinv.buf, t->uas.request->buf, t->uas.request->len);
 	_pv_tinv.buf[t->uas.request->len] = '\0';
 	_pv_tinv.msg.len = t->uas.request->len;
 	_pv_tinv.msg.buf = _pv_tinv.buf;
 	_pv_tinv.tmsgp = t->uas.request;
6913d30c
 	_pv_tinv.index = t->hash_index;
 	_pv_tinv.label = t->label;
4d106ce3
 
 
 	if(pv_t_copy_msg(t->uas.request, &_pv_tinv.msg)!=0)
 	{
 		pkg_free(_pv_tinv.buf);
 		_pv_tinv.buf_size = 0;
 		_pv_tinv.buf = NULL;
 		_pv_tinv.tmsgp = NULL;
6913d30c
 		_pv_tinv.index = 0;
 		_pv_tinv.label = 0;
4d106ce3
 		goto error;
 	}
 
 done:
 	_tmx_tmb.unref_cell(t);
 	return 0;
 
 error:
 	_tmx_tmb.unref_cell(t);
 	return -1;
 }
 
06a6a733
 int pv_get_t_var_req(struct sip_msg *msg,  pv_param_t *param,
 		pv_value_t *res)
 {
 	pv_spec_t *pv=NULL;
 
8988b12d
 	if(!is_route_type(CORE_ONREPLY_ROUTE|TM_ONREPLY_ROUTE)) {
 		LM_DBG("used in unsupported route block - type %d\n", get_route_type());
 		return pv_get_null(msg, param, res);
 	}
 
06a6a733
 	if(pv_t_update_req(msg))
 		return pv_get_null(msg, param, res);
 
 	pv = (pv_spec_t*)param->pvn.u.dname;
 	if(pv==NULL || pv_alter_context(pv))
 		return pv_get_null(msg, param, res);
 
527ba6b4
 	return pv_get_spec_value(&_pv_treq.msg, pv, res);
06a6a733
 }
 
 int pv_get_t_var_rpl(struct sip_msg *msg,  pv_param_t *param,
 		pv_value_t *res)
 {
 	pv_spec_t *pv=NULL;
 
8988b12d
 	if(!is_route_type(FAILURE_ROUTE|BRANCH_FAILURE_ROUTE)) {
 		LM_DBG("used in unsupported route block - type %d\n", get_route_type());
 		return pv_get_null(msg, param, res);
 	}
 
06a6a733
 	if(pv_t_update_rpl(msg))
 		return pv_get_null(msg, param, res);
 
 	pv = (pv_spec_t*)param->pvn.u.dname;
 	if(pv==NULL || pv_alter_context(pv))
 		return pv_get_null(msg, param, res);
 
527ba6b4
 	return pv_get_spec_value(&_pv_trpl.msg, pv, res);
4d106ce3
 }
 
9708bcfb
 int pv_get_t_var_branch(struct sip_msg *msg,  pv_param_t *param,
 		pv_value_t *res)
 {
 	pv_spec_t *pv=NULL;
 
8988b12d
 	if(!is_route_type(FAILURE_ROUTE|BRANCH_FAILURE_ROUTE|TM_ONREPLY_ROUTE)) {
 		LM_DBG("used in unsupported route block - type %d\n", get_route_type());
 		return pv_get_null(msg, param, res);
 	}
 
9708bcfb
 	if(pv_t_update_rpl(msg))
 		return pv_get_null(msg, param, res);
 
 	pv = (pv_spec_t*)param->pvn.u.dname;
 	if(pv==NULL || pv_alter_context(pv))
 		return pv_get_null(msg, param, res);
 
 	return pv_get_spec_value(&_pv_trpl.msg, pv, res);
 }
9051ab74
 
4d106ce3
 int pv_get_t_var_inv(struct sip_msg *msg,  pv_param_t *param,
 		pv_value_t *res)
 {
 	pv_spec_t *pv=NULL;
 
8988b12d
 	if(!is_route_type(REQUEST_ROUTE)) {
 		LM_DBG("used in unsupported route block - type %d\n", get_route_type());
 		return pv_get_null(msg, param, res);
 	}
 
4d106ce3
 	if(pv_t_update_inv(msg))
 		return pv_get_null(msg, param, res);
 
 	pv = (pv_spec_t*)param->pvn.u.dname;
 	if(pv==NULL || pv_alter_context(pv))
 		return pv_get_null(msg, param, res);
 
 	return pv_get_spec_value(&_pv_tinv.msg, pv, res);
06a6a733
 }
 
 int pv_parse_t_var_name(pv_spec_p sp, str *in)
 {
 	pv_spec_t *pv=NULL;
 
 	if(in->s==NULL || in->len<=0)
 		return -1;
 
 	pv = (pv_spec_t*)pkg_malloc(sizeof(pv_spec_t));
 	if(pv==NULL)
 		return -1;
 
 	memset(pv, 0, sizeof(pv_spec_t));
 
 	if(pv_parse_spec(in, pv)==NULL)
 		goto error;
 
 	sp->pvp.pvn.u.dname = (void*)pv;
 	sp->pvp.pvn.type = PV_NAME_PVAR;
 	return 0;
 
 error:
 	LM_ERR("invalid pv name [%.*s]\n", in->len, in->s);
 	if(pv!=NULL)
 		pkg_free(pv);
 	return -1;
 }
 
 /* item functions */
 int pv_get_tm_branch_idx(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res)
 {
 	int l = 0;
 	char *ch = NULL;
14ef60cf
 	struct cell *t;
06a6a733
 	tm_ctx_t *tcx = 0;
14ef60cf
 	int idx = T_BR_UNDEFINED;
06a6a733
 
 	if(msg==NULL || res==NULL)
 		return -1;
 
14ef60cf
 	/* statefull replies have the branch_index set */
72ae9283
 	if(msg->first_line.type == SIP_REPLY) {
14ef60cf
 		tcx = _tmx_tmb.tm_ctx_get();
 		if(tcx != NULL)
 			idx = tcx->branch_index;
 	} else switch(route_type) {
 		case BRANCH_ROUTE:
27c383c9
 		case BRANCH_FAILURE_ROUTE:
 			/* branch and branch_failure routes have their index set */
14ef60cf
 			tcx = _tmx_tmb.tm_ctx_get();
 			if(tcx != NULL)
 				idx = tcx->branch_index;
 			break;
 		case REQUEST_ROUTE:
 			/* take the branch number from the number of added branches */
 			idx = nr_branches;
 			break;
 		case FAILURE_ROUTE:
 			/* first get the transaction */
 			t = _tmx_tmb.t_gett();
37b0bea4
 			if ( t == NULL || t == T_UNDEFINED ) {
14ef60cf
 				return -1;
 			}
 			/* add the currently added branches to the number of
 			 * completed branches in the transaction
 			 */
 			idx = t->nr_of_outgoings + nr_branches;
 			break;
 	}
 
397ef369
 	ch = sint2str(idx, &l);
06a6a733
 
 	res->rs.s = ch;
 	res->rs.len = l;
 
 	res->ri = idx;
 	res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT;
 
 	return 0;
 }
 
6759d6bb
 int pv_get_tm_reply_ruid(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res)
 {
 	struct cell *t;
11791d2b
 	tm_ctx_t *tcx = 0;
6759d6bb
 	int branch;
 
 	if(msg==NULL || res==NULL)
 		return -1;
 
 	/* first get the transaction */
 	if (_tmx_tmb.t_check( msg , 0 )==-1) return -1;
 	if ( (t=_tmx_tmb.t_gett())==0) {
 		/* no T */
11791d2b
 		return pv_get_strempty(msg, param, res);
6759d6bb
 	} else {
 		switch (get_route_type()) {
 			case FAILURE_ROUTE:
 			case BRANCH_FAILURE_ROUTE:
 				/* use the reason of the winning reply */
 				if ( (branch=_tmx_tmb.t_get_picked_branch())<0 ) {
 					LM_CRIT("no picked branch (%d) for a final response"
 							" in MODE_ONFAILURE\n", branch);
11791d2b
 					return pv_get_strempty(msg, param, res);
6759d6bb
 				}
11791d2b
 				LM_DBG("reply ruid is [%.*s]\n", t->uac[branch].ruid.len, t->uac[branch].ruid.s);
 				return pv_get_strval(msg, param, res, &t->uac[branch].ruid);
6759d6bb
 				break;
11791d2b
 			case TM_ONREPLY_ROUTE:
 				tcx = _tmx_tmb.tm_ctx_get();
 				if(tcx == NULL) {
 					return pv_get_strempty(msg, param, res);
 				}
 				branch = tcx->branch_index;
 				if(branch<0 || branch>=t->nr_of_outgoings) {
 					return pv_get_strempty(msg, param, res);
 				}
 				LM_DBG("reply ruid is [%.*s]\n", t->uac[branch].ruid.len, t->uac[branch].ruid.s);
 				return pv_get_strval(msg, param, res, &t->uac[branch].ruid);
6759d6bb
 			default:
 				LM_ERR("unsupported route_type %d\n", get_route_type());
11791d2b
 				return pv_get_strempty(msg, param, res);
6759d6bb
 		}
 	}
 }
 
06a6a733
 int pv_get_tm_reply_code(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res)
 {
 	struct cell *t;
 	int code;
 	int branch;
 
 	if(msg==NULL || res==NULL)
 		return -1;
 
738b113b
 	switch (get_route_type()) {
 		case CORE_ONREPLY_ROUTE:
 		case TM_ONREPLY_ROUTE:
 			/* use the status of the current reply */
 			code = msg->first_line.u.reply.statuscode;
 			goto done;
 	}
 
06a6a733
 	/* first get the transaction */
 	if (_tmx_tmb.t_check( msg , 0 )==-1) return -1;
 	if ( (t=_tmx_tmb.t_gett())==0) {
 		/* no T */
 		code = 0;
 	} else {
5a620b10
 		switch (get_route_type()) {
06a6a733
 			case REQUEST_ROUTE:
2e28163f
 			case BRANCH_ROUTE:
06a6a733
 				/* use the status of the last sent reply */
 				code = t->uas.status;
 				break;
 			case FAILURE_ROUTE:
6759d6bb
 			case BRANCH_FAILURE_ROUTE:
06a6a733
 				/* use the status of the winning reply */
 				if ( (branch=_tmx_tmb.t_get_picked_branch())<0 ) {
 					LM_CRIT("no picked branch (%d) for a final response"
 							" in MODE_ONFAILURE\n", branch);
 					code = 0;
 				} else {
 					code = t->uac[branch].last_received;
 				}
 				break;
 			default:
aad248fc
 				LM_INFO("unsupported route_type %d - code set to 0\n",
 						get_route_type());
06a6a733
 				code = 0;
 		}
 	}
 
738b113b
 done:
 	LM_DBG("reply code is <%d>\n", code);
 	return pv_get_sintval(msg, param, res, code);
06a6a733
 
 	return 0;
 }
 
87bce89f
 int pv_get_tm_reply_reason(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res)
 {
 	struct cell *t;
 	struct sip_msg *reply;
 	int branch;
 
 	if(msg==NULL || res==NULL)
 		return -1;
 
 	/* first get the transaction */
 	if (_tmx_tmb.t_check( msg , 0 )==-1) return -1;
 	if ( (t=_tmx_tmb.t_gett())==0) {
 		/* no T */
738b113b
 		return pv_get_strempty(msg, param, res);
87bce89f
 	} else {
 		switch (get_route_type()) {
 			case CORE_ONREPLY_ROUTE:
 				/*  t_check() above has the side effect of setting T and
21af57b5
 					REFerencing T => we must unref and unset it for the
6c14b13f
 					main/core onreply_route. */
87bce89f
 				_tmx_tmb.t_unref(msg);
 				/* no break */
 			case TM_ONREPLY_ROUTE:
 				/* use the reason of the current reply */
 				res->rs.s = msg->first_line.u.reply.reason.s;
 				res->rs.len = msg->first_line.u.reply.reason.len;
 				break;
 			case FAILURE_ROUTE:
 				/* use the reason of the winning reply */
 				if ( (branch=_tmx_tmb.t_get_picked_branch())<0 ) {
 					LM_CRIT("no picked branch (%d) for a final response"
 							" in MODE_ONFAILURE\n", branch);
 					return -1;
 				}
 				reply = t->uac[branch].reply;
 				if (reply == FAKED_REPLY) {
 					res->rs.s = error_text(t->uac[branch].last_received);
 					res->rs.len = strlen(res->rs.s);
 				} else {
 					res->rs.s = reply->first_line.u.reply.reason.s;
 					res->rs.len = reply->first_line.u.reply.reason.len;
 				}
 				break;
 			default:
 				LM_ERR("unsupported route_type %d\n", get_route_type());
 				return -1;
 		}
 	}
 	LM_DBG("reply reason is [%.*s]\n", res->rs.len, res->rs.s);
 	res->flags = PV_VAL_STR;
 	return 0;
 }
 
2628e120
 int pv_get_tm_reply_last_received(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res)
 {
 	struct cell *t;
 	tm_ctx_t *tcx = 0;
 	int code;
 
 	if(msg==NULL || res==NULL)
 		return -1;
 
 	/* Only for TM reply route */
 	if (get_route_type() != TM_ONREPLY_ROUTE) {
 		LM_ERR("unsupported route_type %d\n", get_route_type());
 		return -1;
 	}
 
 	/* first get the transaction */
 	if (_tmx_tmb.t_check( msg , 0 )==-1) return -1;
 	if ( (t=_tmx_tmb.t_gett())==0) {
 		/* no T */
 		LM_ERR("could not get transaction\n");
 		return -1;
 	}
 
 	/* get the current branch index */
 	tcx = _tmx_tmb.tm_ctx_get();
 	if(tcx == NULL) {
 		LM_ERR("could not get tm context\n");
 		return -1;
 	}
 
 	/* get the last received reply code */
 	code = t->uac[tcx->branch_index].last_received;
 
 	LM_DBG("reply code is <%d>\n",code);
 
 	res->rs.s = int2str( code, &res->rs.len);
 
 	res->ri = code;
 	res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT;
 	return 0;
 }
 
793a3d36
 int pv_parse_t_name(pv_spec_p sp, str *in)
 {
 	if(sp==NULL || in==NULL || in->len<=0)
 		return -1;
 
 	switch(in->len)
 	{
87c5ae36
 		case 3:
 			if(strncmp(in->s, "uri", 3) == 0)
 				sp->pvp.pvn.u.isname.name.n = 6;
 			else goto error;
 			break;
73c29912
 		case 4:
 			if(strncmp(in->s, "ruid", 4) == 0)
 				sp->pvp.pvn.u.isname.name.n = 7;
 			else goto error;
 			break;
9708bcfb
 		case 5:
 			if(strncmp(in->s, "flags", 5) == 0)
 				sp->pvp.pvn.u.isname.name.n = 5;
 			else goto error;
6c14b13f
 			break;
793a3d36
 		case 8:
 			if(strncmp(in->s, "id_label", 8)==0)
 				sp->pvp.pvn.u.isname.name.n = 0;
 			else if(strncmp(in->s, "id_index", 8)==0)
 				sp->pvp.pvn.u.isname.name.n = 1;
 			else goto error;
6c14b13f
 			break;
793a3d36
 		case 10:
 			if(strncmp(in->s, "reply_code", 10)==0)
 				sp->pvp.pvn.u.isname.name.n = 2;
5b76dc48
 			else if(strncmp(in->s, "reply_type", 10)==0)
 				sp->pvp.pvn.u.isname.name.n = 3;
e444f6f8
 			else if(strncmp(in->s, "id_label_n", 10)==0)
 				sp->pvp.pvn.u.isname.name.n = 8;
 			else if(strncmp(in->s, "id_index_n", 10)==0)
 				sp->pvp.pvn.u.isname.name.n = 9;
793a3d36
 			else goto error;
6c14b13f
 			break;
793a3d36
 		case 12:
 			if(strncmp(in->s, "branch_index", 12)==0)
5b76dc48
 				sp->pvp.pvn.u.isname.name.n = 4;
7d2fab4d
 			else if(strncmp(in->s, "reply_reason", 12)==0)
 				sp->pvp.pvn.u.isname.name.n = 10;
793a3d36
 			else goto error;
6c14b13f
 			break;
793a3d36
 		default:
 			goto error;
 	}
 	sp->pvp.pvn.type = PV_NAME_INTSTR;
 	sp->pvp.pvn.u.isname.type = 0;
 
 	return 0;
 
 error:
9708bcfb
 	LM_ERR("unknown PV name %.*s\n", in->len, in->s);
793a3d36
 	return -1;
 
 }
 
 int pv_get_t(struct sip_msg *msg,  pv_param_t *param,
 		pv_value_t *res)
 {
 	tm_cell_t *t;
 
 	if(msg==NULL || param==NULL)
 		return -1;
 
 	/* aliases to old TM pvs */
 	switch(param->pvn.u.isname.name.n)
 	{
 		case 2:
 			return pv_get_tm_reply_code(msg, param, res);
5b76dc48
 		case 4:
793a3d36
 			return pv_get_tm_branch_idx(msg, param, res);
7d2fab4d
 		case 10:
 			return pv_get_tm_reply_reason(msg, param, res);
793a3d36
 	}
 
 	t = _tmx_tmb.t_gett();
 	if(t==NULL || t==T_UNDEFINED) {
 		/* no T */
e444f6f8
 		if(param->pvn.u.isname.name.n==8 || param->pvn.u.isname.name.n==9) {
 			/* id_label_n or id_index_n - attempt to create transaction */
 			if(_tmx_tmb.t_newtran(msg)<0) {
 				LM_ERR("cannot create the transaction\n");
 				return pv_get_null(msg, param, res);
 			}
 			t = _tmx_tmb.t_gett();
 			if (t==NULL || t==T_UNDEFINED) {
 				return pv_get_null(msg, param, res);
 			}
 		} else {
 			return pv_get_null(msg, param, res);
 		}
793a3d36
 	}
 	switch(param->pvn.u.isname.name.n)
 	{
 		case 1:
 			return pv_get_uintval(msg, param, res, t->hash_index);
5b76dc48
 		case 3:
 			if(get_route_type()==FAILURE_ROUTE) {
 				if(_tmx_tmb.t_get_picked_branch()<0 )
 					return pv_get_uintval(msg, param, res, 0);
 				if(t->uac[_tmx_tmb.t_get_picked_branch()].reply==FAKED_REPLY)
 					return pv_get_uintval(msg, param, res, 1);
 			}
 			return pv_get_uintval(msg, param, res, 0);
e444f6f8
 		case 8:
 			return pv_get_uintval(msg, param, res, t->label);
 		case 9:
 			return pv_get_uintval(msg, param, res, t->hash_index);
b0a7ef14
 		default:
 			return pv_get_uintval(msg, param, res, t->label);
793a3d36
 	}
 }
9708bcfb
 
 int pv_get_t_branch(struct sip_msg *msg,  pv_param_t *param,
6c14b13f
 		pv_value_t *res)
9708bcfb
 {
87c5ae36
 	tm_ctx_t *tcx = 0;
6c14b13f
 	tm_cell_t *t;
 	int branch;
 
 	if ((msg == NULL) || (param == NULL)) return -1;
 
 	t = _tmx_tmb.t_gett();
 	if ((t == NULL) || (t == T_UNDEFINED)) {
 		/* no T */
 		return pv_get_null(msg, param, res);
9708bcfb
 	}
6c14b13f
 
 	switch(param->pvn.u.isname.name.n) {
9a8bc024
 		case 5: /* $T_branch(flags) */
6c14b13f
 			switch (get_route_type()) {
 				case FAILURE_ROUTE:
 				case BRANCH_FAILURE_ROUTE:
 					/* use the reason of the winning reply */
 					if ((branch=_tmx_tmb.t_get_picked_branch()) < 0) {
 						LM_CRIT("no picked branch (%d) for a final response"
 								" in MODE_ONFAILURE\n", branch);
9a8bc024
 						return pv_get_null(msg, param, res);
6c14b13f
 					}
 					res->ri = t->uac[branch].branch_flags;
 					res->flags = PV_VAL_INT;
 					LM_DBG("branch flags is [%u]\n", res->ri);
 					break;
 				default:
 					LM_ERR("unsupported route_type %d\n", get_route_type());
9a8bc024
 					return pv_get_null(msg, param, res);
6c14b13f
 			}
755cdf05
 			break;
9a8bc024
 		case 6: /* $T_branch(uri) */
87c5ae36
 			if (get_route_type() != TM_ONREPLY_ROUTE) {
 				LM_ERR("$T_branch(uri) - unsupported route_type %d\n",
 						get_route_type());
 				return pv_get_null(msg, param, res);
 			}
 			tcx = _tmx_tmb.tm_ctx_get();
 			if(tcx == NULL) {
 				return pv_get_null(msg, param, res);
 			}
 			branch = tcx->branch_index;
 			if(branch<0 || branch>=t->nr_of_outgoings) {
 				return pv_get_null(msg, param, res);
 			}
 			return pv_get_strval(msg, param, res, &t->uac[branch].uri);
73c29912
 		case 7: /* $T_branch(ruid) */
 			switch(route_type) {
 				case BRANCH_ROUTE:
 					/* branch and branch_failure routes have their index set */
 					tcx = _tmx_tmb.tm_ctx_get();
 					if(tcx == NULL)
 						return pv_get_null(msg, param, res);
 					return pv_get_strval(msg, param, res, &t->uac[tcx->branch_index].ruid);
 				break;
 				default:
 					return pv_get_tm_reply_ruid(msg, param, res);
 			}
 
6c14b13f
 	}
 	return 0;
9708bcfb
 }