Просмотр исходного кода

- vm functionality moved in tm (not fully tested); DB dependency replaced by
AVP (user's preferences)

Bogdan-Andrei Iancu 21 лет назад
Родитель
Сommit
7683c6fd4e
5 измененных файлов с 609 добавлено и 9 удалено
  1. 465 4
      modules/tm/t_funcs.c
  2. 4 1
      modules/tm/t_funcs.h
  3. 129 4
      modules/tm/t_reply.c
  4. 2 0
      modules/tm/t_reply.h
  5. 9 0
      modules/tm/tm.c

+ 465 - 4
modules/tm/t_funcs.c

@@ -42,23 +42,37 @@
  *  2004-02-13  t->is_invite and t->local replaced with flags (bogdan)
  */
 
-
-#include "defs.h"
-
-
 #include <limits.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <sys/uio.h>
+
 #include "../../dprint.h"
 #include "../../config.h"
+#include "../../usr_avp.h"
 #include "../../parser/parser_f.h"
+#include "../../parser/parse_from.h"
+#include "../../parser/parse_rr.h"
+#include "../../parser/parse_nameaddr.h"
+#include "../../parser/contact/parse_contact.h"
 #include "../../ut.h"
 #include "../../hash_func.h"
 #include "../../dset.h"
 #include "../../mem/mem.h"
+#include "defs.h"
 #include "t_funcs.h"
 #include "t_fwd.h"
 #include "t_lookup.h"
 #include "config.h"
 #include "t_stats.h"
+#include "t_reply.h"
+
+
+
 
 
 /* ----------------------------------------------------- */
@@ -311,3 +325,450 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 done:
 	return ret;
 }
