Ver código fonte

tmx: new module to collect K TM extensions

- includes:
	- pseudo-variables
	- MI commands
	- statistics (only register them now, no update)
Daniel-Constantin Mierla 16 anos atrás
pai
commit
06a6a73321

+ 19 - 0
modules_k/tmx/Makefile

@@ -0,0 +1,19 @@
+# $Id$
+#
+# example module makefile
+#
+# 
+# WARNING: do not run this directly, it should be run by the master Makefile
+
+include ../../Makefile.defs
+auto_gen=
+NAME=tmx.so
+LIBS=
+
+DEFS+=-DOPENSER_MOD_INTERFACE
+
+SERLIBPATH=../../lib
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
+
+include ../../Makefile.modules

+ 174 - 0
modules_k/tmx/README

@@ -0,0 +1,174 @@
+TMX Module
+
+Daniel-Constantin Mierla
+
+   asipto.com
+   <[email protected]>
+
+Edited by
+
+Daniel-Constantin Mierla
+
+   <[email protected]>
+
+   Copyright © 2009 Daniel-Constantin Mierla
+     __________________________________________________________________
+
+   Table of Contents
+
+   1. Admin Guide
+
+        1.1. Overview
+        1.2. Dependencies
+
+              1.2.1. Kamailio Modules
+              1.2.2. External Libraries or Applications
+
+        1.3. Exported pseudo-variables
+        1.4. Exported MI Functions
+
+              1.4.1. t_uac_dlg
+              1.4.2. t_uac_cancel
+              1.4.3. t_hash
+              1.4.4. t_reply
+
+        1.5. Exported statistics
+
+              1.5.1. received_replies
+              1.5.2. relayed_replies
+              1.5.3. local_replies
+              1.5.4. UAS_transactions
+              1.5.5. UAC_transactions
+              1.5.6. 2xx_transactions
+              1.5.7. 3xx_transactions
+              1.5.8. 4xx_transactions
+              1.5.9. 5xx_transactions
+              1.5.10. 6xx_transactions
+              1.5.11. inuse_transactions
+
+Chapter 1. Admin Guide
+
+1.1. Overview
+
+   This module collects extensions from Kamailio TM module.
+
+   Kamailio TM (Transaction Management) module documentation is available
+   at: http://www.kamailio.org/docs/modules/1.5.x/tm.html
+
+1.2. Dependencies
+
+1.2.1. Kamailio Modules
+
+   The following modules must be loaded before this module:
+     * tm - transaction management.
+
+1.2.2. External Libraries or Applications
+
+   The following libraries or applications must be installed before
+   running Kamailio with this module loaded:
+     * None.
+
+1.3. Exported pseudo-variables
+
+     * $T_branch_idx
+     * $T_reply_code
+     * $T_req(pv)
+     * $T_rpl(pv)
+
+   Exported pseudo-variables are documented at
+   http://www.kamailio.org/dokuwiki/.
+
+1.4. Exported MI Functions
+
+1.4.1.  t_uac_dlg
+
+   Generates and sends a local SIP request.
+
+   Parameters:
+     * method - request method
+     * RURI - request SIP URI
+     * NEXT HOP - next hop SIP URI (OBP); use "." if no value.
+     * socket - local socket to be used for sending the request; use "."
+       if no value.
+     * headers - set of additional headers to be added to the request; at
+       least "From" and "To" headers must be specify)
+     * body - (optional, may not be present) request body (if present,
+       requires the "Content-Type" and "Content-length" headers)
+
+1.4.2.  t_uac_cancel
+
+   Generates and sends a CANCEL for an existing local SIP request.
+
+   Parameters:
+     * callid - callid of the INVITE request to be cancelled.
+     * cseq - cseq of the INVITE request to be cancelled.
+
+1.4.3.  t_hash
+
+   Gets information about the load of TM internal hash table.
+
+   Parameters:
+     * none
+
+1.4.4.  t_reply
+
+   Generates and sends a reply for an existing inbound SIP transaction.
+
+   Parameters:
+     * code - reply code
+     * reason - reason phrase.
+     * trans_id - transaction identifier (has the hash_entry:label format)
+     * to_tag - To tag to be added to TO header
+     * new_headers - extra headers to be appended to the reply; use a dot
+       (".") char only if there are no headers;
+     * body - (optional, may not be present) reply body (if present,
+       requires the "Content-Type" and "Content-length" headers)
+
+1.5. Exported statistics
+
+   Exported statistics are listed in the next sections. All statistics
+   except "inuse_transactions" can be reset.
+
+1.5.1. received_replies
+
+   Total number of total replies received by TM module.
+
+1.5.2. relayed_replies
+
+   Total number of replies received and relayed by TM module.
+
+1.5.3. local_replies
+
+   Total number of replies local generated by TM module.
+
+1.5.4. UAS_transactions
+
+   Total number of transactions created by received requests.
+
+1.5.5. UAC_transactions
+
+   Total number of transactions created by local generated requests.
+
+1.5.6. 2xx_transactions
+
+   Total number of transactions completed with 2xx replies.
+
+1.5.7. 3xx_transactions
+
+   Total number of transactions completed with 3xx replies.
+
+1.5.8. 4xx_transactions
+
+   Total number of transactions completed with 4xx replies.
+
+1.5.9. 5xx_transactions
+
+   Total number of transactions completed with 5xx replies.
+
+1.5.10. 6xx_transactions
+
+   Total number of transactions completed with 6xx replies.
+
+1.5.11. inuse_transactions
+
+   Number of transactions existing in memory at current time.

+ 4 - 0
modules_k/tmx/doc/Makefile

@@ -0,0 +1,4 @@
+docs = tmx.xml
+
+docbook_dir = ../../../docbook
+include $(docbook_dir)/Makefile.module

+ 43 - 0
modules_k/tmx/doc/tmx.xml

@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+    <bookinfo>
+	<title>TMX Module</title>
+	<productname class="trade">&kamailioname;</productname>
+	<authorgroup>
+	    <author>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<affiliation><orgname>asipto.com</orgname></affiliation>
+		<email>[email protected]</email>
+		<address>
+		<otheraddr>
+		<ulink url="http://www.asipto.com">http://www.asipto.com</ulink>
+		</otheraddr>
+		</address>
+	    </author>
+	    <editor>
+		<firstname>Daniel-Constantin</firstname>
+		<surname>Mierla</surname>
+		<email>[email protected]</email>
+	    </editor>
+	</authorgroup>
+	<copyright>
+	    <year>2009</year>
+	    <holder>Daniel-Constantin Mierla</holder>
+	</copyright>
+    </bookinfo>
+    <toc></toc>
+    
+    <xi:include href="tmx_admin.xml"/>
+    
+    
+</book>

