/* * * Copyright (C) 2008 Elena-Ramona Modroiu (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 TMX :: var functions * * \ingroup tm * - Module: \ref tm */ #include "../../core/mem/mem.h" #include "../../core/dset.h" #include "tmx_mod.h" #include "t_var.h" struct _pv_tmx_data { unsigned int index; unsigned int label; int branch; 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; static struct _pv_tmx_data _pv_tinv; void pv_tmx_data_init(void) { memset(&_pv_treq, 0, sizeof(struct _pv_tmx_data)); memset(&_pv_trpl, 0, sizeof(struct _pv_tmx_data)); memset(&_pv_tinv, 0, sizeof(struct _pv_tmx_data)); } 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; dst->flags = src->flags; dst->fwd_send_flags = src->fwd_send_flags; dst->rpl_send_flags = src->rpl_send_flags; dst->force_send_socket = src->force_send_socket; 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; if (_pv_treq.label == t->label && _pv_treq.index == t->hash_index) return 0; /* make a copy */ if(_pv_treq.buf==NULL || _pv_treq.buf_size<t->uas.request->len+1) { if(_pv_treq.buf!=NULL) pkg_free(_pv_treq.buf); if(_pv_treq.tmsgp) free_sip_msg(&_pv_treq.msg); _pv_treq.tmsgp = NULL; _pv_treq.index = 0; _pv_treq.label = 0; _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) { PKG_MEM_ERROR; _pv_treq.buf_size = 0; return -1; } } 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; _pv_treq.index = t->hash_index; _pv_treq.label = t->label; if(pv_t_copy_msg(t->uas.request, &_pv_treq.msg)!=0) { pkg_free(_pv_treq.buf); _pv_treq.buf_size = 0; _pv_treq.buf = NULL; _pv_treq.tmsgp = NULL; _pv_treq.index = 0; _pv_treq.label = 0; return -1; } 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; if (_pv_trpl.label == t->label && _pv_trpl.index == t->hash_index && _pv_trpl.branch == branch) return 0; /* make a copy */ if(_pv_trpl.buf==NULL || _pv_trpl.buf_size<t->uac[branch].reply->len+1) { if(_pv_trpl.buf!=NULL) pkg_free(_pv_trpl.buf); if(_pv_trpl.tmsgp) free_sip_msg(&_pv_trpl.msg); _pv_trpl.tmsgp = NULL; _pv_trpl.index = 0; _pv_trpl.label = 0; _pv_trpl.branch = 0; _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) { PKG_MEM_ERROR; _pv_trpl.buf_size = 0; return -1; } } 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; _pv_trpl.index = t->hash_index; _pv_trpl.label = t->label; _pv_trpl.branch = branch; if(pv_t_copy_msg(t->uac[branch].reply, &_pv_trpl.msg)!=0) { pkg_free(_pv_trpl.buf); _pv_trpl.buf_size = 0; _pv_trpl.buf = NULL; _pv_trpl.tmsgp = NULL; _pv_trpl.index = 0; _pv_trpl.label = 0; _pv_trpl.branch = 0; return -1; } return 0; } 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; } if (_pv_tinv.label == t->label && _pv_tinv.index == t->hash_index) 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; _pv_tinv.index = 0; _pv_tinv.label = 0; _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) { PKG_MEM_ERROR; _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; _pv_tinv.index = t->hash_index; _pv_tinv.label = t->label; 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; _pv_tinv.index = 0; _pv_tinv.label = 0; goto error; } done: _tmx_tmb.unref_cell(t); return 0; error: _tmx_tmb.unref_cell(t); return -1; } int pv_get_t_var_req(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { pv_spec_t *pv=NULL; 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); } 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); return pv_get_spec_value(&_pv_treq.msg, pv, res); } int pv_get_t_var_rpl(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { pv_spec_t *pv=NULL; 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); } 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); } int pv_get_t_var_branch(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { pv_spec_t *pv=NULL; 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); } 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); } int pv_get_t_var_inv(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { pv_spec_t *pv=NULL; 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); } 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); } 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) { PKG_MEM_ERROR; 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; struct cell *t; tm_ctx_t *tcx = 0; int idx = T_BR_UNDEFINED; if(msg==NULL || res==NULL) return -1; /* statefull replies have the branch_index set */ if(msg->first_line.type == SIP_REPLY) { tcx = _tmx_tmb.tm_ctx_get(); if(tcx != NULL) idx = tcx->branch_index; } else switch(route_type) { case BRANCH_ROUTE: case BRANCH_FAILURE_ROUTE: /* branch and branch_failure routes have their index set */ 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(); if ( t == NULL || t == T_UNDEFINED ) { return -1; } /* add the currently added branches to the number of * completed branches in the transaction */ idx = t->nr_of_outgoings + nr_branches; break; } ch = sint2str(idx, &l); res->rs.s = ch; res->rs.len = l; res->ri = idx; res->flags = PV_VAL_STR|PV_VAL_INT|PV_TYPE_INT; return 0; } int pv_get_tm_reply_ruid(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { struct cell *t; tm_ctx_t *tcx = 0; 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 */ return pv_get_strempty(msg, param, res); } 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); 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); break; 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); default: LM_ERR("unsupported route_type %d\n", get_route_type()); return pv_get_strempty(msg, param, res); } } } 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; 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; } /* 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 { switch (get_route_type()) { case REQUEST_ROUTE: case BRANCH_ROUTE: /* use the status of the last sent reply */ code = t->uas.status; break; case FAILURE_ROUTE: case BRANCH_FAILURE_ROUTE: /* 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: LM_INFO("unsupported route_type %d - code set to 0\n", get_route_type()); code = 0; } } done: LM_DBG("reply code is <%d>\n", code); return pv_get_sintval(msg, param, res, code); return 0; } 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 */ return pv_get_strempty(msg, param, res); } else { switch (get_route_type()) { case CORE_ONREPLY_ROUTE: /* t_check() above has the side effect of setting T and REFerencing T => we must unref and unset it for the main/core onreply_route. */ _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; } 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; } int pv_parse_t_name(pv_spec_p sp, str *in) { if(sp==NULL || in==NULL || in->len<=0) return -1; switch(in->len) { case 3: if(strncmp(in->s, "uri", 3) == 0) sp->pvp.pvn.u.isname.name.n = 6; else goto error; break; case 4: if(strncmp(in->s, "ruid", 4) == 0) sp->pvp.pvn.u.isname.name.n = 7; else goto error; break; case 5: if(strncmp(in->s, "flags", 5) == 0) sp->pvp.pvn.u.isname.name.n = 5; else goto error; break; 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; break; case 10: if(strncmp(in->s, "reply_code", 10)==0) sp->pvp.pvn.u.isname.name.n = 2; else if(strncmp(in->s, "reply_type", 10)==0) sp->pvp.pvn.u.isname.name.n = 3; 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; else goto error; break; case 12: if(strncmp(in->s, "branch_index", 12)==0) sp->pvp.pvn.u.isname.name.n = 4; else if(strncmp(in->s, "reply_reason", 12)==0) sp->pvp.pvn.u.isname.name.n = 10; else goto error; break; default: goto error; } sp->pvp.pvn.type = PV_NAME_INTSTR; sp->pvp.pvn.u.isname.type = 0; return 0; error: LM_ERR("unknown PV name %.*s\n", in->len, in->s); 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); case 4: return pv_get_tm_branch_idx(msg, param, res); case 10: return pv_get_tm_reply_reason(msg, param, res); } t = _tmx_tmb.t_gett(); if(t==NULL || t==T_UNDEFINED) { /* no T */ 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); } } switch(param->pvn.u.isname.name.n) { case 1: return pv_get_uintval(msg, param, res, t->hash_index); 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); case 8: return pv_get_uintval(msg, param, res, t->label); case 9: return pv_get_uintval(msg, param, res, t->hash_index); default: return pv_get_uintval(msg, param, res, t->label); } } int pv_get_t_branch(struct sip_msg *msg, pv_param_t *param, pv_value_t *res) { tm_ctx_t *tcx = 0; 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); } switch(param->pvn.u.isname.name.n) { case 5: /* $T_branch(flags) */ 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); return pv_get_null(msg, param, res); } break; case TM_ONREPLY_ROUTE: tcx = _tmx_tmb.tm_ctx_get(); if(tcx == NULL) { LM_ERR("no reply branch\n"); return pv_get_null(msg, param, res); } branch = tcx->branch_index; break; default: LM_ERR("unsupported route_type %d\n", get_route_type()); return pv_get_null(msg, param, res); } if(branch<0 || branch>=t->nr_of_outgoings) { return pv_get_null(msg, param, res); } LM_DBG("branch flags is [%u]\n", t->uac[branch].branch_flags); return pv_get_uintval(msg, param, res, t->uac[branch].branch_flags); break; case 6: /* $T_branch(uri) */ 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); 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); } } return 0; }