+
+/********************   T_WRITE_REQ function   **************************/
+
+#define TWRITE_FROM_PARSED     (1<<1)
+#define TWRITE_CONTACT_PARSED  (1<<2)
+#define TWRITE_RR_PARSED       (1<<3)
+#define TWRITE_RRTMP_PARSED    (1<<4)
+
+#define TWRITE_PARAMS          21
+#define TWRITE_VERSION_S       "0.2"
+#define TWRITE_VERSION_LEN     (sizeof(TWRITE_VERSION_S)-1)
+#define eol_line(_i_)          ( lines_eol[2*(_i_)] )
+
+#define IDBUF_LEN              128
+#define ROUTE_BUFFER_MAX       512
+#define HDRS_BUFFER_MAX        512
+#define CMD_BUFFER_MAX         128
+
+#define append_str(_dest,_src,_len) \
+	do{ \
+		memcpy( (_dest) , (_src) , (_len) );\
+		(_dest) += (_len) ;\
+	}while(0);
+
+#define append_chr(_dest,_c) \
+	*((_dest)++) = _c;
+
+#define copy_route(s,len,rs,rlen) \
+	do {\
+		if(rlen+len+3 >= ROUTE_BUFFER_MAX){\
+			LOG(L_ERR,"vm: buffer overflow while copying new route\n");\
+			goto error3;\
+		}\
+		if(len){\
+			append_chr(s,','); len++;\
+		}\
+		append_chr(s,'<');len++;\
+		append_str(s,rs,rlen);\
+		len += rlen; \
+		append_chr(s,'>');len++;\
+	} while(0)
+
+static str   lines_eol[2*TWRITE_PARAMS];
+static str   eol={"\n",1};
+
+
+
+int init_twrite_lines()
+{
+	int i;
+
+	/* init the line table */
+	for(i=0;i<TWRITE_PARAMS;i++) {
+		lines_eol[2*i].s = 0;
+		lines_eol[2*i].len = 0;
+		lines_eol[2*i+1] = eol;
+	}
+
+	/* first line is the version - fill it now */
+	eol_line(0).s   = TWRITE_VERSION_S;
+	eol_line(0).len = TWRITE_VERSION_LEN;
+
+	return 0;
+}
+
+
+static int inline write_to_fifo(char *fifo, int cnt )
+{
+	int   fd_fifo;
+
+	/* open FIFO file stream */
+	if((fd_fifo = open(fifo,O_WRONLY | O_NONBLOCK)) == -1){
+		switch(errno){
+			case ENXIO:
+				LOG(L_ERR,"ERROR:tm:t_write_req: nobody listening on "
+					" [%s] fifo for reading!\n",fifo);
+			default:
+				LOG(L_ERR,"ERROR:tm:t_write_req: failed to open [%s] "
+					"fifo : %s\n", fifo, strerror(errno));
+		}
+		goto error;
+	}
+
+	/* write now (unbuffered straight-down write) */
+repeat:
+	if (writev(fd_fifo, (struct iovec*)lines_eol, 2*cnt)<0) {
+		if (errno!=EINTR) {
+			LOG(L_ERR, "ERROR:tm:t_write_req: writev failed: %s\n",
+				strerror(errno));
+			close(fd_fifo);
+			goto error;
+		} else {
+			goto repeat;
+		}
+	}
+	close(fd_fifo);
+
+	DBG("DEBUG:tm:t_write_req: write completed\n");
+	return 1; /* OK */
+
+error:
+	return -1;
+}
+
+
+int t_write_req(struct sip_msg* msg, char* vm_fifo, char* action)
+{
+	static char     id_buf[IDBUF_LEN];
+	static char     route_buffer[ROUTE_BUFFER_MAX];
+	static char     hdrs_buf[HDRS_BUFFER_MAX];
+	static char     cmd_buf[CMD_BUFFER_MAX];
+	static str      empty_param = {".",1};
+	static str      email_attr = {"email",5};
+	str             body;
+	unsigned int    hash_index;
+	unsigned int    label;
+	contact_body_t* cb=0;
+	name_addr_t     na;
+	struct usr_avp  *email_avp;
+	str             str_uri;
+	contact_t*      c=0;
+	int             int_buflen, l;
+	char*           i2s;
+	char*           s;
+	rr_t*           record_route;
+	char            fproxy_lr;
+	str             route;
+	str             next_hop;
+	str             hdrs;
+	int             msg_flags_size;
+	struct hdr_field* p_hdr;
+	param_hooks_t     hooks;
+	int             parse_flags;
+	int             ret;
+	str             tmp_s;
+
+	ret = -1;
+	email_avp = 0;
+
+	if(msg->first_line.type != SIP_REQUEST){
+		LOG(L_ERR,"ERROR:tm:t_write_req: called for something else then"
+			"a SIP request\n");
+		goto error;
+	}
+
+	parse_flags = 0;
+
+	/* parse all -- we will need every header field for a UAS
+	 * avoid parsing if in FAILURE_ROUTE - all hdr have already been parsed */
+	if ( rmode==MODE_REQUEST && parse_headers(msg, HDR_EOH, 0)==-1) {
+		LOG(L_ERR,"ERROR:tm:t_write_req: parse_headers failed\n");
+		goto error;
+	}
+
+	/* find index and hash; (the transaction can be safely used due 
+	 * to refcounting till script completes) */
+	if( t_get_trans_ident(msg,&hash_index,&label) == -1 ) {
+		LOG(L_ERR,"ERROR:tm:t_write_req: t_get_trans_ident failed\n");
+		goto error;
+	}
+
+	/* if in FAILURE_MODE, we have to remember if the from will be parsed by us
+	 * or was already parsed - if it's parsed by us, free it at the end */
+	if (msg->from->parsed==0) {
+		if (rmode==MODE_ONFAILURE )
+			parse_flags |= TWRITE_FROM_PARSED;
+		if(parse_from_header(msg) == -1){
+			LOG(L_ERR,"ERROR:tm:t_write_req:while parsing <From:> header\n");
+			goto error;
+		}
+	}
+
+	/* parse the RURI (doesn't make any malloc) */
+	msg->parsed_uri_ok = 0; /* force parsing */
+	if (parse_sip_msg_uri(msg)<0) {
+		LOG(L_ERR,"ERROR:tm:t_write_req: vm: uri has not been parsed\n");
+		goto error1;
+	}
+
+	/* parse contact header */
+	str_uri.s = 0;
+	str_uri.len = 0;
+	if(msg->contact) {
+		if (msg->contact->parsed==0) {
+			if (rmode==MODE_ONFAILURE)
+				parse_flags |= TWRITE_CONTACT_PARSED;
+			if( parse_contact(msg->contact) == -1) {
+				LOG(L_ERR,"ERROR:tm:t_write_req: error while parsing "
+						"<Contact:> header\n");
+				goto error1;
+			}
+		}
+#ifdef EXTRA_DEBUG
+		DBG("DEBUG: vm:msg->contact->parsed ******* contacts: *******\n");
+#endif
+		cb = (contact_body_t*)msg->contact->parsed;
+		if(cb && (c=cb->contacts)) {
+			str_uri = c->uri;
+			if (find_not_quoted(&str_uri,'<')) {
+				parse_nameaddr(&str_uri,&na);
+				str_uri = na.uri;
+			}
+#ifdef EXTRA_DEBUG
+			/*print_contacts(c);*/
+			for(; c; c=c->next)
+				DBG("DEBUG:           %.*s\n",c->uri.len,c->uri.s);
+#endif
+		}
+#ifdef EXTRA_DEBUG
+		DBG("DEBUG:tm:t_write_req: **** end of contacts ****\n");
+#endif
+	}
+
+	/* str_uri is taken from caller's contact or from header
+	 * for backwards compatibility with pre-3261 (from is already parsed)*/
+	if(!str_uri.len || !str_uri.s)
+		str_uri = get_from(msg)->uri;
+
+	/* parse Record-Route headers */
+	route.s = s = route_buffer; route.len = 0;
+	fproxy_lr = 0;
+	next_hop = empty_param;
+
+	p_hdr = msg->record_route;
+	if(p_hdr) {
+		if (p_hdr->parsed==0) {
+			if (rmode==MODE_ONFAILURE)
+				parse_flags |= TWRITE_RR_PARSED;
+			if ( parse_rr(p_hdr) ) {
+				LOG(L_ERR,"ERROR:tm:t_write_req: while parsing "
+					"'Record-Route:' header\n");
+				goto error2;
+			}
+		}
+		record_route = (rr_t*)p_hdr->parsed;
+	} else {
+		record_route = 0;
+	}
+
+	if( record_route ) {
+		if ( (tmp_s.s=find_not_quoted(&record_route->nameaddr.uri,';'))!=0 &&
+		tmp_s.s+1!=record_route->nameaddr.uri.s+
+		record_route->nameaddr.uri.len) {
+			/* Parse all parameters */
+			tmp_s.len = record_route->nameaddr.uri.len - (tmp_s.s-
+				record_route->nameaddr.uri.s);
+			if (parse_params( &tmp_s, CLASS_URI, &hooks, 
+			&record_route->params) < 0) {
+				LOG(L_ERR,"ERROR:tm:t_write_req: error while parsing "
+					"record route uri params\n");
+				goto error3;
+			}
+			fproxy_lr = (hooks.uri.lr != 0);
+			DBG("DEBUG:tm:t_write_req: record_route->nameaddr.uri: %.*s\n",
+				record_route->nameaddr.uri.len,record_route->nameaddr.uri.s);
+			if(fproxy_lr){
+				DBG("DEBUG:tm:t_write_req: first proxy has loose routing.\n");
+				copy_route(s,route.len,record_route->nameaddr.uri.s,
+					record_route->nameaddr.uri.len);
+			}
+		}
+		for(p_hdr = p_hdr->next;p_hdr;p_hdr = p_hdr->next) {
+			/* filter out non-RR hdr and empty hdrs */
+			if( (p_hdr->type!=HDR_RECORDROUTE) || p_hdr->body.len==0)
+				continue;
+
+			if(p_hdr->parsed==0) {
+				/* if we are in failure route and we have to parse,
+				 * remember to free before exiting */
+				if (rmode==MODE_ONFAILURE)
+					parse_flags |= TWRITE_RRTMP_PARSED;
+				if ( parse_rr(p_hdr) ){
+					LOG(L_ERR,"ERROR:tm:t_write_req: "
+						"while parsing <Record-route:> header\n");
+					goto error3;
+				}
+			}
+			for(record_route=p_hdr->parsed; record_route;
+			record_route=record_route->next){
+				DBG("DEBUG:tm:t_write_req: record_route->nameaddr.uri: %.*s\n",
+					record_route->nameaddr.uri.len,
+					record_route->nameaddr.uri.s);
+				copy_route(s,route.len,record_route->nameaddr.uri.s,
+					record_route->nameaddr.uri.len);
+			}
+			if (parse_flags&TWRITE_RRTMP_PARSED)
+				free_rr( ((rr_t**)&p_hdr->parsed) );
+		}
+
+		if(!fproxy_lr){
+			copy_route(s,route.len,str_uri.s,str_uri.len);
+			str_uri = ((rr_t*)msg->record_route->parsed)->nameaddr.uri;
+		} else {
+			next_hop = ((rr_t*)msg->record_route->parsed)->nameaddr.uri;
+		}
+	}
+
+	DBG("DEBUG:tm:t_write_req: calculated route: %.*s\n",
+		route.len,route.len ? route.s : "");
+	DBG("DEBUG:tm:t_write_req: next r-uri: %.*s\n",
+		str_uri.len,str_uri.len ? str_uri.s : "");
+
+	body = empty_param;
+	//email = empty_param;
+	//domain = empty_param;
+
+	if( REQ_LINE(msg).method_value==METHOD_INVITE ) {
+		/* get body */
+		if( (body.s = get_body(msg)) == 0 ){
+			LOG(L_ERR, "ERROR:tm:t_write_req: get_body failed\n");
+			goto error3;
+		}
+		body.len = msg->len - (body.s - msg->buf);
+
+		/* get email (if any) */
+		if ( (email_avp=search_avp( &email_attr ))!=0 &&
+		email_avp->val_type!=AVP_TYPE_STR ) {
+			LOG(L_WARN, "WARNING:tm:t_write_req: 'email' avp found but "
+				"not string -> ignoring it\n");
+			email_avp = 0;
+		}
+	}
+
+	/* additional headers */
+	hdrs.s=hdrs_buf; hdrs.len=0;
+	s = hdrs_buf;
+
+	if(hdrs.len+12+sizeof(flag_t)+1 >= HDRS_BUFFER_MAX){
+		LOG(L_ERR,"ERROR:tm:t_write_req: buffer overflow "
+			"while copying optional header\n");
+		goto error3;
+	}
+	append_str(s,"P-MsgFlags: ",12); hdrs.len += 12;
+	msg_flags_size = sizeof(flag_t);
+	int2reverse_hex(&s, &msg_flags_size, (int)msg->msg_flags);
+	hdrs.len += sizeof(flag_t) - msg_flags_size;
+	append_chr(s,'\n'); hdrs.len++;
+
+	for(p_hdr = msg->headers;p_hdr;p_hdr = p_hdr->next) {
+		if( !(p_hdr->type&HDR_OTHER) )
+			continue;
+
+		if(hdrs.len+p_hdr->name.len+p_hdr->body.len+4 >= HDRS_BUFFER_MAX){
+			LOG(L_ERR,"ERROR:tm:t_write_req: buffer overflow while "
+				"copying optional header\n");
+			goto error3;
+		}
+		append_str(s,p_hdr->name.s,p_hdr->name.len);
+		hdrs.len += p_hdr->name.len;
+		append_chr(s,':');append_chr(s,' '); hdrs.len+=2;
+		append_str(s,p_hdr->body.s,p_hdr->body.len);
+		hdrs.len += p_hdr->body.len;
+		if(*(s-1) != '\n'){
+			append_chr(s,'\n');
+			hdrs.len++;
+		}
+	}
+
+	append_chr(s,'.');
+	hdrs.len++;
+
+	eol_line(1).s = s = cmd_buf;
+	if(strlen(action)+12 >= CMD_BUFFER_MAX){
+		LOG(L_ERR,"ERROR:tm:t_write_req: buffer overflow while "
+			"copying command name\n");
+		goto error3;
+	}
+	append_str(s,"sip_request.",12);
+	append_str(s,action,strlen(action));
+	eol_line(1).len = s-eol_line(1).s;
+
+	eol_line(2)=REQ_LINE(msg).method;     /* method type */
+	eol_line(3)=msg->parsed_uri.user;     /* user from r-uri */
+	eol_line(4)=email_avp?(email_avp->val.str_val):empty_param;  /* email */
+	eol_line(5)=msg->parsed_uri.host;     /* domain */
+
+	eol_line(6)=msg->rcv.bind_address->address_str; /* dst ip */
+
+	eol_line(7)=msg->rcv.dst_port==SIP_PORT ?
+			empty_param : msg->rcv.bind_address->port_no_str; /* port */
+
+	/* r_uri ('Contact:' for next requests) */
+	eol_line(8)=msg->first_line.u.request.uri;
+
+	/* r_uri for subsequent requests */
+	eol_line(9)=str_uri.len?str_uri:empty_param;
+
+	eol_line(10)=get_from(msg)->body;		/* from */
+	eol_line(11)=msg->to->body;			/* to */
+	eol_line(12)=msg->callid->body;		/* callid */
+	eol_line(13)=get_from(msg)->tag_value;	/* from tag */
+	eol_line(14)=get_to(msg)->tag_value;	/* to tag */
+	eol_line(15)=get_cseq(msg)->number;	/* cseq number */
+
+	i2s=int2str(hash_index, &l);		/* hash:label */
+	if (l+1>=IDBUF_LEN) {
+		LOG(L_ERR, "ERROR:tm:t_write_req: too big hash\n");
+		goto error3;
+	}
+	memcpy(id_buf, i2s, l);
+	id_buf[l]=':';int_buflen=l+1;
+	i2s=int2str(label, &l);
+	if (l+1+int_buflen>=IDBUF_LEN) {
+		LOG(L_ERR, "ERROR:tm:t_write_req: too big label\n");
+		goto error3;
+	}
+	memcpy(id_buf+int_buflen, i2s, l);int_buflen+=l;
+	eol_line(16).s=id_buf;eol_line(16).len=int_buflen;
+
+	eol_line(17) = route.len ? route : empty_param;
+	eol_line(18) = next_hop;
+	eol_line(19) = hdrs;
+	eol_line(20) = body;
+
+	if ( write_to_fifo(vm_fifo, TWRITE_PARAMS)==-1 ) {
+		LOG(L_ERR, "ERROR:tm:t_write_req: write_to_fifo failed\n");
+		goto error3;
+	}
+
+	/* make sure that if voicemail does not initiate a reply
+	 * timely, a SIP timeout will be sent out */
+	if( add_blind_uac()==-1 ) {
+		LOG(L_ERR, "ERROR:tm:t_write_req: add_blind failed\n");
+		goto error3;
+	}
+
+	/* success */
+	ret = 1;
+
+error3:
+	if (parse_flags&TWRITE_RR_PARSED)
+		free_rr( ((rr_t**)&msg->record_route->parsed) );
+error2:
+	if (parse_flags&TWRITE_CONTACT_PARSED)
+		free_contact( ((contact_body_t**)&msg->contact->parsed) );
+error1:
+	if (parse_flags&TWRITE_FROM_PARSED) {
+		free_from( msg->from->parsed );
+		msg->from->parsed = 0;
+	}
+error:
+	/* 0 would lead to immediate script exit -- -1 returns
+	 * with 'false' to script processing */
+	return ret;
+}
+
+