+ 270 - 0
modules_k/tmx/doc/tmx_admin.xml

@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding='ISO-8859-1'?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+
+<!-- Include general documentation entities -->
+<!ENTITY % docentities SYSTEM "../../../docbook/entities.xml">
+%docentities;
+
+]>
+<!-- Module User's Guide -->
+
+<chapter>
+	
+	<title>&adminguide;</title>
+	
+	<section>
+	<title>Overview</title>
+	<para>
+	This module collects extensions from Kamailio TM module.
+	</para>
+	<para>
+	Kamailio TM (Transaction Management) module documentation is available at:
+	<ulink url="http://www.kamailio.org/docs/modules/1.5.x/tm.html">
+	http://www.kamailio.org/docs/modules/1.5.x/tm.html</ulink>
+	</para>
+	</section>
+	
+	<section>
+	<title>Dependencies</title>
+	<section>
+		<title>&kamailio; Modules</title>
+		<para>
+		The following modules must be loaded before this module:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>tm</emphasis> -  transaction management.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	<section>
+		<title>External Libraries or Applications</title>
+		<para>
+		The following libraries or applications must be installed before running
+		&kamailio; with this module loaded:
+			<itemizedlist>
+			<listitem>
+			<para>
+				<emphasis>None</emphasis>.
+			</para>
+			</listitem>
+			</itemizedlist>
+		</para>
+	</section>
+	</section>
+
+	<section>
+		<title>Exported pseudo-variables</title>
+		<itemizedlist>
+			<listitem><para>
+				<emphasis>$T_branch_idx</emphasis>
+			</para></listitem>
+			<listitem><para>
+				<emphasis>$T_reply_code</emphasis>
+			</para></listitem>
+			<listitem><para>
+				<emphasis>$T_req(pv)</emphasis>
+			</para></listitem>
+			<listitem><para>
+				<emphasis>$T_rpl(pv)</emphasis>
+			</para></listitem>
+		</itemizedlist>
+		<para>
+		Exported pseudo-variables are documented at &kamwikilink;.
+		</para>
+	</section>
+
+
+	<section>
+	<title>Exported MI Functions</title>
+
+	<section>
+		<title>
+		<function moreinfo="none">t_uac_dlg</function>
+		</title>
+		<para>
+		Generates and sends a local SIP request.
+		</para>
+		<para>Parameters: </para>
+		<itemizedlist>
+			<listitem><para>
+				<emphasis>method</emphasis> - request method
+			</para></listitem>
+			<listitem><para>
+				<emphasis>RURI</emphasis> - request SIP URI
+			</para></listitem>
+			<listitem><para>
+				<emphasis>NEXT HOP</emphasis> - next hop SIP URI (OBP);
+				use <quote>.</quote> if no value.
+			</para></listitem>
+			<listitem><para>
+				<emphasis>socket</emphasis> - local socket to be used for
+				sending the request; use <quote>.</quote> if no value.
+			</para></listitem>
+			<listitem><para>
+				<emphasis>headers</emphasis> - set of additional headers to
+				be added to the request; at least 
+				<quote>From</quote> and <quote>To</quote> headers must be
+				specify)
+			</para></listitem>
+			<listitem><para>
+				<emphasis>body</emphasis> - (optional, may not be present)
+				request body (if present, requires the 
+				<quote>Content-Type</quote> and <quote>Content-length</quote>
+				headers)
+			</para></listitem>
+		</itemizedlist>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">t_uac_cancel</function>
+		</title>
+		<para>
+		Generates and sends a CANCEL for an existing local SIP request.
+		</para>
+		<para>Parameters: </para>
+		<itemizedlist>
+			<listitem><para>
+				<emphasis>callid</emphasis> - callid of the INVITE request
+				to be cancelled.
+			</para></listitem>
+			<listitem><para>
+				<emphasis>cseq</emphasis> - cseq of the INVITE request to be
+				cancelled.
+			</para></listitem>
+		</itemizedlist>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">t_hash</function>
+		</title>
+		<para>
+		Gets information about the load of TM internal hash table.
+		</para>
+		<para>Parameters: </para>
+		<itemizedlist>
+			<listitem><para>
+				<emphasis>none</emphasis>
+			</para></listitem>
+		</itemizedlist>
+	</section>
+
+	<section>
+		<title>
+		<function moreinfo="none">t_reply</function>
+		</title>
+		<para>
+		Generates and sends a reply for an existing inbound SIP transaction.
+		</para>
+		<para>Parameters: </para>
+		<itemizedlist>
+			<listitem><para>
+				<emphasis>code</emphasis> - reply code
+			</para></listitem>
+			<listitem><para>
+				<emphasis>reason</emphasis> - reason phrase.
+			</para></listitem>
+			<listitem><para>
+				<emphasis>trans_id</emphasis> - transaction identifier
+				(has the hash_entry:label format)
+			</para></listitem>
+			<listitem><para>
+				<emphasis>to_tag</emphasis> - To tag to be added to TO header
+			</para></listitem>
+			<listitem><para>
+				<emphasis>new_headers</emphasis> - extra headers to be
+				appended to the reply; use a dot (<quote>.</quote>) char 
+				only if there are no headers;
+			</para></listitem>
+			<listitem><para>
+				<emphasis>body</emphasis> - (optional, may not be present)
+				reply body (if present, requires the 
+				<quote>Content-Type</quote> and <quote>Content-length</quote>
+				headers)
+			</para></listitem>
+		</itemizedlist>
+	</section>
+
+	</section>
+
+
+	<section>
+		<title>Exported statistics</title>
+		<para>
+		Exported statistics are listed in the next sections. All statistics
+		except <quote>inuse_transactions</quote> can be reset.
+		</para>
+		<section>
+		<title>received_replies</title>
+			<para>
+			Total number of total replies received by TM module.
+			</para>
+		</section>
+		<section>
+		<title>relayed_replies</title>
+			<para>
+			Total number of replies received and relayed by TM module.
+			</para>
+		</section>
+		<section>
+		<title>local_replies</title>
+			<para>
+			Total number of replies local generated by TM module.
+			</para>
+		</section>
+		<section>
+		<title>UAS_transactions</title>
+			<para>
+			Total number of transactions created by received requests.
+			</para>
+		</section>
+		<section>
+		<title>UAC_transactions</title>
+			<para>
+			Total number of transactions created by local generated requests.
+			</para>
+		</section>
+		<section>
+		<title>2xx_transactions</title>
+			<para>
+			Total number of transactions completed with 2xx replies.
+			</para>
+		</section>
+		<section>
+		<title>3xx_transactions</title>
+			<para>
+			Total number of transactions completed with 3xx replies.
+			</para>
+		</section>
+		<section>
+		<title>4xx_transactions</title>
+			<para>
+			Total number of transactions completed with 4xx replies.
+			</para>
+		</section>
+		<section>
+		<title>5xx_transactions</title>
+			<para>
+			Total number of transactions completed with 5xx replies.
+			</para>
+		</section>
+		<section>
+		<title>6xx_transactions</title>
+			<para>
+			Total number of transactions completed with 6xx replies.
+			</para>
+		</section>
+		<section>
+		<title>inuse_transactions</title>
+			<para>
+			Number of transactions existing in memory at current time.
+			</para>
+		</section>
+	</section>
+</chapter>
+

+ 820 - 0
modules_k/tmx/t_mi.c

@@ -0,0 +1,820 @@
+/*
+ * $Id: mi.c 5299 2008-12-04 18:12:33Z henningw $
+ *
+ * Header file for TM MI functions
+ *
+ * Copyright (C) 2001-2003 FhG Fokus
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ *  2006-12-04  created (bogdan)
+ */
+
+/*! \file
+ * \brief TM :: MI functions
+ *
+ * \ingroup tm
+ * - Module: \ref tm
+ */
+
+#include <stdlib.h>
+#include "../../parser/parse_from.h"
+#include "../../modules/tm/ut.h"
+#include "tmx_mod.h"
+
+/*! simple string list */
+struct str_list {
+	str s;
+	struct str_list *next;
+};
+
+/*! Which header fields should be skipped */
+#define skip_hf(_hf) \
+	(((_hf)->type == HDR_FROM_T)  || \
+	((_hf)->type == HDR_TO_T)     || \
+	((_hf)->type == HDR_CALLID_T) || \
+	((_hf)->type == HDR_CSEQ_T))
+
+
+/*!
+ * \brief Convert a URI into socket address.
+ *
+ * Convert a URI into a socket address. Create a temporary proxy.
+ * \param uri input URI
+ * \param to_su target structure
+ * \param proto protocol
+ * \return choosen protocol
+ */
+static inline int uri2su(str *uri, union sockaddr_union *to_su, int proto)
+{
+	struct proxy_l *proxy;
+
+	proxy = uri2proxy(uri, proto);
+	if (!proxy) {
+		ser_error = E_BAD_ADDRESS;
+		LM_ERR("failed create a dst proxy\n");
+		return -1;
+	}
+
+	hostent2su(to_su, &proxy->host, proxy->addr_idx, 
+		(proxy->port) ? proxy->port : SIP_PORT);
+	proto = proxy->proto;
+
+	free_proxy(proxy);
+	pkg_free(proxy);
+	return proto;
+}
+
+/* should be replaced by tm's uri2dst instead */
+static inline struct socket_info *uri2sock(struct sip_msg* msg, str *uri,
+									union sockaddr_union *to_su, int proto)
+{
+	struct socket_info* send_sock;
+
+	if ( (proto=uri2su(uri, to_su, proto))==-1 )
+		return 0;
+
+	send_sock = get_send_socket(msg, to_su, proto);
+	if (!send_sock) {
+		LM_ERR("no corresponding socket for af %d\n", to_su->s.sa_family);
+		ser_error = E_NO_SOCKET;
+	}
+
+	return send_sock;
+}
+
+
+/************** Helper functions (from previous FIFO impl) *****************/
+
+/*!
+ * \brief Check if the request pushed via MI is correctly formed
+ *
+ * Check if the request pushed via MI is correctly formed. Test if
+ * necessary SIP header fileds are included, could be parsed and the
+ * CSEQ is correct.
+ * \param msg SIP message
+ * \param method SIP method
+ * \param body SIP body
+ * \param cseq SIP CSEQ value
+ * \param callid SIP callid, optional
+ * \return zero on success, or a mi_root with an error message included otherwise
+ */
+static inline struct mi_root* mi_check_msg(struct sip_msg* msg, str* method,
+										str* body, int* cseq, str* callid)
+{
+	struct cseq_body *parsed_cseq;
+
+	if (body && body->len && !msg->content_type)
+		return init_mi_tree( 400, "Content-Type missing", 19);
+
+	if (body && body->len && msg->content_length)
+		return init_mi_tree( 400, "Content-Length disallowed", 24);
+
+	if (!msg->to)
+		return init_mi_tree( 400, "To missing", 10);
+
+	if (!msg->from)
+		return init_mi_tree( 400, "From missing", 12);
+
+	/* we also need to know if there is from-tag and add it otherwise */
+	if (parse_from_header(msg) < 0)
+		return init_mi_tree( 400, "Error in From", 13);
+
+	if (msg->cseq && (parsed_cseq = get_cseq(msg))) {
+		if (str2int( &parsed_cseq->number, (unsigned int*)cseq)!=0)
+			return init_mi_tree( 400, "Bad CSeq number", 15);
+
+		if (parsed_cseq->method.len != method->len
+		|| memcmp(parsed_cseq->method.s, method->s, method->len) !=0 )
+			return init_mi_tree( 400, "CSeq method mismatch", 20);
+	} else {
+		*cseq = -1;
+	}
+
+	if (msg->callid) {
+		callid->s = msg->callid->body.s;
+		callid->len = msg->callid->body.len;
+	} else {
+		callid->s = 0;
+		callid->len = 0;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Allocate a new str on a str list
+ *
+ * Allocate a new str in pkg_mem and attach it to a str list. Update
+ * the total number of list elements.
+ * \param s char array
+ * \param len length of the char array
+ * \param last last list element
+ * \param total total number of list elements
+ * \return pointer to the new list element
+ */
+static inline struct str_list *new_str(char *s, int len, struct str_list **last, int *total)
+{
+	struct str_list *new;
+	new=pkg_malloc(sizeof(struct str_list));
+	if (!new) {
+		LM_ERR("no more pkg mem\n");
+		return 0;
+	}
+	new->s.s=s;
+	new->s.len=len;
+	new->next=0;
+
+	(*last)->next=new;
+	*last=new;
+	*total+=len;
+
+	return new;
+}
+
+/*!
+ * \brief Convert a header field block to char array
+ *
+ * Convert a header field block to char array, allocated in
+ * pkg_mem.
+ * \param uri SIP URI
+ * \param hf header field
+ * \param l
+ * \param send_sock socket information
+ * \return new allocated char array on success, zero otherwise
+ */
+static inline char *get_hfblock( str *uri, struct hdr_field *hf, int *l, struct socket_info** send_sock)
+{
+	struct str_list sl, *last, *new, *i, *foo;
+	int hf_avail, frag_len, total_len;
+	char *begin, *needle, *dst, *ret, *d;
+	str *sock_name, *portname;
+	union sockaddr_union to_su;
+
+	ret=0; /* pessimist: assume failure */
+	total_len=0;
+	last=&sl;
+	last->next=0;
+	portname=sock_name=0;
+
+	for (; hf; hf=hf->next) {
+		if (skip_hf(hf)) continue;
+
+		begin=needle=hf->name.s; 
+		hf_avail=hf->len;
+
+		/* substitution loop */
+		while(hf_avail) {
+			d=memchr(needle, SUBST_CHAR, hf_avail);
+			if (!d || d+1>=needle+hf_avail) { /* nothing to substitute */
+				new=new_str(begin, hf_avail, &last, &total_len); 
+				if (!new) goto error;
+				break;
+			} else {
+				frag_len=d-begin;
+				d++; /* d not at the second substitution char */
+				switch(*d) {
+					case SUBST_CHAR:	/* double SUBST_CHAR: IP */
+						/* string before substitute */
+						new=new_str(begin, frag_len, &last, &total_len); 
+						if (!new) goto error;
+						/* substitute */
+						if (!sock_name) {
+							if (*send_sock==0){
+								*send_sock=uri2sock(0, uri, &to_su,PROTO_NONE);
+								if (!*send_sock) {
+									LM_ERR("send_sock failed\n");
+									goto error;
+								}
+							}
+							sock_name=&(*send_sock)->address_str;
+							portname=&(*send_sock)->port_no_str;
+						}
+						new=new_str(sock_name->s, sock_name->len,
+								&last, &total_len );
+						if (!new) goto error;
+						/* inefficient - FIXME --andrei*/
+						new=new_str(":", 1, &last, &total_len);
+						if (!new) goto error;
+						new=new_str(portname->s, portname->len,
+								&last, &total_len );
+						if (!new) goto error;
+						/* keep going ... */
+						begin=needle=d+1;hf_avail-=frag_len+2;
+						continue;
+					default:
+						/* no valid substitution char -- keep going */
+						hf_avail-=frag_len+1;
+						needle=d;
+				}
+			} /* possible substitute */
+		} /* substitution loop */
+		/* proceed to next header */
+		/* new=new_str(CRLF, CRLF_LEN, &last, &total_len );
+		if (!new) goto error; */
+		LM_DBG("one more hf processed\n");
+	} /* header loop */
+
+
+	/* construct a single header block now */
+	ret=pkg_malloc(total_len);
+	if (!ret) {
+		LM_ERR("no pkg mem for hf block\n");
+		goto error;
+	}
+	i=sl.next;
+	dst=ret;
+	while(i) {
+		foo=i;
+		i=i->next;
+		memcpy(dst, foo->s.s, foo->s.len);
+		dst+=foo->s.len;
+		pkg_free(foo);
+	}
+	*l=total_len;
+	return ret;
+
+error:
+	i=sl.next;
+	while(i) {
+		foo=i;
+		i=i->next;
+		pkg_free(foo);
+	}
+	*l=0;
+	return 0;
+}
+
+
+/*!
+ * \brief Print routes
+ *
+ * Print route to MI node, allocate temporary memory in pkg_mem.
+ * \param node MI node
+ * \param dlg route set
+ */
+static inline void mi_print_routes( struct mi_node *node, dlg_t* dlg)
+{
+#define MI_ROUTE_PREFIX_S       "Route: "
+#define MI_ROUTE_PREFIX_LEN     (sizeof(MI_ROUTE_PREFIX_S)-1)
+#define MI_ROUTE_SEPARATOR_S    ", "
+#define MI_ROUTE_SEPARATOR_LEN  (sizeof(MI_ROUTE_SEPARATOR_S)-1)
+	rr_t* ptr;
+	int len;
+	char *p, *s;
+
+	ptr = dlg->hooks.first_route;
+
+	if (ptr==NULL) {
+		add_mi_node_child( node, 0, 0, 0, ".",1);
+		return;
+	}
+
+	len = MI_ROUTE_PREFIX_LEN;
+	for( ; ptr ; ptr=ptr->next)
+		len += ptr->len + MI_ROUTE_SEPARATOR_LEN*(ptr->next!=NULL);
+	if (dlg->hooks.last_route)
+		len += dlg->hooks.last_route->len + 2;
+
+
+	s = pkg_malloc( len );
+	if (s==0) {
+		LM_ERR("no more pkg mem\n");
+		return;
+	}
+
+
+	p = s;
+	memcpy( p, MI_ROUTE_PREFIX_S, MI_ROUTE_PREFIX_LEN);
+	p += MI_ROUTE_PREFIX_LEN;
+
+	for( ptr = dlg->hooks.first_route ; ptr ; ptr=ptr->next) {
+		memcpy( p, ptr->nameaddr.name.s, ptr->len);
+		p += ptr->len;
+		if (ptr->next) {
+			memcpy( p, MI_ROUTE_SEPARATOR_S, MI_ROUTE_SEPARATOR_LEN);
+			p += MI_ROUTE_SEPARATOR_LEN;
+		}
+	}
+
+	if (dlg->hooks.last_route) {
+		*(p++) = '<';
+		memcpy( p, dlg->hooks.last_route->s, dlg->hooks.last_route->len);
+		p += dlg->hooks.last_route->len;
+		*(p++) = '>';
+	}
+
+	add_mi_node_child( node, MI_DUP_VALUE, 0, 0, s, len);
+	pkg_free(s);
+}
+
+
+/*!
+ * \brief Print URIs
+ *
+ * Print URIs to MI node, allocate temporary memory in shm_mem.
+ * \param node MI node
+ * \param reply SIP reply
+ * \return zero on success, -1 on errors
+ */
+static inline int mi_print_uris( struct mi_node *node, struct sip_msg* reply)
+{
+	dlg_t* dlg;
+
+	if (reply==0)
+		goto empty;
+
+	dlg = (dlg_t*)shm_malloc(sizeof(dlg_t));
+	if (!dlg) {
+		LM_ERR("no shm memory left\n");
+		return -1;
+	}
+
+	memset(dlg, 0, sizeof(dlg_t));
+	if (_tmx_tmb.dlg_response_uac(dlg, reply, TARGET_REFRESH_UNKNOWN) < 0) {
+		LM_ERR("failed to create dialog\n");
+		_tmx_tmb.free_dlg(dlg);
+		return -1;
+	}
+
+	if (dlg->state != DLG_CONFIRMED) {
+		_tmx_tmb.free_dlg(dlg);
+		goto empty;
+	}
+
+	if (dlg->hooks.request_uri->s) {
+		add_mi_node_child( node, MI_DUP_VALUE, 0, 0,
+			dlg->hooks.request_uri->s, dlg->hooks.request_uri->len);
+	} else {
+		add_mi_node_child( node, 0, 0, 0, ".",1);
+	}
+	if (dlg->hooks.next_hop->s) {
+		add_mi_node_child( node, MI_DUP_VALUE, 0, 0,
+			dlg->hooks.next_hop->s, dlg->hooks.next_hop->len);
+	} else {
+		add_mi_node_child( node, 0, 0, 0, ".",1);
+	}
+
+	mi_print_routes( node, dlg);
+
+	_tmx_tmb.free_dlg(dlg);
+	return 0;
+empty:
+	add_mi_node_child( node, 0, 0, 0, ".",1);
+	add_mi_node_child( node, 0, 0, 0, ".",1);
+	add_mi_node_child( node, 0, 0, 0, ".",1);
+	return 0;
+}
+
+
+static void mi_uac_dlg_hdl( struct cell *t, int type, struct tmcb_params *ps )
+{
+	struct mi_handler *mi_hdl;
+	struct mi_root *rpl_tree;
+	str text;
+
+	LM_DBG("MI UAC generated status %d\n", ps->code);
+	if (!*ps->param)
+		return;
+
+	mi_hdl = (struct mi_handler *)(*ps->param);
+
+	rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+	if (rpl_tree==0)
+		goto done;
+
+	if (ps->rpl==FAKED_REPLY) {
+		get_reply_status( &text, ps->rpl, ps->code);
+		if (text.s==0) {
+			LM_ERR("get_reply_status failed\n");
+			rpl_tree = 0;
+			goto done;
+		}
+		add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, 0, 0,
+			text.s, text.len);
+		pkg_free(text.s);
+		mi_print_uris( &rpl_tree->node, 0 );
+		add_mi_node_child( &rpl_tree->node, 0, 0, 0, ".",1);
+	} else { 
+		addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%d %.*s",
+			ps->rpl->first_line.u.reply.statuscode,
+			ps->rpl->first_line.u.reply.reason.len,
+			ps->rpl->first_line.u.reply.reason.s);
+		mi_print_uris( &rpl_tree->node, ps->rpl);
+		add_mi_node_child( &rpl_tree->node, MI_DUP_VALUE, 0, 0,
+			ps->rpl->headers->name.s,
+			ps->rpl->len-(ps->rpl->headers->name.s - ps->rpl->buf));
+	}
+
+	LM_DBG("mi_callback successfully completed\n");
+done:
+	if (ps->code >= 200) {
+		mi_hdl->handler_f( rpl_tree, mi_hdl, 1 /*done*/ );
+		*ps->param = 0;
+	} else {
+		mi_hdl->handler_f( rpl_tree, mi_hdl, 0 );
+	}
+}
+
+
+
+/**************************** MI functions ********************************/
+
+
+/*
+  Syntax of "t_uac_dlg" :
+    method
+    RURI
+    NEXT_HOP
+    socket
+    headers
+    [Body]
+*/
+struct mi_root*  mi_tm_uac_dlg(struct mi_root* cmd_tree, void* param)
+{
+	static char err_buf[MAX_REASON_LEN];
+	static struct sip_msg tmp_msg;
+	static dlg_t dlg;
+	struct mi_root *rpl_tree;
+	struct mi_node *node;
+	struct sip_uri pruri;
+	struct sip_uri pnexthop;
+	struct socket_info* sock;
+	str *method;
+	str *ruri;
+	str *nexthop;
+	str *socket;
+	str *hdrs;
+	str *body;
+	str s;
+	str callid = {0,0};
+	int sip_error;
+	int proto;
+	int port;
+	int cseq;
+	int n;
+	uac_req_t uac_r;
+
+	for( n=0,node = cmd_tree->node.kids; n<6 && node ; n++,node=node->next );
+	if ( !(n==5 || n==6) || node!=0)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	/* method name (param 1) */
+	node = cmd_tree->node.kids;
+	method = &node->value;
+
+	/* RURI (param 2) */
+	node = node->next;
+	ruri = &node->value;
+	if (parse_uri( ruri->s, ruri->len, &pruri) < 0 )
+		return init_mi_tree( 400, "Invalid RURI", 12);
+
+	/* nexthop RURI (param 3) */
+	node = node->next;
+	nexthop = &node->value;
+	if (nexthop->len==1 && nexthop->s[0]=='.') {
+		nexthop = 0;
+	} else {
+		if (parse_uri( nexthop->s, nexthop->len, &pnexthop) < 0 )
+			return init_mi_tree( 400, "Invalid NEXTHOP", 15);
+	}
+
+	/* socket (param 4) */
+	node = node->next;
+	socket = &node->value;
+	if (socket->len==1 && socket->s[0]=='.' ) {
+		sock = 0;
+	} else {
+		if (parse_phostport( socket->s, &s.s, &s.len,
+		&port,&proto)!=0)
+			return init_mi_tree( 404, "Invalid local socket", 20);
+		sock = grep_sock_info( &s, (unsigned short)port, proto);
+		if (sock==0)
+			return init_mi_tree( 404, "Local socket not found", 22);
+	}
+
+	/* new headers (param 5) */
+	node = node->next;
+	if (node->value.len==1 && node->value.s[0]=='.')
+		hdrs = 0;
+	else {
+		hdrs = &node->value;
+		/* use SIP parser to look at what is in the FIFO request */
+		memset( &tmp_msg, 0, sizeof(struct sip_msg));
+		tmp_msg.len = hdrs->len; 
+		tmp_msg.buf = tmp_msg.unparsed = hdrs->s;
+		if (parse_headers( &tmp_msg, HDR_EOH_F, 0) == -1 )
+			return init_mi_tree( 400, "Bad headers", 11);
+	}
+
+	/* body (param 5 - optional) */
+	node = node->next;
+	if (node)
+		body = &node->value;
+	else
+		body = 0;
+
+	/* at this moment, we collected all the things we got, let's
+	 * verify user has not forgotten something */
+	rpl_tree = mi_check_msg( &tmp_msg, method, body, &cseq, &callid);
+	if (rpl_tree) {
+		if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers);
+		return rpl_tree;
+	}
+
+	s.s = get_hfblock( nexthop ? nexthop : ruri,
+			tmp_msg.headers, &s.len, &sock);
+	if (s.s==0) {
+		if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers);
+		return 0;
+	}
+
+	memset( &dlg, 0, sizeof(dlg_t));
+	/* Fill in Call-ID, use given Call-ID if
+	 * present and generate it if not present */
+	if (callid.s && callid.len)
+		dlg.id.call_id = callid;
+	else
+		_tmx_tmb.generate_callid(&dlg.id.call_id);
+
+	/* We will not fill in dlg->id.rem_tag because
+	 * if present it will be printed within To HF */
+
+	/* Generate fromtag if not present */
+	if (!(get_from(&tmp_msg)->tag_value.len&&get_from(&tmp_msg)->tag_value.s))
+		_tmx_tmb.generate_fromtag(&dlg.id.loc_tag, &dlg.id.call_id);
+
+	/* Fill in CSeq */
+	if (cseq!=-1)
+		dlg.loc_seq.value = cseq;
+	else
+		dlg.loc_seq.value = DEFAULT_CSEQ;
+	dlg.loc_seq.is_set = 1;
+
+	dlg.loc_uri = tmp_msg.from->body;
+	dlg.rem_uri = tmp_msg.to->body;
+	dlg.hooks.request_uri = ruri;
+	dlg.hooks.next_hop = (nexthop ? nexthop : ruri);
+	dlg.send_sock = sock;
+
+	memset(&uac_r, 0, sizeof(uac_req_t));
+	uac_r.method = method;
+	uac_r.body = body;
+	uac_r.headers = &s;
+	uac_r.dialog = &dlg;
+	if (cmd_tree->async_hdl!=NULL)
+	{
+		uac_r.cb = mi_uac_dlg_hdl;
+		uac_r.cbp = (void*)cmd_tree->async_hdl;
+	}
+	n = _tmx_tmb.t_uac(&uac_r);
+
+	pkg_free(s.s);
+	if (tmp_msg.headers) free_hdr_field_lst(tmp_msg.headers);
+
+	if (n<=0) {
+		/* error */
+		rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+		if (rpl_tree==0)
+			return 0;
+
+		n = err2reason_phrase( n, &sip_error, err_buf, sizeof(err_buf),
+			"MI/UAC") ;
+		if (n > 0 )
+			addf_mi_node_child( &rpl_tree->node, 0, 0, 0, "%d %.*s",
+				sip_error, n, err_buf);
+		else
+			add_mi_node_child( &rpl_tree->node, 0, 0, 0,
+				"500 MI/UAC failed", 17);
+
+		return rpl_tree;
+	} else {
+		if (cmd_tree->async_hdl==NULL)
+			return init_mi_tree( 202, "Accepted", 8);
+		else
+			return MI_ROOT_ASYNC_RPL;
+	}
+}
+
+
+/*
+  Syntax of "t_uac_cancel" :
+    callid
+    cseq
+*/
+struct mi_root* mi_tm_cancel(struct mi_root* cmd_tree, void* param)
+{
+	struct mi_node *node;
+	struct cell *trans;
+
+	node =  cmd_tree->node.kids;
+	if ( !node || !node->next || node->next->next)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	if( _tmx_tmb.t_lookup_callid( &trans, node->value, node->next->value) < 0 )
+		return init_mi_tree( 481, "No such transaction", 19);
+
+	/* cancel the call */
+	LM_DBG("cancelling transaction %p\n",trans);
+
+	_tmx_tmb.cancel_uacs( trans, ~0/*all branches*/, 0);
+
+	_tmx_tmb.unref_cell(trans);
+
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+}
+
+
+/*
+  Syntax of "t_hash" :
+    no nodes
+*/
+struct mi_root* mi_tm_hash(struct mi_root* cmd_tree, void* param)
+{
+	struct mi_root* rpl_tree= NULL;
+	struct mi_node* rpl;
+	struct mi_node* node;
+#ifdef TM_HASH_STATS
+	struct mi_attr* attr;
+#endif
+	struct s_table* tm_t;
+	char *p;
+	int i;
+	int len;
+
+	rpl_tree = init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+	if (rpl_tree==0)
+		return 0;
+	rpl = &rpl_tree->node;
+	tm_t = get_tm_table();
+
+	for (i=0; i<TABLE_ENTRIES; i++) {
+		p = int2str((unsigned long)i, &len );
+		node = add_mi_node_child(rpl, MI_DUP_VALUE , 0, 0, p, len);
+		if(node == NULL)
+			goto error;
+
+#ifdef TM_HASH_STATS
+		p = int2str((unsigned long)tm_t->entries[i].cur_entries, &len );
+		attr = add_mi_attr(node, MI_DUP_VALUE, "Current", 7, p, len );
+		if(attr == NULL)
+			goto error;
+
+		p = int2str((unsigned long)tm_t->entries[i].acc_entries, &len );
+		attr = add_mi_attr(node, MI_DUP_VALUE, "Total", 5, p, len );
+		if(attr == NULL)
+			goto error;
+#endif
+	}
+
+	return rpl_tree;
+error:
+	free_mi_tree(rpl_tree);
+	return init_mi_tree( 500, MI_INTERNAL_ERR_S, MI_INTERNAL_ERR_LEN);
+}
+
+
+/*
+  Syntax of "t_reply" :
+  code
+  reason
+  trans_id
+  to_tag
+  new headers
+  [Body]
+*/
+struct mi_root* mi_tm_reply(struct mi_root* cmd_tree, void* param)
+{
+	struct mi_node* node;
+	unsigned int hash_index;
+	unsigned int hash_label;
+	unsigned int rpl_code;
+	struct cell *trans;
+	char *reason;
+	char *totag;
+	char *new_hdrs;
+	char *body;
+	str tmp;
+	char *p;
+	int n;
+
+	for( n=0,node = cmd_tree->node.kids; n<6 && node ; n++,node=node->next );
+	if ( !(n==5 || n==6) || node!=0)
+		return init_mi_tree( 400, MI_MISSING_PARM_S, MI_MISSING_PARM_LEN);
+
+	/* get all info from the command */
+
+	/* reply code (param 1) */
+	node = cmd_tree->node.kids;
+	if (str2int( &node->value, &rpl_code)!=0 || rpl_code>=700)
+		return init_mi_tree( 400, "Invalid reply code", 18);
+
+	/* reason text (param 2) */
+	node = node->next;
+	reason = as_asciiz(&node->value);
+
+	/* trans_id (param 3) */
+	node = node->next;
+	tmp = node->value;
+	p = memchr( tmp.s, ':', tmp.len);
+	if ( p==NULL)
+		return init_mi_tree( 400, "Invalid trans_id", 16);
+
+	tmp.len = p-tmp.s;
+	if( str2int( &tmp, &hash_index)!=0 )
+		return init_mi_tree( 400, "Invalid index in trans_id", 25);
+
+	tmp.s = p+1;
+	tmp.len = (node->value.s+node->value.len) - tmp.s;
+	if( str2int( &tmp, &hash_label)!=0 )
+		return init_mi_tree( 400, "Invalid label in trans_id", 25);
+
+	if( _tmx_tmb.t_lookup_ident( &trans, hash_index, hash_label)<0 )
+		return init_mi_tree( 404, "Transaction not found", 21);
+
+	/* to_tag (param 4) */
+	node = node->next;
+	totag = as_asciiz(&node->value);
+
+	/* new headers (param 5) */
+	node = node->next;
+	if (node->value.len==1 && node->value.s[0]=='.')
+		new_hdrs = 0;
+	else 
+		new_hdrs = as_asciiz(&node->value);
+
+	/* body (param 5 - optional) */
+	node = node->next;
+	if (node)
+		body = as_asciiz(&node->value);
+	else
+		body = 0;
+
+	/* it's refcounted now, t_reply_with body unrefs for me -- I can 
+	 * continue but may not use T anymore  */
+	n = _tmx_tmb.t_reply_with_body( trans, rpl_code, reason, body,
+			new_hdrs, totag);
+
+	if (n<0)
+		return init_mi_tree( 500, "Reply failed", 12);
+
+	return init_mi_tree( 200, MI_OK_S, MI_OK_LEN);
+}
+