+ 4 - 1
modules/tm/t_funcs.h

@@ -127,7 +127,6 @@ static void inline force_retr(struct retr_buf *rb)
 }
 
 
-int   tm_startup();
 void tm_shutdown();
 
 
@@ -157,6 +156,10 @@ void cleanup_localcancel_timers( struct cell *t );
 int t_relay_to( struct sip_msg  *p_msg ,
 	struct proxy_l *proxy, int proto, int replicate ) ;
 
+int t_write_req(struct sip_msg* msg,
+	char* vm_fifo, char* action);
+
+int init_twrite_lines();
 
 #endif
 

+ 129 - 4
modules/tm/t_reply.c

@@ -63,13 +63,9 @@
 
 
 #include <assert.h>
-#include "defs.h"
 
 #include "../../comp_defs.h"
-
 #include "../../hash_func.h"
-#include "t_funcs.h"
-#include "h_table.h"
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
@@ -82,7 +78,10 @@
 #include "../../data_lump.h"
 #include "../../data_lump_rpl.h"
 #include "../../usr_avp.h"
+#include "../../fifo_server.h"
 
+#include "defs.h"
+#include "h_table.h"
 #include "t_hooks.h"
 #include "t_funcs.h"
 #include "t_reply.h"
@@ -1279,3 +1278,129 @@ error:
 	return -1;
 }
 