+ 55 - 0
modules_k/tmx/t_mi.h

@@ -0,0 +1,55 @@
+/*
+ * $Id: mi.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * Header file for TM MI functions
+ *
+ * Copyright (C) 2006 Voice Sistem SRL
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * History:
+ * --------
+ *  2006-12-04  created (bogdan)
+ */
+
+/*! \file
+ * \brief TM :: MI functions
+ *
+ * \ingroup tm
+ * - Module: \ref tm
+ * - \ref mi.c
+ */
+
+#ifndef _TM_MI_H_
+#define _TM_MI_H_
+
+#include "../../lib/kmi/mi.h"
+
+#define MI_TM_UAC      "t_uac_dlg"
+#define MI_TM_CANCEL   "t_uac_cancel"
+#define MI_TM_HASH     "t_hash"
+#define MI_TM_REPLY    "t_reply"
+
+struct mi_root* mi_tm_uac_dlg(struct mi_root* cmd_tree, void* param);
+
+struct mi_root* mi_tm_cancel(struct mi_root* cmd_tree, void* param);
+
+struct mi_root* mi_tm_hash(struct mi_root* cmd_tree, void* param);
+
+struct mi_root* mi_tm_reply(struct mi_root* cmd_tree, void* param);
+
+#endif

+ 321 - 0
modules_k/tmx/t_var.c

@@ -0,0 +1,321 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2008 Elena-Ramona Modroiu (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "../../mem/mem.h"
+
+#include "tmx_mod.h"
+#include "t_var.h"
+
+static struct cell *_pv_T_req = NULL;
+static struct cell *_pv_T_rpl = NULL;
+static struct sip_msg _pv_treq;
+static struct sip_msg _pv_trpl;
+static struct sip_msg *_pv_treq_p = NULL;
+static struct sip_msg *_pv_trpl_p = NULL;
+static unsigned int _pv_treq_id = 0;
+static unsigned int _pv_trpl_id = 0;
+static char *_pv_treq_buf = NULL;
+static char *_pv_trpl_buf = NULL;
+static unsigned int _pv_treq_size = 0;
+static unsigned int _pv_trpl_size = 0;
+
+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;
+
+	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_T_req==t && t->uas.request==_pv_treq_p
+			&& t->uas.request->id==_pv_treq_id)
+		return 0;
+
+	/* make a copy */
+	if(_pv_treq_buf==NULL || _pv_treq_size<t->uas.request->len+1)
+	{
+		if(_pv_treq_buf!=NULL)
+			pkg_free(_pv_treq_buf);
+		if(_pv_treq_p)
+			free_sip_msg(&_pv_treq);
+		_pv_treq_p = NULL;
+		_pv_treq_id = 0;
+		_pv_T_req = NULL;
+		_pv_treq_size = t->uas.request->len+1;
+		_pv_treq_buf = (char*)pkg_malloc(_pv_treq_size*sizeof(char));
+		if(_pv_treq_buf==NULL)
+		{
+			LM_ERR("no more pkg\n");
+			_pv_treq_size = 0;
+			return -1;
+		}
+	}
+	memset(&_pv_treq, 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.len = t->uas.request->len;
+	_pv_treq.buf = _pv_treq_buf;
+	_pv_treq_p = t->uas.request;
+	_pv_treq_id = t->uas.request->id;
+	_pv_T_req = t;
+
+
+	pv_t_copy_msg(t->uas.request, &_pv_treq);
+
+	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_T_rpl==t && t->uac[branch].reply==_pv_trpl_p
+			&& t->uac[branch].reply->id==_pv_trpl_id)
+		return 0;
+
+	/* make a copy */
+	if(_pv_trpl_buf==NULL || _pv_trpl_size<t->uac[branch].reply->len+1)
+	{
+		if(_pv_trpl_buf!=NULL)
+			pkg_free(_pv_trpl_buf);
+		if(_pv_trpl_p)
+			free_sip_msg(&_pv_trpl);
+		_pv_trpl_p = NULL;
+		_pv_trpl_id = 0;
+		_pv_T_rpl = NULL;
+		_pv_trpl_size = t->uac[branch].reply->len+1;
+		_pv_trpl_buf = (char*)pkg_malloc(_pv_trpl_size*sizeof(char));
+		if(_pv_trpl_buf==NULL)
+		{
+			LM_ERR("no more pkg\n");
+			_pv_trpl_size = 0;
+			return -1;
+		}
+	}
+	memset(&_pv_trpl, 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.len = t->uac[branch].reply->len;
+	_pv_trpl.buf = _pv_trpl_buf;
+	_pv_trpl_p = t->uac[branch].reply;
+	_pv_trpl_id = t->uac[branch].reply->id;
+	_pv_T_rpl = t;
+
+	pv_t_copy_msg(t->uac[branch].reply, &_pv_trpl);
+
+	return 0;
+}
+
+int pv_get_t_var_req(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res)
+{
+	pv_spec_t *pv=NULL;
+
+	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, 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(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, 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)
+		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;
+	tm_ctx_t *tcx = 0;
+	int idx = 0;
+
+	if(msg==NULL || res==NULL)
+		return -1;
+
+	tcx = _tmx_tmb.tm_ctx_get();
+	if(tcx != NULL)
+		idx = tcx->branch_index;
+	
+	ch = int2str(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_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;
+
+	/* 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 (route_type) {
+			case REQUEST_ROUTE:
+				/* use the status of the last sent reply */
+				code = t->uas.status;
+				break;
+			case ONREPLY_ROUTE:
+				/* use the status of the current reply */
+				code = msg->first_line.u.reply.statuscode;
+				break;
+			case 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_ERR("unsupported route_type %d\n", route_type);
+				code = 0;
+		}
+	}
+
+	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;
+}
+