+
+
+/*
+  Syntax:
+
+  ":vm_reply:[response file]\n
+  code\n
+  reason\n
+  trans_id\n
+  to_tag\n
+  [new headers]\n
+  \n
+  [Body]\n
+  .\n
+  \n"
+ */
+int fifo_t_reply( FILE *stream, char *response_file )
+{
+	int ret;
+	struct cell *trans;
+	char code[16];
+	char reason[128];
+	char trans_id[128];
+	char new_headers[MAX_HEADER];
+	char body[MAX_BODY];
+	char to_tag[128];
+	str sc;       /*  code */
+	str sr;       /*  reason */
+	str sti;      /*  trans_id */
+	str snh;      /*  new_headers */
+	str sb;       /*  body */
+	str sttag;    /*  to-tag */
+	unsigned int hash_index,label,icode;
+
+	sc.s=code;
+	sr.s=reason;
+	sti.s=trans_id;
+	snh.s=new_headers; sb.s=body;
+	sttag.s=to_tag; sttag.len=0;
+
+
+	/*  get the infos from FIFO server */
+
+	DBG("DEBUG: fifo_t_reply: ############### begin ##############\n");
+
+	if (!read_line(sc.s, 16, stream, &sc.len)||sc.len==0) {
+		LOG(L_ERR, "ERROR: fifo_t_reply: code expected\n");
+		fifo_reply(response_file, "400 fifo_t_reply: code expected");
+		return -1;
+	}
+
+	icode = str2s(sc.s,sc.len,&ret);
+	if(ret){
+		LOG(L_ERR, "ERROR: fifo_t_reply: code(int) has wrong format\n");
+		fifo_reply(response_file, "400 fifo_t_reply: code(int) has"
+			" wrong format");
+		return -1;
+	}
+
+	if(!read_line(sr.s, 128, stream, &sr.len)||sr.len==0){
+		LOG(L_ERR, "ERROR: fifo_t_reply: reason expected\n");
+		fifo_reply(response_file, "400 fifo_t_reply: reason expected");
+		return -1;
+	}
+	sr.s[sr.len]='\0';
+
+	if (!read_line(sti.s, 128, stream, &sti.len)||sti.len==0) {
+		LOG(L_ERR, "ERROR: fifo_t_reply: trans_id expected\n");
+		fifo_reply(response_file, "400 fifo_t_reply: trans_id expected");
+		return -1;
+	}
+	sti.s[sti.len]='\0';
+	DBG("DEBUG: fifo_t_reply: trans_id=%.*s\n",sti.len,sti.s);
+
+	if(sscanf(sti.s,"%u:%u", &hash_index, &label) != 2){
+		LOG(L_ERR, "ERROR: fifo_t_reply: invalid trans_id (%s)\n",sti.s);
+		fifo_reply(response_file, "400 fifo_t_reply: invalid trans_id");
+		return -1;
+	}
+	DBG("DEBUG: fifo_t_reply: hash_index=%u label=%u\n",hash_index,label);
+
+	if( !read_line(sttag.s,64,stream,&sttag.len) || sttag.len==0 ){
+		LOG(L_ERR, "ERROR: fifo_t_reply: to-tag expected\n");
+		fifo_reply(response_file, "400 fifo_t_reply: to-ta expected");
+		return -1;
+	}
+	sttag.s[sttag.len]='\0';
+	DBG("DEBUG: fifo_t_reply: to-tag: %.*s\n",sttag.len,sttag.s);
+
+	/* read the new headers */
+	if (!read_line_set(snh.s, MAX_HEADER, stream, &snh.len)) {
+		LOG(L_ERR, "ERROR: fifo_t_reply: while reading new headers\n");
+		fifo_reply(response_file, "400 fifo_t_reply: while reading "
+			"new headers");
+		return -1;
+    }
+	snh.s[snh.len]='\0';
+	DBG("DEBUG: fifo_t_reply: new headers: %.*s\n", snh.len, snh.s);
+
+	/*  body can be empty ... */
+	read_body(sb.s, MAX_BODY, stream, &sb.len);
+	sb.s[sb.len]='\0';
+	DBG("DEBUG: fifo_t_reply: body: <%.*s>\n", sb.len, sb.s);
+
+	if( t_lookup_ident(&trans,hash_index,label)<0 ) {
+		LOG(L_ERR,"ERROR: fifo_t_reply: lookup failed\n");
+		fifo_reply(response_file, "481 fifo_t_reply: no such transaction");
+		return -1;
+	}
+
+	/* it's refcounted now, t_reply_with body unrefs for me -- I can 
+	 * continue but may not use T anymore  */
+	ret = t_reply_with_body(trans,icode,reason,body,new_headers,to_tag);
+
+	if (ret<0) {
+		LOG(L_ERR, "ERROR: fifo_t_reply: reply failed\n");
+		fifo_reply(response_file, "500 fifo_t_reply: reply failed");
+		return -1;
+	}
+
+	fifo_reply(response_file, "200 fifo_t_reply succeeded\n");
+	DBG("DEBUG: fifo_t_reply: ################ end ##############\n");
+	return 1;
+}
+
+