+ 40 - 0
modules_k/tmx/t_var.h

@@ -0,0 +1,40 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2008 Elena-Ramona Modroiu (asipto.com)
+ *
+ * This file is part of kamailio, a free SIP server.
+ *
+ * openser 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
+ *
+ * openser 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+		       
+#ifndef _T_VAR_H_
+#define _T_VAR_H_
+
+#include "../../pvar.h"
+
+int pv_get_t_var_req(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res);
+int pv_get_t_var_rpl(struct sip_msg *msg,  pv_param_t *param,
+		pv_value_t *res);
+
+int pv_parse_t_var_name(pv_spec_p sp, str *in);
+
+int pv_get_tm_branch_idx(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
+int pv_get_tm_reply_code(struct sip_msg *msg, pv_param_t *param,
+		pv_value_t *res);
+
+#endif

+ 166 - 0
modules_k/tmx/tmx_mod.c

@@ -0,0 +1,166 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009
+ *
+ * This file is part of SIP-Router.org, a free SIP server.
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "../../sr_module.h"
+#include "../../dprint.h"
+#include "../../mod_fix.h"
+#include "../../modules/tm/tm_load.h"
+
+#include "t_var.h"
+#include "t_mi.h"
+
+MODULE_VERSION
+
+
+/** TM bind */
+struct tm_binds _tmx_tmb;
+
+/** parameters */
+
+/** module functions */
+static int mod_init(void);
+void destroy(void);
+
+/* statistic variables */
+stat_var *tm_rcv_rpls;
+stat_var *tm_rld_rpls;
+stat_var *tm_loc_rpls;
+stat_var *tm_uas_trans;
+stat_var *tm_uac_trans;
+stat_var *tm_trans_2xx;
+stat_var *tm_trans_3xx;
+stat_var *tm_trans_4xx;
+stat_var *tm_trans_5xx;
+stat_var *tm_trans_6xx;
+stat_var *tm_trans_inuse;
+
+#ifdef STATISTICS
+static stat_export_t mod_stats[] = {
+	{"received_replies" ,    0,              &tm_rcv_rpls    },
+	{"relayed_replies" ,     0,              &tm_rld_rpls    },
+	{"local_replies" ,       0,              &tm_loc_rpls    },
+	{"UAS_transactions" ,    0,              &tm_uas_trans   },
+	{"UAC_transactions" ,    0,              &tm_uac_trans   },
+	{"2xx_transactions" ,    0,              &tm_trans_2xx   },
+	{"3xx_transactions" ,    0,              &tm_trans_3xx   },
+	{"4xx_transactions" ,    0,              &tm_trans_4xx   },
+	{"5xx_transactions" ,    0,              &tm_trans_5xx   },
+	{"6xx_transactions" ,    0,              &tm_trans_6xx   },
+	{"inuse_transactions" ,  STAT_NO_RESET,  &tm_trans_inuse },
+	{0,0,0}
+};
+#endif
+
+/**
+ * pseudo-variables exported by TM module
+ */
+static pv_export_t mod_pvs[] = {
+	{ {"T_branch_idx", sizeof("T_branch_idx")-1}, PVT_OTHER,
+		pv_get_tm_branch_idx, 0,
+		 0, 0, 0, 0 },
+	{ {"T_reply_code", sizeof("T_reply_code")-1}, PVT_OTHER,
+		pv_get_tm_reply_code, 0,
+		 0, 0, 0, 0 },
+	{ {"T_req", sizeof("T_req")-1}, PVT_OTHER, pv_get_t_var_req, 0,
+		pv_parse_t_var_name, 0, 0, 0 },
+	{ {"T_rpl", sizeof("T_rpl")-1}, PVT_OTHER, pv_get_t_var_rpl, 0,
+		pv_parse_t_var_name, 0, 0, 0 },
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+static mi_export_t mi_cmds [] = {
+	{MI_TM_UAC,     mi_tm_uac_dlg,   MI_ASYNC_RPL_FLAG,  0,  0 },
+	{MI_TM_CANCEL,  mi_tm_cancel,    0,                  0,  0 },
+	{MI_TM_HASH,    mi_tm_hash,      MI_NO_INPUT_FLAG,   0,  0 },
+	{MI_TM_REPLY,   mi_tm_reply,     0,                  0,  0 },
+	{0,0,0,0,0}
+};
+
+
+static cmd_export_t cmds[]={
+	{0,0,0,0,0,0}
+};
+
+static param_export_t params[]={
+	{0,0,0}
+};
+
+
+/** module exports */
+struct module_exports exports= {
+	"tmx",
+	DEFAULT_DLFLAGS, /* dlopen flags */
+	cmds,
+	params,
+#ifdef STATISTICS
+	mod_stats,  /* exported statistics */
+#else
+	0,
+#endif
+	mi_cmds,    /* exported MI functions */
+	mod_pvs,    /* exported pseudo-variables */
+	0,          /* extra processes */
+	mod_init,   /* module initialization function */
+	0,
+	(destroy_function) destroy,
+	0           /* per-child init function */
+};
+
+/**
+ * init module function
+ */
+static int mod_init(void)
+{
+	/* load the TM API */
+	if (load_tm_api(&_tmx_tmb)!=0) {
+		LM_ERR("can't load TM API\n");
+		return -1;
+	}
+
+	if(register_mi_mod(exports.name, mi_cmds)!=0)
+	{
+		LM_ERR("failed to register MI commands\n");
+		return -1;
+	}
+#ifdef STATISTICS
+	/* register statistics */
+	if (register_module_stats( exports.name, mod_stats)!=0 ) {
+		LM_ERR("failed to register statistics\n");
+		return -1;
+	}
+#endif
+	return 0;
+}
+
+/**
+ * destroy function
+ */
+void destroy(void)
+{
+	return;
+}
+
+

+ 31 - 0
modules_k/tmx/tmx_mod.h

@@ -0,0 +1,31 @@
+/**
+ * $Id$
+ *
+ * Copyright (C) 2009
+ *
+ * This file is part of SIP-Router.org, a free SIP server.
+ *
+ * SIP-Router 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _TMX_MOD_H_
+#define _TMX_MOD_H_
+
+#include "../../modules/tm/tm_load.h"
+
+/** TM bind */
+extern struct tm_binds _tmx_tmb;
+
+#endif