+ 2 - 0
modules/tm/t_reply.h

@@ -141,5 +141,7 @@ void tm_init_tags();
 /* selects the branch for fwd-ing the reply */
 int t_pick_branch(int inc_branch, int inc_code, struct cell *t, int *res_code);
 
+int fifo_t_reply( FILE *stream, char *response_file );
+
 #endif
 

+ 9 - 0
modules/tm/tm.c

@@ -228,6 +228,8 @@ static cmd_export_t cmds[]={
 			REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
 	{"t_attr_to_uri",     t_attr_to_uri,            1, fixup_string2str,
 			REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE },
+	{"t_write_req",       t_write_req,              2, 0,
+			REQUEST_ROUTE | FAILURE_ROUTE },
 
 	/* not applicable from the script */
 	{"register_tmcb",      (cmd_function)register_tmcb,     NO_SCRIPT,   0, 0},
@@ -481,6 +483,12 @@ static int mod_init(void)
 		return -1;
 	}
 
+	if (register_fifo_cmd(fifo_t_reply, "t_reply", 0)<0) {
+		LOG(L_CRIT, "cannot register t_reply\n");
+		return -1;
+	}
+
+
 	/* building the hash table*/
 	if (!init_hash_table()) {
 		LOG(L_ERR, "ERROR: mod_init: initializing hash_table failed\n");
@@ -520,6 +528,7 @@ static int mod_init(void)
 	}
 
 	tm_init_tags();
+	init_twrite_lines();
 
 	/* register post-script clean-up function */
 	register_script_cb( w_t_unref, POST_SCRIPT_CB,