Browse Source

sorry -- I'm taking off in few minutes and wanted to commit not
to road-block dependent work -- I will post on that and provide
history of changes when I'm back

Jiri Kuthan 22 years ago
parent
commit
fde02f64a4

+ 1 - 1
Makefile

@@ -18,7 +18,7 @@ auto_gen=lex.yy.c cfg.tab.c   #lexx, yacc etc
 #include  source related defs
 include Makefile.sources
 
-override exclude_modules:=CVS cpl cpl-c ext radius_acc radius_auth snmp \
+override exclude_modules:=CVS cpl cpl-c ext radius_acc radius_auth snmp jabber sms pa extcmd msilo auth \
 	$(exclude_modules)
 static_modules=
 static_modules_path=$(addprefix modules/, $(static_modules))

+ 1 - 3
Makefile.defs

@@ -175,8 +175,6 @@ DEFS+= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \
 	 -DADAPTIVE_WAIT -DADAPTIVE_WAIT_LOOPS=1024 \
 	 -DDNS_IP_HACK \
 	 -DUSE_IPV6 \
-	 -DVOICE_MAIL \
-	 -D_TOTAG \
 	 -DUSE_TCP \
 	 -DDISABLE_NAGLE \
 	 -DDIGEST_DOMAIN \
@@ -197,7 +195,7 @@ DEFS+= -DNAME='"$(NAME)"' -DVERSION='"$(RELEASE)"' -DARCH='"$(ARCH)"' \
 	 #-DNO_LOG
 
 #PROFILE=  -pg #set this if you want profiling
-#mode = debug
+mode = debug
 ifeq ($(mode),)
 	mode = release
 endif

+ 1 - 1
examples/ctd.sh

@@ -55,7 +55,7 @@ else
 fi
 # address of user wishing to initiate conversation
 if [ -z "$1" ] ; then
-	URI="sip:113311@192.168.2.16"
+	URI="sip:44@192.168.2.16"
 	echo "caller unspecified -- taking default value $URI"
 else
 	URI="$1"

+ 27 - 0
modules/tm/h_table.c

@@ -43,12 +43,26 @@
 #include "t_cancel.h"
 #include "t_stats.h"
 
+static enum kill_reason kr;
+
 /* pointer to the big table where all the transaction data
    lives
 */
 
 static struct s_table*  tm_table;
 
+void set_kr( enum kill_reason _kr )
+{
+	if (kr!=0 && _kr!=0) {
+			DBG("DEBUG: set_kr: kill reason reset: "
+					"old=%d update=%d\n", kr, _kr );
+	}
+	kr|=_kr;
+}
+enum kill_reason get_kr() {
+	return kr;
+}
+
 void lock_hash(int i) 
 {
 	lock(&tm_table->entrys[i].mutex);
@@ -84,6 +98,7 @@ void free_cell( struct cell* dead_cell )
 	char *b;
 	int i;
 	struct sip_msg *rpl;
+	struct totag_elem *tt, *foo;
 
 	release_cell_lock( dead_cell );
 	shm_lock();
@@ -116,6 +131,15 @@ void free_cell( struct cell* dead_cell )
 		}
 	}
 
+	/* collected to tags */
+	tt=dead_cell->fwded_totags;
+	while(tt) {
+		foo=tt->next;
+		shm_free_unsafe(tt->tag.s);
+		shm_free_unsafe(tt);
+		tt=foo;
+	}
+
 	/* the cell's body */
 	shm_free_unsafe( dead_cell );
 
@@ -158,6 +182,9 @@ struct cell*  build_cell( struct sip_msg* p_msg )
 	/*fprintf(stderr,"before clone VIA |%.*s|\n",via_len(p_msg->via1),
 		via_s(p_msg->via1,p_msg));*/
 
+	callback_event(TMCB_REQUEST_IN, new_cell, p_msg, 
+			p_msg ? p_msg->REQ_METHOD : METHOD_UNDEF );
+
 	if (p_msg) {
 		new_cell->uas.request = sip_msg_cloner(p_msg);
 		if (!new_cell->uas.request)

+ 23 - 18
modules/tm/h_table.h

@@ -120,10 +120,16 @@ typedef struct ua_server
 	struct sip_msg   *request;
 	struct retr_buf  response;
 	unsigned int     status;
+	/* keep to-tags for local 200 replies for INVITE -- 
+	 * we need them for dialog-wise matching of ACKs;
+	 * the pointer shows to shmem-ed reply */
+	str				 local_totag;
 #ifdef _TOTAG
 	str              to_tag;
 #endif
+#ifdef _OBSO
 	unsigned int     isACKed;
+#endif
 }ua_server_type;
 
 
@@ -151,6 +157,11 @@ typedef struct ua_client
 }ua_client_type;
 
 
+struct totag_elem {
+	str tag;
+	short acked;
+	struct totag_elem *next;
+};
 
 /* transaction context */
 
@@ -205,7 +216,8 @@ typedef struct cell
 
 	/* number of forks */
 	int nr_of_outgoings;
-	/* nr of replied branch */
+	/* nr of replied branch; 0..MAX_BRANCHES=branch value,
+	 * -1 no reply, -2 local reply */
 	int relaied_reply_branch;
 	/* UA Server */
 	struct ua_server  uas;
@@ -221,16 +233,9 @@ typedef struct cell
 	   dropping when C timer hits
 	*/
 	int noisy_ctimer;
-	/* is it a local transaction ? */
+	/* is it a local UAC transaction ? */
 	int local;
 
-#ifdef _XWAIT
-	/* protection against reentering WAIT state */
-	ser_lock_t	wait_mutex;
-	/* has the transaction been put on wait status ? */
-	int on_wait;
-#endif
-
 	/* MD5checksum  (meaningful only if syn_branch=0 */
 	char md5[MD5_LEN];
 
@@ -239,7 +244,12 @@ typedef struct cell
 	short damocles;
 #endif
 	/* has the transaction been scheduled to die? */
-	enum kill_reason kr;
+/*	enum kill_reason kr; */
+
+	/* to-tags of 200/INVITEs which were received from downstream and 
+	 * forwarded or passed to UAC; note that there can be arbitrarily 
+	 * many due to downstream forking; */
+	struct totag_elem *fwded_totags;
 }cell_type;
 
 
@@ -266,14 +276,9 @@ struct s_table
 	struct entry   entrys[ TABLE_ENTRIES ];
 };
 
-inline static void set_kr( struct cell *t, enum kill_reason kr )
-{
-	if (t->kr!=0) {
-		LOG(L_ERR, "ERROR: set_kr: kill_reason reset: from=%d to=%d\n",
-		t->kr, kr);
-	}
-	t->kr|=kr;
-}
+
+void set_kr( enum kill_reason kr );
+enum kill_reason get_kr();
 
 struct s_table* get_tm_table();
 struct s_table* init_hash_table();

+ 1 - 1
modules/tm/t_funcs.c

@@ -101,7 +101,7 @@ void tm_shutdown()
 */
 int t_release_transaction( struct cell *trans )
 {
-	set_kr(trans,REQ_RLSD);
+	set_kr(REQ_RLSD);
 
 	reset_timer( & trans->uas.response.fr_timer );
 	reset_timer( & trans->uas.response.retr_timer );

+ 3 - 6
modules/tm/t_funcs.h

@@ -92,10 +92,12 @@ int send_pr_buffer( struct retr_buf *rb,
 	UNREF_UNSAFE(_T_cell); \
 	UNLOCK_HASH( (_T_cell)->hash_index ); })
 #define REF_UNSAFE(_T_cell) ({  (_T_cell)->ref_count++; })
+#ifdef _OBSO
 #define REF(_T_cell) ({ \
 	LOCK_HASH( (_T_cell)->hash_index ); \
 	REF_UNSAFE(_T_cell); \
 	UNLOCK_HASH( (_T_cell)->hash_index ); })
+#endif
 #define INIT_REF_UNSAFE(_T_cell) (_T_cell)->ref_count=1
 #define IS_REFFED_UNSAFE(_T_cell) ((_T_cell)->ref_count!=0)
 
@@ -111,19 +113,14 @@ void tm_shutdown();
 int  t_add_transaction( struct sip_msg* p_msg  );
 
 
-/* returns 1 if everything was OK or -1 for error
- */
+/* returns 1 if everything was OK or -1 for error */
 int t_release_transaction( struct cell *trans );
 
 
-/* int forward_serial_branch(struct cell* Trans,int branch); */
-int t_put_on_wait(  struct cell  *Trans  );
 int get_ip_and_port_from_uri( str* uri , unsigned int *param_ip,
 	unsigned int *param_port);
 
 
-int t_newtran( struct sip_msg* p_msg );
-
 void put_on_wait(  struct cell  *Trans  );
 
 void start_retr( struct retr_buf *rb );

+ 41 - 2
modules/tm/t_fwd.c

@@ -75,7 +75,7 @@ char *print_uac_request( struct cell *t, struct sip_msg *i_req,
 	i_req->new_uri=*uri;
 
 	/* ... give apps a chance to change things ... */
-	callback_event( TMCB_REQUEST_OUT, t, i_req, -i_req->REQ_METHOD);
+	callback_event( TMCB_REQUEST_FWDED, t, i_req, -i_req->REQ_METHOD);
 
 	/* ... and build it now */
 	buf=build_req_buf_from_sip_req( i_req, len, send_sock, i_req->rcv.proto );
@@ -110,6 +110,45 @@ error01:
 	return shbuf;
 }
 
+/* introduce a new uac, which is blind -- it only creates the
+   data structures and starts FR timer, but that's it; it does
+   not print messages and send anything anywhere; that is good
+   for FIFO apps -- the transaction must look operationally
+   and FR must be ticking, whereas the request is "forwarded"
+   using a non-SIP way and will be replied the same way
+*/
+int add_blind_uac( /*struct cell *t*/ )
+{
+	unsigned short branch;
+	struct cell *t;
+
+	t=get_t();
+	if (t==T_UNDEFINED || !t ) {
+		LOG(L_ERR, "ERROR: add_blind_uac: no transaction context\n");
+		return -1;
+	}
+
+	branch=t->nr_of_outgoings;	
+	if (branch==MAX_BRANCHES) {
+		LOG(L_ERR, "ERROR: add_blind_uac: "
+			"maximum number of branches exceeded\n");
+		return -1;
+	}
+	/* make sure it will be replied */
+	t->noisy_ctimer=1; 
+	t->nr_of_outgoings++;
+	/* start FR timer -- protocol set by default to PROTO_NONE,
+       which means retransmission timer will not be started
+    */
+	start_retr(&t->uac[branch].request);
+	/* we are on a timer -- don't need to put on wait on script
+	   clean-up	
+	*/
+	set_kr(REQ_FWDED); 
+
+	return 1; /* success */
+}
+
 /* introduce a new uac to transaction; returns its branch id (>=0)
    or error (<0); it doesn't send a message yet -- a reply to it
    might interfere with the processes of adding multiple branches
@@ -343,7 +382,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
 	/* make -Wall happy */
 	current_uri.s=0;
 
-	set_kr(t, REQ_FWDED);
+	set_kr(REQ_FWDED);
 
 	if (p_msg->REQ_METHOD==METHOD_CANCEL) {
 		t_invite=t_lookupOriginalT(  p_msg );

+ 2 - 0
modules/tm/t_fwd.h

@@ -40,6 +40,7 @@
 #include "../../proxy.h"
 
 typedef int (*tfwd_f)(struct sip_msg* p_msg , struct proxy_l * proxy );
+typedef int (*taddblind_f)( /*struct cell *t */ );
 
 int t_replicate(struct sip_msg *p_msg, struct proxy_l * proxy, int proto);
 char *print_uac_request( struct cell *t, struct sip_msg *i_req,
@@ -48,6 +49,7 @@ void e2e_cancel( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell
 int e2e_cancel_branch( struct sip_msg *cancel_msg, struct cell *t_cancel, struct cell *t_invite, int branch );
 int add_uac(	struct cell *t, struct sip_msg *request, str *uri,
 				struct proxy_l *proxy, int proto );
+int add_blind_uac( /* struct cell *t */ );
 int t_forward_nonack( struct cell *t, struct sip_msg* p_msg,
 						struct proxy_l * p, int proto);
 int t_forward_ack( struct sip_msg* p_msg );

+ 82 - 70
modules/tm/t_hooks.h

@@ -35,83 +35,95 @@
 struct sip_msg;
 struct cell;
 
-typedef enum { TMCB_REPLY,  TMCB_E2EACK, TMCB_REPLY_IN, 
-	TMCB_REQUEST_OUT, TMCB_LOCAL_COMPLETED, TMCB_ON_NEGATIVE,
-	TMCB_END } tmcb_type;
+typedef enum { 
+		/* input events */
+		TMCB_RESPONSE_IN=1, TMCB_REQUEST_IN, TMCB_E2EACK_IN, 
+		/* routing decisions in progress */
+		TMCB_REQUEST_FWDED, TMCB_RESPONSE_FWDED, TMCB_ON_FAILURE,
+		/* completion events */
+		TMCB_RESPONSE_OUT, TMCB_LOCAL_COMPLETED, 
+		TMCB_END } tmcb_type;
 
 /* 
-	TMCB_REPLY	-  a reply has been sent out
-	  no chance to change anything in the message; 
-	  still good enough for many uses, such as accounting
-	  of completed transactions; note well that the message
-	  passed to the callback may also have value FAKED_REPLY,
-	  i.e., refering to it will segfault
-	TMCB_REPLY_IN - a reply was received and is about to be forwarded;
-	  compared to TMCB_REPLY, it is a very internal callback and
-	  you should use it with lot of caution
-	  - it allows you to change the message (called before printing
-	    the relayed message)
-	  - it is called from a reply lock -- it is mroe dangerous and
-	    anything you do makes the processes spend more time in
-	    the lock, decreasing overall performance
-	  - is is called only for replies >100, <300 (final replies
-	    might be cached on forking, stored in shmem -- then, there
-		is no more easy way to change messages)
-	  - as it is called before printing and forwarding, there is
-	    no guarantee the message will be sent out -- either can
-	    fail
-
-		Note: none of the reply callbacks will be evoked if
-		"silent C timer" hits. Silent C timer is a feature which
-		prevents cancellation of a call in progress by a server
-		in the middle, when C timer expires. On one side, 
-		INVITE transactional state cannot be kept for ever,
-		on the other side you want to allow long ringing 
-		uninterrupted by a proxy server. The silent_c feature
-		-- if circumstances allow -- simply discards transaction
-		state when C timer hits, the transaction can then complete
-		statelessly. Then, however, the stateful callback will
-		NOT be called. If you do not wish this behaviour (e.g.,
-		for sake of transaction accounting, in which you do
-		not desire users to wait until silent C hits and
-		eventually complete an unaccounted transaction), turn
-		silent C off either globaly (TM option "noisy_ctimer"
-		set to 1) or for a specific transaction (you can for
-		example set the transaction member "noisy_timer"
-		from request callback.)
-
-	TMCB_E2EACK - presumably, an end2end ACK was received and
-		is about to be processed statelessly; you better don't
-	    use this callback as there is no reliable way to match
-	    an e2e ACK to an INVITE transaction, we just try it for
-	    those, who believe they can't live without knowing about
-	    the ACK; There are more reasons why the e2e ACK callback
-	    is never triggered: 1) the e2eACK does not pass the server
-	    at all 2) the e2e ACK does not match an INVITE transaction
-		because its r-uri or via is different
-	TMCB_REQUEST_OUT - a request was received and is about to be fwd-ed;
-		it is not called on retransmissions; it is called prior to
-		printing the relayed message, i.e., changes to it can
-		be done
-	TMCB_LOCAL_COMPLETED - a local transaction completed; note that
-	    the callback parameter may be FAKED_REPLY
-	TMCB_MISSED -- transaction was replied with a negative value;
-		called from within a REPLY_LOCK, message may be FAKED_REPLY
-	TMCB_ON_NEGATIVE -- called whenever a transaction is about to complete
-	    with a negative result; it's a great time to introduce a new
-	    uac (serial forking) or change the reply code; be cautions
-	    though -- it is called from within REPLY_LOCK and careless
-	    usage of the callback can easily result in a deadlock; msg
-	    is always 0 (callback refers to whole transaction and not
-	    to individual message), code is the currently lowest status
-	    code
-	TMCB_END	- just a bumper
+ *  Caution: most of the callbacks work with shmem-ized messages
+ *  which you can no more change (e.g., lumps are fixed). Most
+ *  reply-processing callbacks are also called from a mutex,
+ *  which may cause deadlock if you are not careful. Also, reply
+ *  callbacks may pass the value of FAKED_REPLY messages, which
+ *  is a non-dereferencable pointer indicating that no message
+ *  was received and a timer hit instead.
+ *
+ *  Callback description:
+ *  ---------------------
+ *
+ * TMCB_REQUEST_IN -- a brand-new request was received and is
+ * about to establish transaction; it is not yet cloned and
+ * lives in pkg mem -- your last chance to mangle it before
+ * it gets shmem-ized (then, it's read-only); it's called from
+ * HASH_LOCK, so be careful. It is guaranteed not to be
+ * a retransmission.
+ *
+ * TMCB_RESPONSE_IN -- a brand-new reply was received which matches
+ * an existing transaction. It may or may not be a retranmisssion.
+ *
+ *  TMCB_RESPONSE_OUT -- a final reply was sent out (eiter local 
+ *  or proxied) -- there is nothing more you can change from
+ *  the callback, it is good for accounting-like uses.
+ *
+ *    Note: the message passed to callback may also have
+ *    value FAKED_REPLY (like other reply callbacks) which
+ *    indicates a psedo_reply caused by a timer. Check for
+ *    this value before derefing -- you will cause a segfault
+ *    otherwise.
+ *
+ *  TMCB_ON_FAILURE -- called on receipt of a reply or timer;
+ *  it means all branches completed with a failure; that's 
+ *  a chance for example to add new transaction branches
+ *
+ *  TMCB_RESPONSE_FWDED -- called when a reply is about to be
+ *  forwarded; it is called after a message is received but before
+ *  a message is sent out: it is called when the decision is 
+ *  made to forward a reply; it is parametrized by pkg message 
+ *  which caused the transaction to complete (which is not 
+ *  necessarily the same which will be forwarded). As forwarding
+ *  has not been executed and may fail, there is no guarentee
+ *  a reply will be successfuly sent out at this point of time.
+ *
+ *     Note: TMCB_REPLY_ON_FAILURE and TMCB_REPLY_FWDED are
+ *     called from reply mutex which is used to deterministically
+ *     process multiple replies received in parallel. A failure
+ *     to set the mutex again or stay too long in the callback
+ *     may result in deadlock.
+ *
+ *     Note: the reply callbacks will not be evoked if "silent
+ *     C-timer hits". That's a feature to clean transactional
+ *     state from a proxy quickly -- transactions will then
+ *     complete statelessly. If you wish to disable this
+ *     feature, either set the global option "noisy_ctimer"
+ *     to 1, or set t->noisy_ctimer for selected transaction.
+ *
+ *  TMCB_E2EACK_IN -- called when an ACK belonging to a proxied
+ *  INVITE transaction completed with 200 arrived. Note that
+ *  because it can be only dialog-wise matched, only the first
+ *  transaction occurence will be matched with spirals. If
+ *  record-routing is not enabled, you will never receive the
+ *  ACK and the callback will be never triggered.
+ *
+ *
+ *  TMCB_REQUEST_FWDED -- request is being forwarded out. It is 
+ *  called before a message is forwarded and it is your last
+ *  chance to change its shape. 
+ *
+ *  TMCB_LOCAL COMPLETED -- final reply for localy initiated
+ *  transaction arrived. Message may be FAKED_REPLY.
+ *
+ *  TMCB_END	- just a bumper
 
 	see the 'acc' module for an example of callback usage
 
 	note that callbacks MUST be installed before forking
     (callback lists do not live in shmem and have no access
-	protection)
+	protection), i.e., at best from mod_init functions.
 */
 
 typedef void (transaction_cb) ( struct cell* t, struct sip_msg* msg, 

+ 275 - 126
modules/tm/t_lookup.c

@@ -71,6 +71,7 @@
 #include "../../dprint.h"
 #include "../../config.h"
 #include "../../parser/parser_f.h"
+#include "../../parser/parse_from.h"
 #include "../../ut.h"
 #include "../../timer.h"
 #include "../../hash_func.h"
@@ -141,54 +142,178 @@ struct cell *get_t() { return T; }
 void set_t(struct cell *t) { T=t; }
 void init_t() {global_msg_id=0; set_t(T_UNDEFINED);}
 
+static inline int parse_dlg( struct sip_msg *msg )
+{
+	if (parse_headers(msg, HDR_FROM | HDR_CSEQ | HDR_TO, 0)==-1) {
+		LOG(L_ERR, "ERROR: tid_matching: From or Cseq or To invalid\n");
+		return 0;
+	}
+	if (parse_from_header(msg)==-1) {
+		LOG(L_ERR, "ERROR: tid_matching: From broken\n");
+		return 0;
+	}
+	/* To is automatically parsed through HDR_TO in parse bitmap,
+	 * we don't need to worry about it now
+	if (parse_to_header(msg)==-1) {
+		LOG(L_ERR, "ERROR: tid_matching: To broken\n");
+		return 0;
+	}
+	*/
+	return 1;
+}
+
+/* is the ACK (p_msg) in p_msg dialog-wise equal to the INVITE (t_msg) 
+ * except to-tags? */
+static inline int partial_dlg_matching(struct sip_msg *t_msg, struct sip_msg *p_msg)
+{
+	struct to_body *inv_from;
+
+	if (!EQ_LEN(callid)) return 0;
+	if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
+		return 0;
+	inv_from=get_from(t_msg);
+	if (!inv_from) {
+		LOG(L_ERR, "ERROR: partial_dlg_matching: INV/From not parsed\n");
+		return 0;
+	}
+	if (inv_from->tag_value.len!=get_from(p_msg)->tag_value.len)
+		return 0;
+	if (!EQ_STR(callid)) 
+		return 0;
+	if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,
+			get_cseq(p_msg)->number.len)!=0)
+		return 0;
+	if (memcmp(inv_from->tag_value.s, get_from(p_msg)->tag_value.s,
+			get_from(p_msg)->tag_value.len)!=0)
+		return 0;
+	return 1;
+}
+
+/* are to-tags in ACK/200 same as those we sent out? */
+static inline int dlg_matching(struct cell *p_cell, struct sip_msg *ack )
+{
+	if (get_to(ack)->tag_value.len!=p_cell->uas.local_totag.len)
+		return 0;
+	if (memcmp(get_to(ack)->tag_value.s,p_cell->uas.local_totag.s,
+				p_cell->uas.local_totag.len)!=0)
+		return 0;
+	return 1;
+}
+
+static inline int ack_matching(struct cell *p_cell, struct sip_msg *p_msg) 
+{
+	/* partial dialog matching -- no to-tag, only from-tag, 
+	 * callid, cseq number ; */
+	if (!partial_dlg_matching(p_cell->uas.request, p_msg)) 
+		return 0;
+
+  	/* if this transaction is proxied (as opposed to UAS) we're
+	 * done now -- we ignore to-tags; the ACK simply belongs to
+	 * this UAS part of dialog, whatever to-tag it gained
+	 */
+	if (p_cell->relaied_reply_branch!=-2) {
+		return 2; /* e2e proxied ACK */
+	}
+	/* it's a local dialog -- we wish to verify to-tags too */
+	if (dlg_matching(p_cell, p_msg)) {
+		return 1;
+	}
+	return 0;
+}
+
+/* branch-based transaction matching */
+static inline int via_matching( struct via_body *inv_via, 
+				struct via_body *ack_via )
+{
+	if (inv_via->tid.len!=ack_via->tid.len)
+		return 0;
+	if (memcmp(inv_via->tid.s, ack_via->tid.s,
+				ack_via->tid.len)!=0)
+		return 0;
+	/* ok, tid matches -- now make sure that the
+	 * originater matches too to avoid confusion with
+	 * different senders generating the same tid
+	 */
+	if (inv_via->host.len!=ack_via->host.len)
+		return 0;;
+	if (memcmp(inv_via->host.s, ack_via->host.s,
+			ack_via->host.len)!=0)
+		return 0;
+	if (inv_via->port!=ack_via->port)
+		return 0;
+	if (inv_via->transport.len!=ack_via->transport.len)
+		return 0;
+	if (memcmp(inv_via->transport.s, ack_via->transport.s,
+			ack_via->transport.len)!=0)
+		return 0;
+	/* everything matched -- we found it */
+	return 1;
+}
+
 
 /* transaction matching a-la RFC-3261 using transaction ID in branch
- * (the function assumes there is magic cookie in branch) */
+   (the function assumes there is magic cookie in branch) 
+   It returns:
+	 2 if e2e ACK for a proxied transaction found
+     1  if found (covers ACK for local UAS)
+	 0  if not found (trans undefined)
+*/
 
-static struct cell *tid_matching( int hash_index, 
-		struct via_body *via1, 
-		enum request_method skip_method)
+static int matching_3261( struct sip_msg *p_msg, struct cell **trans,
+			enum request_method skip_method)
 {
 	struct cell *p_cell;
 	struct sip_msg  *t_msg;
+	struct via_body *via1;
+	int is_ack;
+	int dlg_parsed;
+	int ret;
 
-
+	via1=p_msg->via1;
+	is_ack=p_msg->REQ_METHOD==METHOD_ACK;
+	dlg_parsed=0;
 	/* update parsed tid */
 	via1->tid.s=via1->branch->value.s+MCOOKIE_LEN;
 	via1->tid.len=via1->branch->value.len-MCOOKIE_LEN;
 
-	for ( p_cell = get_tm_table()->entrys[hash_index].first_cell;
+	for ( p_cell = get_tm_table()->entrys[p_msg->hash_index].first_cell;
 		p_cell; p_cell = p_cell->next_cell ) 
 	{
 		t_msg=p_cell->uas.request;
 		if (skip_method & t_msg->REQ_METHOD)
 			continue;
-		if (t_msg->via1->tid.len!=via1->tid.len)
-			continue;
-		if (memcmp(t_msg->via1->tid.s, via1->tid.s,
-				via1->tid.len)!=0)
-			continue;
-		/* ok, tid matches -- now make sure that the
-		 * originater matches too to avoid confusion with
-		 * different senders generating the same tid
-		 */
-		if (via1->host.len!=t_msg->via1->host.len)
-			continue;
-		if (memcmp(via1->host.s, t_msg->via1->host.s,
-					via1->host.len)!=0)
-			continue;
-		if (via1->port!=t_msg->via1->port)
-			continue;
-		if (via1->transport.len!=t_msg->via1->transport.len)
+
+		/* dialog matching needs to be applied for ACK/200s */
+		if (is_ack && p_cell->uas.status<300) {
+			/* make sure we have parsed all things we need for dialog
+			 * matching */
+			if (!dlg_parsed) {
+				dlg_parsed=1;
+				if (!parse_dlg(p_msg)) {
+					LOG(L_ERR, "ERROR: tid_matching: dlg parsing failed\n");
+					return 0;
+				}
+			}
+			ret=ack_matching(p_cell /* t w/invite */, p_msg /* ack */);
+			if (ret>0) {
+				*trans=p_cell;
+				return ret; /* 2: e2e proxied ACK, 1 e2e UAS ACK */
+			}
+			/* this ACK is neither local "negative" one, nor a proxied
+			 * end-2-end one, nor an end-2-end one for a UAS transaction
+			 * -- we failed to match */
 			continue;
-		if (memcmp(via1->transport.s, t_msg->via1->transport.s,
-					via1->transport.len)!=0)
+		}
+		/* now real tid matching occurs  for negative ACKs and any 
+	 	 * other requests */
+		if (!via_matching(t_msg->via1 /* inv via */, via1 /* ack */ ))
 			continue;
 		/* all matched -- we found the transaction ! */
 		DBG("DEBUG: RFC3261 transaction matched, tid=%.*s\n",
 			via1->tid.len, via1->tid.s);
 
-		return p_cell;
+		*trans=p_cell;
+		return 1;
 	}
 	/* :-( ... we didn't find any */
 	DBG("DEBUG: RFC3261 transaction matching failed\n");
@@ -209,6 +334,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 	struct sip_msg  *t_msg;
 	int ret;
 	struct via_param *branch;
+	int match_status;
 
 	/* parse all*/
 	if (check_transaction_quadruple(p_msg)==0)
@@ -221,8 +347,7 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 
 	/* start searching into the table */
 	if (!p_msg->hash_index)
-		p_msg->hash_index=hash( p_msg->callid->body , 
-			get_cseq(p_msg)->number ) ;
+		p_msg->hash_index=hash( p_msg->callid->body , get_cseq(p_msg)->number ) ;
 	isACK = p_msg->REQ_METHOD==METHOD_ACK;
 	DBG("t_lookup_request: start searching: hash=%d, isACK=%d\n",
 		p_msg->hash_index,isACK);
@@ -245,29 +370,20 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 			&& memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) {
 		/* huhuhu! the cookie is there -- let's proceed fast */
 		LOCK_HASH(p_msg->hash_index);
-		p_cell=tid_matching(p_msg->hash_index, p_msg->via1, 
-				/* skip transactions with different
-				 * method; otherwise CANCEL would 
-				 * match the previous INVITE trans.
-				 */
+		match_status=matching_3261(p_msg,&p_cell, 
+				/* skip transactions with different method; otherwise CANCEL would 
+	 	 		 * match the previous INVITE trans.  */
 				isACK ? ~METHOD_INVITE: ~p_msg->REQ_METHOD);
-		if (p_cell) {
-			/* ACK/200 */
-			if (isACK && p_cell->uas.status>=200 && p_cell->uas.status<300) {
-				/* perhaps there are some spirals on the synonym list, but
-				   it makes no sense to iterate the list until bitter end */
-				t_ack=p_cell;
-				ret=-2;
-				goto notfound;
-			}
-			/* all but 200/ACK */
-			goto found;
-		} 
-		/* new */
-		goto notfound;
+		switch(match_status) {
+				case 0:	goto notfound;	/* no match */
+				case 1:	goto found; 	/* match */
+				case 2:	goto e2e_ack;	/* e2e proxy ACK */
+		}
 	}
 
-	/* ok -- it's ugly old-fashioned transaction matching */
+	/* ok -- it's ugly old-fashioned transaction matching -- it is
+	 * a bit simplified to be fast -- we don't do all the comparisons
+	 * of parsed uri, which was simply too bloated */
 	DBG("DEBUG: proceeding to pre-RFC3261 transaction matching\n");
 
 	/* lock the whole entry*/
@@ -303,63 +419,39 @@ int t_lookup_request( struct sip_msg* p_msg , int leave_new_locked )
 			/* ACK's relate only to INVITEs */
 			if (t_msg->REQ_METHOD!=METHOD_INVITE) continue;
 
+			/* From|To URI , CallID, CSeq # must be always there */
 			/* compare lengths now */
 			if (!EQ_LEN(callid)) continue;
 			/* CSeq only the number without method ! */
 			if (get_cseq(t_msg)->number.len!=get_cseq(p_msg)->number.len)
 				continue;
 			if (! EQ_LEN(from)) continue;
-			/* To only the uri and ... */
+			/* To only the uri -- to many UACs screw up tags  */
 			if (get_to(t_msg)->uri.len!=get_to(p_msg)->uri.len)
 				continue;
-			/* don't care about to-tags -- many UAC screw them
-			 * up anyway, and it doesn't hurt if we ignore 
-			 * them */
-#ifdef ACKTAG
-			/* ... its to-tag compared to reply's tag */
-			if (p_cell->uas.to_tag.len!=get_to(p_msg)->tag_value.len)
-				continue;
-#endif
-
-			/* we first skip r-uri and Via and proceed with
-			   content of other header-fields */
-
-			if ( memcmp(t_msg->callid->body.s, p_msg->callid->body.s,
-				p_msg->callid->body.len)!=0) continue;
-			if ( memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,
+			if (!EQ_STR(callid)) continue;
+			if (memcmp(get_cseq(t_msg)->number.s, get_cseq(p_msg)->number.s,
 				get_cseq(p_msg)->number.len)!=0) continue;
 			if (!EQ_STR(from)) continue;
 			if (memcmp(get_to(t_msg)->uri.s, get_to(p_msg)->uri.s,
 				get_to(t_msg)->uri.len)!=0) continue;
-#ifdef ACKTAG
-			if (
-#ifdef _BUG
-				p_cell->uas.to_tag.len!=0 /* to-tags empty */ || 
-#endif
-				memcmp(p_cell->uas.to_tag.s, get_to(p_msg)->tag_value.s,
-				p_cell->uas.to_tag.len)!=0) continue;
-#endif
-	
-			/* ok, now only r-uri or via can mismatch; they must match
-			   for non-2xx; if it is a 2xx, we don't try to match
-			   (we might have checked that earlier to speed-up, but
-			   we still want to see a diagnosti message telling
-			   "this ACK presumably belongs to this 2xx transaction";
-			   might change in future); the reason is 2xx ACKs are
-			   a separate transaction which may carry different
-			   r-uri/via1 and is thus also impossible to match it
-			   uniquely to a spiraled transaction;
-			*/
-			if (p_cell->uas.status>=200 && p_cell->uas.status<300) {
-				DBG("DEBUG: an ACK hit a 2xx transaction (T=%p); "
-					"considered mismatch\n", p_cell );
-				/* perhaps there are some spirals on the synonym list, but
-				   it makes no sense to iterate the list until bitter end */
-				t_ack=p_cell;
-				ret=-2;
-				break;
+
+			/* it is e2e ACK/200 */
+			if (p_cell->uas.status<300) {
+				/* all criteria for proxied ACK are ok */
+				if (p_cell->relaied_reply_branch!=-2) 
+					goto e2e_ack;
+				/* it's a local UAS transaction */
+				if (dlg_matching(p_cell, p_msg))
+					goto found;
+				continue;
 			}
-			/* its for a >= 300 ... everything must match ! */
+
+			/* it is not an e2e ACK/200 -- perhaps it is 
+			 * local negative case; in which case we will want
+			 * more elements to match: r-uri and via; allow
+			 * mismatching r-uri as an config option for broken
+			 * UACs */
 			if (ruri_matching && ! EQ_REQ_URI_LEN ) continue;
 			if (! EQ_VIA_LEN(via1)) continue;
 			if (ruri_matching && !EQ_REQ_URI_STR) continue;
@@ -378,12 +470,21 @@ notfound:
 		UNLOCK_HASH(p_msg->hash_index);
 	}
 	DBG("DEBUG: t_lookup_request: no transaction found\n");
-	return ret;
+	return -1;
+
+e2e_ack:
+	t_ack=p_cell;	/* e2e proxied ACK */
+	set_t(0);
+	if (!leave_new_locked) {
+		UNLOCK_HASH(p_msg->hash_index);
+	}
+	DBG("DEBUG: t_lookup_request: e2e proxy ACK found\n");
+	return -2;
 
 found:
 	set_t(p_cell);
 	REF_UNSAFE( T );
-	set_kr(T, REQ_EXIST);
+	set_kr(REQ_EXIST);
 	UNLOCK_HASH( p_msg->hash_index );
 	DBG("DEBUG: t_lookup_request: transaction found (T=%p)\n",T);
 	return 1;
@@ -402,6 +503,7 @@ struct cell* t_lookupOriginalT(  struct sip_msg* p_msg )
 	unsigned int     hash_index;
 	struct sip_msg  *t_msg;
 	struct via_param *branch;
+	int ret;
 
 
 	/* start searching in the table */
@@ -423,12 +525,12 @@ struct cell* t_lookupOriginalT(  struct sip_msg* p_msg )
 			&& memcmp(branch->value.s,MCOOKIE,MCOOKIE_LEN)==0) {
 		/* huhuhu! the cookie is there -- let's proceed fast */
 		LOCK_HASH(hash_index);
-		p_cell=tid_matching(hash_index, p_msg->via1, 
+		ret=matching_3261(p_msg, &p_cell,
 				/* we are seeking the original transaction --
 				 * skip CANCEL transactions during search
 				 */
 				METHOD_CANCEL);
-		if (p_cell) goto found; else goto notfound;
+		if (ret==1) goto found; else goto notfound;
 	}
 
 	/* no cookies --proceed to old-fashioned pre-3261 t-matching */
@@ -658,6 +760,23 @@ int t_reply_matching( struct sip_msg *p_msg , int *p_branch )
 		REF_UNSAFE( T );
 		UNLOCK_HASH(hash_index);
 		DBG("DEBUG: t_reply_matching: reply matched (T=%p)!\n",T);
+		/* if this is a 200 for INVITE, we will wish to store to-tags to be
+		 * able to distuingish retransmissions later and not to call
+ 		 * TMCB_RESPONSE_OUT uselessly; we do it only if callbacks are
+		 * enabled -- except callback customers, nobody cares about 
+		 * retransmissions of multiple 200/INV or ACK/200s
+		 */
+		if (p_cell->is_invite && p_msg->REPLY_STATUS>=200 
+				&& p_msg->REPLY_STATUS<300 
+				&& ( (!p_cell->local && 
+					(callback_array[TMCB_RESPONSE_OUT]||
+						callback_array[TMCB_E2EACK_IN]))
+					|| (p_cell->local && callback_array[TMCB_LOCAL_COMPLETED]) )) {
+			if (parse_headers(p_msg, HDR_TO, 0)==-1) {
+				LOG(L_ERR, "ERROR: t_reply_matching: to parsing failed\n");
+			}
+		}
+		callback_event(TMCB_RESPONSE_IN, T, p_msg, p_msg->REPLY_STATUS);
 		return 1;
 	} /* for cycle */
 
@@ -675,10 +794,12 @@ nomatch2:
 
 
 
-/* Functions update T (T gets either a valid pointer in it or it equals zero) if no transaction
-  * for current message exists;
-  * it returns 1 if found, 0 if not found, -1 on error
-  */
+/* Determine current transaction
+ *
+ *                   Found      Not Found     Error (e.g. parsing)
+ *  Return Value     1          0             -1
+ *  T                ptr        0             T_UNDEFINED
+ */
 int t_check( struct sip_msg* p_msg , int *param_branch )
 {
 	int local_branch;
@@ -693,8 +814,20 @@ int t_check( struct sip_msg* p_msg , int *param_branch )
 		/* transaction lookup */
 		if ( p_msg->first_line.type==SIP_REQUEST ) {
 			/* force parsing all the needed headers*/
-			if (parse_headers(p_msg, HDR_EOH, 0 )==-1)
+			if (parse_headers(p_msg, HDR_EOH, 0 )==-1) {
+				LOG(L_ERR, "ERROR: t_check: parsing error\n");
 				return -1;
+			}
+			/* in case, we act as UAS for INVITE and reply with 200,
+			 * we will need to run dialog-matching for subsequent
+			 * ACK, for which we need From-tag; We also need from-tag
+			 * in case people want to have proxied e2e ACKs accounted
+			 */
+			if (p_msg->REQ_METHOD==METHOD_INVITE 
+							&& parse_from_header(p_msg)==-1) {
+				LOG(L_ERR, "ERROR: t_check: from parsing failed\n");
+				return -1;
+			}
 			t_lookup_request( p_msg , 0 /* unlock before returning */ );
 		} else {
 			/* we need Via for branch and Cseq method to distinguish
@@ -738,7 +871,7 @@ int t_check( struct sip_msg* p_msg , int *param_branch )
 			DBG("DEBUG: t_check: T previously sought and not found\n");
 	}
 
-	return ((T)?1:0) ;
+	return T ? (T==T_UNDEFINED ? -1 : 1 ) : 0;
 }
 
 int init_rb( struct retr_buf *rb, struct sip_msg *msg)
@@ -840,6 +973,21 @@ int t_newtran( struct sip_msg* p_msg )
 	if (lret<0) {
 		new_cell=0;
 		if ( p_msg->REQ_METHOD!=METHOD_ACK ) {
+			/* REVIEW */
+			/* for ACK-dlw-wise matching, we want From-tags */
+			if (p_msg->REQ_METHOD==METHOD_INVITE) {
+				if (parse_from_header(p_msg)<0) {
+					LOG(L_ERR, "ERROR: t_newtran: no valid From\n");
+					return E_BAD_REQ;
+				}
+			}
+			/* REVIEW */
+			/* make sure uri will be parsed before cloning */
+			if (parse_sip_msg_uri(p_msg)<0) {
+				LOG(L_ERR, "ERROR: t_new_tran: uri invalid\n");
+				return E_BAD_REQ;
+			}
+			
 			/* add new transaction */
 			new_cell = build_cell( p_msg ) ;
 			if  ( !new_cell ){
@@ -867,6 +1015,7 @@ int t_newtran( struct sip_msg* p_msg )
 
 				new_cell->method=new_cell->uas.request->first_line.u.request.method;
 				new_cell->is_invite=p_msg->REQ_METHOD==METHOD_INVITE;
+
 			}
 
 		}
@@ -875,7 +1024,9 @@ int t_newtran( struct sip_msg* p_msg )
 		if (lret==-2) {
 				REF_UNSAFE(t_ack);
 				UNLOCK_HASH(p_msg->hash_index);
-				callback_event( TMCB_E2EACK, t_ack, p_msg, p_msg->REQ_METHOD );
+				if (unmatched_totag(t_ack, p_msg)) {
+						callback_event( TMCB_E2EACK_IN, t_ack, p_msg, p_msg->REQ_METHOD );
+				}
 				UNREF(t_ack);
 		} else { /* not e2e ACK */
 			UNLOCK_HASH(p_msg->hash_index);
@@ -915,10 +1066,13 @@ int t_newtran( struct sip_msg* p_msg )
 
 int t_unref( struct sip_msg* p_msg  )
 {
+	enum kill_reason kr;
+
 	if (T==T_UNDEFINED || T==T_NULL_CELL)
 		return -1;
-	if (T->kr==0 
-		||(p_msg->REQ_METHOD==METHOD_ACK && !(T->kr & REQ_RLSD))) {
+	kr=get_kr();
+	if (kr==0 
+		||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD))) {
 		LOG(L_WARN, "WARNING: script writer didn't release transaction\n");
 		t_release_transaction(T);
 	}
@@ -927,7 +1081,6 @@ int t_unref( struct sip_msg* p_msg  )
 	return 1;
 }
 
-#ifdef VOICE_MAIL
 int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label)
 {
     struct cell* t;
@@ -947,14 +1100,13 @@ int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned
     return 1;
 }
 
-int t_lookup_ident(struct sip_msg** p_msg, unsigned int hash_index, unsigned int label)
+int t_lookup_ident(struct cell ** trans, unsigned int hash_index, unsigned int label)
 {
-    int ret = 0;
     struct cell* p_cell;
 
     if(hash_index >= TABLE_ENTRIES){
-	LOG(L_ERR,"ERROR: t_lookup_ident: invalid hash_index=%u\n",hash_index);
-	return -1;
+		LOG(L_ERR,"ERROR: t_lookup_ident: invalid hash_index=%u\n",hash_index);
+		return -1;
     }
 
     LOCK_HASH(hash_index);
@@ -963,21 +1115,23 @@ int t_lookup_ident(struct sip_msg** p_msg, unsigned int hash_index, unsigned int
     for ( p_cell = get_tm_table()->entrys[hash_index].first_cell;
 	  p_cell; p_cell = p_cell->next_cell ) 
     {
-	if(p_cell->label == label){
-	    ret = 1;
-	    break;
-	}
+		if(p_cell->label == label){
+			REF_UNSAFE(p_cell);
+    		UNLOCK_HASH(hash_index);
+			set_t(p_cell);
+			*trans=p_cell;
+			DBG("DEBUG: t_lookup_ident: transaction found\n");
+			return 1;
+		}
     }
+	
+	UNLOCK_HASH(hash_index);
+	set_t(0);
+	*trans=p_cell;
 
-    if(ret==1){
-	DBG("DEBUG: t_lookup_ident: transaction found\n");
-	*p_msg = p_cell->uas.request;
-    }
-    else
 	DBG("DEBUG: t_lookup_ident: transaction not found\n");
     
-    UNLOCK_HASH(hash_index);
-    return ret;
+    return -1;
 }
 
 int t_is_local(struct sip_msg* p_msg)
@@ -995,8 +1149,3 @@ int t_is_local(struct sip_msg* p_msg)
     
     return t->local;
 }
-
-#endif
-
-
-

+ 2 - 4
modules/tm/t_lookup.h

@@ -75,7 +75,6 @@ struct cell *get_t();
  * primarily set by lookup functions */
 void set_t(struct cell *t);
 
-#ifdef VOICE_MAIL
 
 #define T_GET_TI       "t_get_trans_ident"
 #define T_LOOKUP_IDENT "t_lookup_ident"
@@ -83,12 +82,11 @@ void set_t(struct cell *t);
 
 typedef int (*tislocal_f)(struct sip_msg*);
 typedef int (*tget_ti_f)(struct sip_msg*, unsigned int*, unsigned int*);
-typedef int (*tlookup_ident_f)(struct sip_msg**, unsigned int, unsigned int);
+typedef int (*tlookup_ident_f)(struct cell**, unsigned int, unsigned int);
 
 int t_is_local(struct sip_msg*);
 int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label);
-int t_lookup_ident(struct sip_msg** p_msg, unsigned int hash_index, unsigned int label);
-#endif
+int t_lookup_ident(struct cell** trans, unsigned int hash_index, unsigned int label);
 
 #endif
 

+ 219 - 121
modules/tm/t_reply.c

@@ -36,12 +36,15 @@
  * 2003-01-19  faked lump list created in on_reply handlers
  */
 
+
+#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"
@@ -73,6 +76,7 @@ static char *tm_tag_suffix;
 /* where to go if there is no positive reply */
 static int goto_on_negative=0;
 
+
 /* we store the reply_route # in private memory which is
    then processed during t_relay; we cannot set this value
    before t_relay creates transaction context or after
@@ -101,6 +105,88 @@ void tm_init_tags()
 		"SER-TM/tags", TM_TAG_SEPARATOR );
 }
 
+/* returns 0 if the message was previously acknowledged
+ * (i.e., no E2EACK callback is needed) and one if the
+ * callback shall be executed */
+int unmatched_totag(struct cell *t, struct sip_msg *ack)
+{
+	struct totag_elem *i;
+	str *tag;
+
+	if (parse_headers(ack, HDR_TO,0)==-1 || 
+				!ack->to ) {
+		LOG(L_ERR, "ERROR: ack_totag_set: To invalid\n");
+		return 1;
+	}
+	tag=&get_to(ack)->tag_value;
+	for (i=t->fwded_totags; i; i=i->next) {
+		if (i->tag.len==tag->len
+				&& memcmp(i->tag.s, tag->s, tag->len)==0) {
+			DBG("DEBUG: totag for e2e ACK found: %d\n", i->acked);
+			/* to-tag recorded, and an ACK has been received for it */
+			if (i->acked) return 0;
+			/* to-tag recorded, but this ACK came for the first time */
+			i->acked=1;
+			return 1;
+		}
+	}
+	/* surprising: to-tag never sighted before */
+	return 1;
+}
+
+
+/* append a newly received tag from a 200/INVITE to 
+ * transaction's set; (only safe if called from within
+ * a REPLY_LOCK); it returns 1 if such a to tag already
+ * exists
+ */
+inline static int update_totag_set(struct cell *t, struct sip_msg *ok)
+{
+	struct totag_elem *i, *n;
+	str *tag;
+	char *s;
+
+	if (!ok->to || !ok->to->parsed) {
+		LOG(L_ERR, "ERROR: update_totag_set: to not parsed\n");
+		return 0;
+	}
+	tag=&get_to(ok)->tag_value;
+	if (!tag->s) {
+		LOG(L_ERR, "ERROR: update_totag_set: no tag in to\n");
+		return 0;
+	}
+
+	for (i=t->fwded_totags; i; i=i->next) {
+		if (i->tag.len==tag->len
+				&& memcmp(i->tag.s, tag->s, tag->len) ==0 )
+			/* to tag already recorded */
+#ifdef XL_DEBUG
+			LOG(L_CRIT, "DEBUG: update_totag_set: totag retranmission\n");
+#else
+			DBG("DEBUG: update_totag_set: totag retranmission\n");
+#endif
+			return 1;
+	}
+	/* that's a new to-tag -- record it */
+	shm_lock();
+	n=(struct totag_elem*) shm_malloc_unsafe(sizeof(struct totag_elem));
+	s=(char *)shm_malloc_unsafe(tag->len);
+	shm_unlock();
+	if (!s || !n) {
+		LOG(L_ERR, "ERROR: update_totag_set: no  memory \n");
+		if (n) shm_free(n);
+		if (s) shm_free(s);
+		return 0;
+	}
+	memset(n, 0, sizeof(struct totag_elem));
+	memcpy(s, tag->s, tag->len );
+	n->tag.s=s;n->tag.len=tag->len;
+	n->next=t->fwded_totags;
+	t->fwded_totags=n;
+	DBG("DEBUG: update_totag_set: new totag \n");
+	return 0;
+}
+
 
 static char *build_ack(struct sip_msg* rpl,struct cell *trans,int branch,
 	unsigned int *ret_len)
@@ -315,7 +401,7 @@ static enum rps t_should_relay_response( struct cell *Trans , int new_code,
 		/* no more pending branches -- try if that changes after
 		   a callback
 		*/
-		callback_event( TMCB_ON_NEGATIVE, Trans, 
+		callback_event( TMCB_ON_FAILURE, Trans, 
 			lowest_b==branch?reply:Trans->uac[lowest_b].reply, 
 			lowest_s );
 
@@ -458,18 +544,7 @@ error:
 }
 
 
-#ifdef VOICE_MAIL
-static int _reply_light( struct cell *trans, char* buf, unsigned int len,
-			 unsigned int code, char * text, 
-			 char *to_tag, unsigned int to_tag_len, int lock );
 
-int t_reply_light( struct cell *t, char* buf, unsigned int len,
-		   unsigned int code, char * text,
-		   char *to_tag, unsigned int to_tag_len )
-{
-    return _reply_light( t, buf, len, code, text, to_tag, to_tag_len, 1 /* lock replies */ );
-}
-#endif
 
 int t_reply( struct cell *t, struct sip_msg* p_msg, unsigned int code, 
 	char * text )
@@ -484,69 +559,26 @@ int t_reply_unsafe( struct cell *t, struct sip_msg* p_msg, unsigned int code,
 }
 
 
-
-/* send a UAS reply
- * returns 1 if everything was OK or -1 for error
- */
-static int _reply( struct cell *trans, struct sip_msg* p_msg, 
-	unsigned int code, char * text, int lock )
+static inline void update_local_tags(struct cell *trans, 
+				struct bookmark *bm, char *dst_buffer,
+				char *src_buffer /* to which bm refers */)
 {
-#ifndef VOICE_MAIL
-	unsigned int len, buf_len=0;
-	char * buf;
-	struct retr_buf *rb;
-
-	branch_bm_t cancel_bitmap;
-#else
-	unsigned int len;
-	char * buf;
-#endif
-
-	if (code>=200) set_kr(trans,REQ_RPLD);
-	/*
-	buf = build_res_buf_from_sip_req(code,text,trans->uas.tag->s,
-		trans->uas.tag->len, trans->uas.request,&len);
-	*/
-#ifndef VOICE_MAIL
-	cancel_bitmap=0;
-#endif
-	/* compute the buffer in private memory prior to entering lock;
-	 * create to-tag if needed */
-	if (code>=180 && p_msg->to 
-			&& (get_to(p_msg)->tag_value.s==0 
-			    || get_to(p_msg)->tag_value.len==0)) {
-		calc_crc_suffix( p_msg, tm_tag_suffix );
-		buf = build_res_buf_from_sip_req(code,text, 
-				tm_tags, TOTAG_VALUE_LEN, 
-				p_msg,&len);
-#ifdef VOICE_MAIL
-
-		return _reply_light(trans,buf,len,code,text,
-				    tm_tags, TOTAG_VALUE_LEN,
-				    lock);
-#endif
-	} else {
-		buf = build_res_buf_from_sip_req(code,text, 0,0, /* no to-tag */
-			p_msg,&len);
-#ifdef VOICE_MAIL
-
-		return _reply_light(trans,buf,len,code,text,
-				    0,0, /* no to-tag */
-				    lock);
-#endif
+	if (bm->to_tag_val.s) {
+		trans->uas.local_totag.s=bm->to_tag_val.s-src_buffer+dst_buffer;
+		trans->uas.local_totag.len=bm->to_tag_val.len;
 	}
-	DBG("DEBUG: t_reply: buffer computed\n");
-#ifdef VOICE_MAIL
 }
 
+
 static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 			 unsigned int code, char * text, 
-			 char *to_tag, unsigned int to_tag_len, int lock )
+			 char *to_tag, unsigned int to_tag_len, int lock,
+			 struct bookmark *bm	)
 {
 	struct retr_buf *rb;
-	unsigned int buf_len=0;
-	branch_bm_t cancel_bitmap=0;
-#endif
+	unsigned int buf_len;
+	branch_bm_t cancel_bitmap;
+
 	if (!buf)
 	{
 		DBG("DEBUG: t_reply: response building failed\n");
@@ -560,6 +592,7 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 		goto error;
 	}
 
+	cancel_bitmap=0;
 	if (lock) LOCK_REPLIES( trans );
 	if (trans->is_invite) which_cancel(trans, &cancel_bitmap );
 	if (trans->uas.status>=200) {
@@ -567,6 +600,19 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 			" when a final %d was sent out\n", code, trans->uas.status);
 		goto error2;
 	}
+
+#ifdef _TOTAG
+	if(to_tag){
+	    trans->uas.to_tag.s = (char*)shm_resize( trans->uas.to_tag.s, to_tag_len );
+	    if(! trans->uas.to_tag.s ){
+			LOG(L_ERR, "ERROR: t_reply: cannot allocate shmem buffer\n");
+			goto error2; 
+	    }
+	    trans->uas.to_tag.len = to_tag_len;
+	    memcpy( trans->uas.to_tag.s, to_tag, to_tag_len );
+	}
+#endif
+
 	rb = & trans->uas.response;
 	rb->activ_type=code;
 
@@ -576,27 +622,17 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 	/* puts the reply's buffer to uas.response */
 	if (! rb->buffer ) {
 			LOG(L_ERR, "ERROR: t_reply: cannot allocate shmem buffer\n");
-			goto error2;
+			goto error3;
 	}
+	update_local_tags(trans, bm, rb->buffer, buf);
+
 	rb->buffer_len = len ;
 	memcpy( rb->buffer , buf , len );
-#ifdef VOICE_MAIL
-	if(to_tag){
-	    trans->uas.to_tag.s = (char*)shm_resize( trans->uas.to_tag.s, to_tag_len );
-	    if(! trans->uas.to_tag.s ){
-			LOG(L_ERR, "ERROR: t_reply: cannot allocate shmem buffer\n");
-			// Is it ok? or should i free rb->buffer also, 
-			// or will it be freed in free_cell() ?
-			goto error2; 
-	    }
-	    trans->uas.to_tag.len = to_tag_len;
-	    memcpy( trans->uas.to_tag.s, to_tag, to_tag_len );
-	}
-#endif
 	/* needs to be protected too because what timers are set depends
 	   on current transactions status */
 	/* t_update_timers_after_sending_reply( rb ); */
 	update_reply_stats( code );
+	trans->relaied_reply_branch=-2;
 	tm_stats->replied_localy++;
 	if (lock) UNLOCK_REPLIES( trans );
 	
@@ -609,7 +645,7 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 			if (trans->completion_cb) 
 				trans->completion_cb( trans, FAKED_REPLY, code, 0 /* empty param */);
 		} else {
-			callback_event( TMCB_REPLY, trans, FAKED_REPLY, code );
+			callback_event( TMCB_RESPONSE_OUT, trans, FAKED_REPLY, code );
 		}
 
 		cleanup_uac_timers( trans );
@@ -632,6 +668,13 @@ static int _reply_light( struct cell *trans, char* buf, unsigned int len,
 	DBG("DEBUG: t_reply: finished\n");
 	return 1;
 
+error3:
+#ifdef _TOTAG
+	if (totag) {
+		shm_free(trans->uas.to_tag.s);
+		trans->uas.to_tag.s=0;
+	}
+#endif
 error2:
 	if (lock) UNLOCK_REPLIES( trans );
 	pkg_free ( buf );
@@ -644,11 +687,47 @@ error:
 	return -1;
 }
 
+/* send a UAS reply
+ * returns 1 if everything was OK or -1 for error
+ */
+static int _reply( struct cell *trans, struct sip_msg* p_msg, 
+	unsigned int code, char * text, int lock )
+{
+	unsigned int len;
+	char * buf;
+	struct bookmark bm;
+
+	if (code>=200) set_kr(REQ_RPLD);
+	/* compute the buffer in private memory prior to entering lock;
+	 * create to-tag if needed */
+	if (code>=180 && p_msg->to 
+				&& (get_to(p_msg)->tag_value.s==0 
+			    || get_to(p_msg)->tag_value.len==0)) {
+		calc_crc_suffix( p_msg, tm_tag_suffix );
+		buf = build_res_buf_from_sip_req(code,text, 
+				tm_tags, TOTAG_VALUE_LEN, 
+				p_msg,&len, &bm);
+
+		return _reply_light(trans,buf,len,code,text,
+				    tm_tags, TOTAG_VALUE_LEN,
+				    lock, &bm);
+	} else {
+		buf = build_res_buf_from_sip_req(code,text, 0,0, /* no to-tag */
+			p_msg,&len, &bm);
+
+		return _reply_light(trans,buf,len,code,text,
+				    0,0, /* no to-tag */
+				    lock, &bm);
+	}
+	DBG("DEBUG: t_reply: buffer computed\n");
+}
+
 void set_final_timer( /* struct s_table *h_table, */ struct cell *t )
 {
 	if ( !t->local 
 		&& t->uas.request->REQ_METHOD==METHOD_INVITE 
-		&& t->uas.status>=300  ) {
+		&& (t->uas.status>=300 || 
+				(t->relaied_reply_branch==-2 && t->uas.status>=200) )) {
 			/* crank timers for negative replies */
 			start_retr( &t->uas.response );
 	} else put_on_wait(t);
@@ -706,6 +785,8 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 	unsigned int res_len;
 	int relayed_code;
 	struct sip_msg *relayed_msg;
+	struct bookmark bm;
+	int totag_retr;
 #ifdef _TOTAG
 	str	to_tag;
 #endif
@@ -718,6 +799,7 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 	buf=0;
 	relayed_msg=0;
 	relayed_code=0;
+	totag_retr=0;
 
 
 	/* remember, what was sent upstream to know whether we are
@@ -747,12 +829,12 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 		   anyway 
         */
 		if (msg_status<300 && branch==relay) {
-			callback_event( TMCB_REPLY_IN, t, p_msg, msg_status );
+			callback_event( TMCB_RESPONSE_FWDED, t, p_msg, msg_status );
 		}
 		/* try bulding the outbound reply from either the current
 	       or a stored message */
 		relayed_msg = branch==relay ? p_msg :  t->uac[relay].reply;
-		if (relayed_msg ==FAKED_REPLY) {
+		if (relayed_msg==FAKED_REPLY) {
 			tm_stats->replied_localy++;
 			relayed_code = branch==relay
 				? msg_status : t->uac[relay].last_received;
@@ -765,11 +847,11 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 						relayed_code,
 						error_text(relayed_code),
 						tm_tags, TOTAG_VALUE_LEN, 
-						t->uas.request, &res_len );
+						t->uas.request, &res_len, &bm );
 			} else {
 				buf = build_res_buf_from_sip_req( relayed_code,
 					error_text(relayed_code), 0,0, /* no to-tag */
-					t->uas.request, &res_len );
+					t->uas.request, &res_len, &bm );
 			}
 
 		} else {
@@ -804,6 +886,10 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 		}
 		uas_rb->buffer_len = res_len;
 		memcpy( uas_rb->buffer, buf, res_len );
+		if (relayed_msg==FAKED_REPLY) { /* to-tags for local replies */
+			update_local_tags(t, &bm, uas_rb->buffer, buf);
+		}
+		tm_stats->replied_localy++;
 #ifdef _TOTAG
 		/* to tag now */
 		if (relayed_code>=300 && t->is_invite) {
@@ -828,6 +914,13 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 		/* update the status ... */
 		t->uas.status = relayed_code;
 		t->relaied_reply_branch = relay;
+
+		if (t->is_invite && relayed_msg!=FAKED_REPLY
+				&& relayed_code>=200 && relayed_code < 300
+				&& (callback_array[TMCB_RESPONSE_OUT] ||
+						callback_array[TMCB_E2EACK_IN]))  {
+			totag_retr=update_totag_set(t, relayed_msg);
+		}
 	}; /* if relay ... */
 
 	UNLOCK_REPLIES( t );
@@ -837,7 +930,8 @@ enum rps relay_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 		SEND_PR_BUFFER( uas_rb, buf, res_len );
 		DBG("DEBUG: reply relayed. buf=%p: %.9s..., shmem=%p: %.9s\n", 
 			buf, buf, uas_rb->buffer, uas_rb->buffer );
-		callback_event( TMCB_REPLY, t, relayed_msg, relayed_code );
+		if (!totag_retr) 
+				callback_event( TMCB_RESPONSE_OUT, t, relayed_msg, relayed_code );
 		pkg_free( buf );
 	}
 
@@ -881,11 +975,13 @@ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 	enum rps reply_status;
 	struct sip_msg *winning_msg;
 	int winning_code;
+	int totag_retr;
 	/* branch_bm_t cancel_bitmap; */
 
 	/* keep warning 'var might be used un-inited' silent */	
 	winning_msg=0;
 	winning_code=0;
+	totag_retr=0;
 
 	*cancel_bitmap=0;
 
@@ -909,13 +1005,23 @@ enum rps local_reply( struct cell *t, struct sip_msg *p_msg, int branch,
 		}
 		t->uas.status = winning_code;
 		update_reply_stats( winning_code );
+		if (t->is_invite && winning_msg!=FAKED_REPLY 
+				&& winning_code>=200 && winning_code <300
+				&& (callback_array[TMCB_RESPONSE_OUT] ||
+						callback_array[TMCB_E2EACK_IN]))  {
+			totag_retr=update_totag_set(t, winning_msg);
+		}
+		
 	}
 	UNLOCK_REPLIES(t);
 	if (local_winner>=0 && winning_code>=200 ) {
 		DBG("DEBUG: local transaction completed\n");
-		callback_event( TMCB_LOCAL_COMPLETED, t, winning_msg, winning_code );
-		if (t->completion_cb) 
-			t->completion_cb( t, winning_msg, winning_code, 0 /* empty param */);
+		if (!totag_retr) {
+			callback_event( TMCB_LOCAL_COMPLETED, t, winning_msg, 
+				winning_code );
+			if (t->completion_cb) t->completion_cb( t, winning_msg, 
+						winning_code, 0 /* empty param */);
+		}
 	}
 	return reply_status;
 
@@ -1044,33 +1150,24 @@ done:
 	return 0;
 }
 
-#ifdef VOICE_MAIL
 
-#include <assert.h>
 
-int t_reply_with_body( struct sip_msg* p_msg, unsigned int code, char * text, char * body, char * new_header, char * to_tag )
+int t_reply_with_body( struct cell *trans, unsigned int code, 
+		char * text, char * body, char * new_header, char * to_tag )
 {
-    struct cell * t;
-    //char to_tag[64];
+
     str  s_to_tag,sb,snh;
     char* res_buf;
-    int res_len,ret;
-
-    /*  check if we have a transaction */
-    if (t_check(p_msg, 0)==-1) {
-	LOG(L_ERR,"ERROR: t_reply_with_body: no transaction found.\n");
-	return -1;
-    }
-
-    t=get_t();
-    assert(t);
+    int res_len;
+	int ret;
+	struct bookmark bm;
 
     s_to_tag.s = to_tag;
     if(to_tag)
-	s_to_tag.len = strlen(to_tag);
+		s_to_tag.len = strlen(to_tag);
 
-    // mark the transaction as replied
-    set_kr(t,REQ_RPLD);
+    /* mark the transaction as replied */
+    if (code>=200) set_kr(REQ_RPLD);
 
     /* compute the response */
     sb.s = body;
@@ -1078,21 +1175,22 @@ int t_reply_with_body( struct sip_msg* p_msg, unsigned int code, char * text, ch
     snh.s = new_header;
     snh.len = strlen(new_header);
 
-    res_buf = build_res_buf_with_body_from_sip_req(code,text, s_to_tag.s, s_to_tag.len,
-						   sb.s,sb.len,
-						   snh.s,snh.len,
-						   p_msg,&res_len);
+    res_buf = build_res_buf_with_body_from_sip_req(
+					code,text, s_to_tag.s, s_to_tag.len,
+		   			sb.s,sb.len,
+					snh.s,snh.len,
+					trans->uas.request,&res_len, &bm);
     
     DBG("t_reply_with_body: buffer computed\n");
     // frees 'res_buf' ... no panic !
-    ret = t_reply_light(t, res_buf, res_len, code, text,
-			s_to_tag.s, s_to_tag.len);
-
-    // TODO: i'm not sure i should do this here ...
-    if(t_unref(p_msg) == -1)
-	LOG(L_WARN,"WARNING: fifo_t_reply: could not unref transaction %p\n",t);
+    ret=_reply_light( trans, res_buf, res_len, code, text, 
+		s_to_tag.s, s_to_tag.len, 1 /* lock replies */, &bm );
+	/* this is ugly hack -- the function caller may wish to continue with
+	 * transction and I unref; however, there is now only one use from
+	 * vm/fifo_vm_reply and I'm currently to lazy to export UNREF; -jiri
+	 */
+	UNREF(trans);
+	return ret;
 
-    return ret;
 }
 
-#endif

+ 8 - 8
modules/tm/t_reply.h

@@ -54,17 +54,17 @@ enum rps {
 enum route_mode { MODE_REQUEST=1, MODE_ONREPLY_REQUEST };
 extern enum route_mode rmode;
 
+/* has this to-tag been never seen in previous 200/INVs? */
+int unmatched_totag(struct cell *t, struct sip_msg *ack);
+
 /* branch bitmap type */
 typedef unsigned int branch_bm_t;
 
 /* reply export types */
-typedef int (*treply_f)( struct sip_msg* p_msg,
-	unsigned int code, char * text );
-#ifdef VOICE_MAIL
-typedef int (*treply_wb_f)( struct sip_msg* p_msg,
+typedef int (*treply_f)(struct sip_msg * , unsigned int , char * );
+typedef int (*treply_wb_f)( struct cell* trans,
 	unsigned int code, char * text, char * body, 
 	char * new_header, char * to_tag);
-#endif
 
 #define LOCK_REPLIES(_t) lock(&(_t)->reply_mutex )
 #define UNLOCK_REPLIES(_t) unlock(&(_t)->reply_mutex )
@@ -88,16 +88,16 @@ int t_retransmit_reply( /* struct sip_msg * */  );
  * Warning: 'buf' and 'len' should already have been build.
  * returns 1 if everything was OK or -1 for erro
  */
-#ifdef VOICE_MAIL
 
+#ifdef _OBSO
 int t_reply_light( struct cell *trans, char* buf, unsigned int len,
 		   unsigned int code, char * text,
 		   char *to_tag, unsigned int to_tag_len);
+#endif
 
-int t_reply_with_body( struct sip_msg* p_msg, unsigned int code, 
+int t_reply_with_body( struct cell *trans, unsigned int code, 
 		       char * text, char * body, char * new_header, char * to_tag );
 
-#endif
 
 /* send a UAS reply
  * returns 1 if everything was OK or -1 for erro

+ 1 - 1
modules/tm/timer.c

@@ -336,7 +336,7 @@ inline static void final_response_handler( void *attr)
 	if (r_buf->activ_type>0) {
 #		ifdef EXTRA_DEBUG
 		if (t->uas.request->REQ_METHOD!=METHOD_INVITE
-			|| t->uas.status < 300 ) {
+			|| t->uas.status < 200 ) {
 			LOG(L_ERR, "ERROR: FR timer: uknown type reply buffer\n");
 			abort();
 		}

+ 13 - 13
modules/tm/tm.c

@@ -158,12 +158,11 @@ struct module_exports exports= {
 				"register_tmcb",
 				T_UAC_DLG,
 				"load_tm",
-#ifdef VOICE_MAIL
 				T_REPLY_WB,
 				T_IS_LOCAL,
 				T_GET_TI,
 				T_LOOKUP_IDENT,
-#endif
+				T_ADDBLIND,
 				"t_newdlg"
 			},
 	(cmd_function[]){
@@ -189,12 +188,11 @@ struct module_exports exports= {
 					(cmd_function) register_tmcb,
 					(cmd_function) t_uac_dlg,
 					(cmd_function) load_tm,
-#ifdef VOICE_MAIL
 					(cmd_function) t_reply_with_body,
 					(cmd_function) t_is_local,
 					(cmd_function) t_get_trans_ident,
 					(cmd_function) t_lookup_ident,
-#endif
+					(cmd_function) add_blind_uac,
 					w_t_newdlg,
 					},
 	(int[]){
@@ -219,12 +217,11 @@ struct module_exports exports= {
 				NO_SCRIPT /* register_tmcb */,
 				NO_SCRIPT /* t_uac_dlg */,
 				NO_SCRIPT /* load_tm */,
-#ifdef VOICE_MAIL
 				NO_SCRIPT /* t_reply_with_body */,
 				NO_SCRIPT /* t_is_local */,
 				NO_SCRIPT /* t_get_trans_ident */,
 				NO_SCRIPT /* t_lookup_ident */,
-#endif
+				NO_SCRIPT /* add_blind_uac */,
 				0 /* t_newdlg */
 			},
 	(fixup_function[]){
@@ -249,19 +246,15 @@ struct module_exports exports= {
 				0,						/* register_tmcb */
 				0,                                              /* t_uac_dlg */
 				0,						/* load_tm */
-#ifdef VOICE_MAIL
 				0, /* t_reply_with_body */
 				0, /* t_is_local */
 				0, /* t_get_trans_ident */
 				0, /* t_lookup_ident */
-#endif
+				0, /* add_blind_uac */
 				0						/* t_newdlg */
 	
 		},
-#ifdef VOICE_MAIL
-	4+
-#endif
-	14 + 8 /* *_(UDP|TCP) */,
+	5 /* voicemail */ + 14 + 8 /* *_(UDP|TCP) */,
 
 	/* ------------ exported variables ---------- */
 	(char *[]) { /* Module parameter names */
@@ -346,6 +339,9 @@ static int script_init( struct sip_msg *foo, void *bar)
 	*/
 	t_on_negative( 0 );
 
+	/* reset the kr status */
+	set_kr(0);
+
 	return 1;
 }
 
@@ -505,7 +501,11 @@ inline static int _w_t_forward_nonack(struct sip_msg* msg, char* proxy,
 									 char* _foo, int proto)
 {
 	struct cell *t;
-	if (t_check( msg , 0 )==-1) return -1;
+	if (t_check( msg , 0 )==-1) {
+		LOG(L_ERR, "ERROR: forward_nonack: "
+				"can't forward when no transaction was set up\n");
+		return -1;
+	}
 	t=get_t();
 	if ( t && t!=T_UNDEFINED ) {
 		if (msg->REQ_METHOD==METHOD_ACK) {

+ 4 - 2
modules/tm/tm_load.c

@@ -57,7 +57,6 @@ int load_tm( struct tm_binds *tmb)
 		LOG( L_ERR, LOAD_ERROR "'t_reply' not found\n");
 		return -1;
 	}
-#ifdef VOICE_MAIL
 	if (!(tmb->t_reply_with_body=(treply_wb_f)find_export(T_REPLY_WB, NO_SCRIPT)) ) {
 	        LOG( L_ERR, LOAD_ERROR "'t_reply' not found\n");
 		return -1;
@@ -74,7 +73,10 @@ int load_tm( struct tm_binds *tmb)
 	        LOG( L_ERR, LOAD_ERROR "'t_lookup_ident' not found\n");
 		return -1;
 	}
-#endif
+	if (!(tmb->t_addblind=(taddblind_f)find_export(T_ADDBLIND, NO_SCRIPT)) ) {
+	        LOG( L_ERR, LOAD_ERROR "'addblind' not found\n");
+		return -1;
+	}
 	if (!(tmb->t_forward_nonack=(tfwd_f)find_export(T_FORWARD_NONACK , 2)) ) {
 		LOG( L_ERR, LOAD_ERROR "'t_forward_nonack' not found\n");
 		return -1;

+ 12 - 12
modules/tm/tm_load.h

@@ -37,9 +37,7 @@
 #include "uac.h"
 #include "t_fwd.h"
 #include "t_reply.h"
-#ifdef VOICE_MAIL
-#    include "t_lookup.h"
-#endif
+#include "t_lookup.h"
 
 /* export not usable from scripts */
 #define NO_SCRIPT	-1
@@ -52,13 +50,16 @@
 #define T_RELAY_TCP "t_relay_tcp"
 #define T_UAC_DLG "t_uac_dlg"
 #define T_REPLY "t_reply"
-#ifdef VOICE_MAIL
 #define T_REPLY_WB "t_reply_with_body"
-#endif
+#define T_ADDBLIND "t_add_blind"
 #define T_REPLY_UNSAFE "t_reply_unsafe"
 #define T_FORWARD_NONACK "t_forward_nonack"
 #define T_FORWARD_NONACK_UDP "t_forward_nonack_udp"
 #define T_FORWARD_NONACK_TCP "t_forward_nonack_tcp"
+#define T_GET_TI       "t_get_trans_ident"
+#define T_LOOKUP_IDENT "t_lookup_ident"
+#define T_IS_LOCAL     "t_is_local"
+
 
 
 
@@ -66,14 +67,13 @@ struct tm_binds {
 	register_tmcb_f	register_tmcb;
 	cmd_function	t_relay_to;
 	cmd_function 	t_relay;
-	tuacdlg_f               t_uac_dlg;
+	tuacdlg_f		t_uac_dlg;
 	treply_f		t_reply;
-#ifdef VOICE_MAIL
-        treply_wb_f             t_reply_with_body;
-        tislocal_f              t_is_local;
-        tget_ti_f               t_get_trans_ident;
-        tlookup_ident_f         t_lookup_ident;
-#endif
+	treply_wb_f		t_reply_with_body;
+	tislocal_f		t_is_local;
+	tget_ti_f		t_get_trans_ident;
+	tlookup_ident_f t_lookup_ident;
+	taddblind_f		t_addblind;
 	treply_f		t_reply_unsafe;
 	tfwd_f			t_forward_nonack;
 };

+ 1 - 1
modules/tm/uac.c

@@ -360,7 +360,7 @@ int t_uac_dlg(str* msg,                     /* Type of the message - MESSAGE, OP
 
 	new_cell->is_invite = msg->len == INVITE_LEN && memcmp(msg->s, INVITE, INVITE_LEN) == 0;
 	new_cell->local= 1 ;
-	set_kr(new_cell, REQ_FWDED);
+	set_kr(REQ_FWDED);
 
 	request = &new_cell->uac[branch].request;
 	request->dst.to = to_su;

+ 16 - 3
msg_translator.c

@@ -829,19 +829,21 @@ error:
 
 char * build_res_buf_from_sip_req( unsigned int code, char *text,
 					char *new_tag, unsigned int new_tag_len,
-					struct sip_msg* msg, unsigned int *returned_len)
+					struct sip_msg* msg, unsigned int *returned_len,
+					struct bookmark *bmark)
 {
     return build_res_buf_with_body_from_sip_req(code,text,new_tag,new_tag_len,
 						0,0, /* no body */
 						0,0, /* no content type */
-						msg,returned_len);
+						msg,returned_len, bmark);
 }
 
 char * build_res_buf_with_body_from_sip_req( unsigned int code, char *text ,
 					     char *new_tag, unsigned int new_tag_len ,
 					     char *body, unsigned int body_len,
 					     char *content_type, unsigned int content_type_len,
-					     struct sip_msg* msg, unsigned int *returned_len)
+					     struct sip_msg* msg, unsigned int *returned_len,
+						 struct bookmark *bmark)
 {
 	char              *buf, *p;
 	unsigned int      len,foo;
@@ -862,6 +864,7 @@ char * build_res_buf_with_body_from_sip_req( unsigned int code, char *text ,
 	char *content_len;
 	char *after_body;
 	str to_tag;
+	char *totags;
 
 	received_buf=0;
 	received_len=0;
@@ -1033,6 +1036,8 @@ char * build_res_buf_with_body_from_sip_req( unsigned int code, char *text ,
 						/* before to-tag */
 						append_str( p, hdr->name.s, to_tag.s-hdr->name.s);
 						/* to tag replacement */
+						bmark->to_tag_val.s=p;
+						bmark->to_tag_val.len=new_tag_len;
 						append_str( p, new_tag,new_tag_len);
 						/* the rest after to-tag */
 						append_str( p, to_tag.s+to_tag.len,
@@ -1041,12 +1046,20 @@ char * build_res_buf_with_body_from_sip_req( unsigned int code, char *text ,
 						after_body=hdr->body.s+hdr->body.len;
 						append_str( p, hdr->name.s, after_body-hdr->name.s);
 						append_str(p, TOTAG_TOKEN, TOTAG_TOKEN_LEN);
+						bmark->to_tag_val.s=p;
+						bmark->to_tag_val.len=new_tag_len;
 						append_str( p, new_tag,new_tag_len);
 						append_str( p, after_body, 
 										hdr->name.s+hdr->len-after_body);
 					}
 					break;
 				} /* no new to-tag -- proceed to 1:1 copying  */
+				totags=((struct to_body*)(hdr->parsed))->tag_value.s;
+				if (totags) {
+					bmark->to_tag_val.s=p+(totags-hdr->name.s);
+					bmark->to_tag_val.len=
+							((struct to_body*)(hdr->parsed))->tag_value.len;
+				};
 			case HDR_FROM:
 			case HDR_CALLID:
 			case HDR_CSEQ:

+ 9 - 2
msg_translator.h

@@ -45,6 +45,11 @@
 #include "parser/msg_parser.h"
 #include "ip_addr.h"
 
+/* point to some remarkable positions in a SIP message */
+struct bookmark {
+	str to_tag_val;
+};
+
 char * build_req_buf_from_sip_req (	struct sip_msg* msg, 
 				unsigned int *returned_len, struct socket_info* send_sock,
 				int proto);
@@ -57,7 +62,8 @@ char * build_res_buf_from_sip_req(	unsigned int code ,
 				char *new_tag ,
 				unsigned int new_tag_len ,
 				struct sip_msg* msg,
-				unsigned int *returned_len);
+				unsigned int *returned_len,
+				struct bookmark *bmark);
 char * build_res_buf_with_body_from_sip_req(	unsigned int code ,
 				char *text ,
 				char *new_tag ,
@@ -67,7 +73,8 @@ char * build_res_buf_with_body_from_sip_req(	unsigned int code ,
 				char *content_type,
 				unsigned int content_type_len,
 				struct sip_msg* msg,
-				unsigned int *returned_len);
+				unsigned int *returned_len,
+				struct bookmark *bmark);
 
 char* via_builder( unsigned int *len,
 	struct socket_info* send_sock,

+ 1 - 1
parser/msg_parser.h

@@ -56,7 +56,7 @@
 #define REPLY_CLASS(_reply) ((_reply)->REPLY_STATUS/100)
 
 /* number methods as power of two to allow bitmap matching */
-enum request_method { METHOD_INVITE=1, METHOD_CANCEL=2, METHOD_ACK=4, 
+enum request_method { METHOD_UNDEF=0, METHOD_INVITE=1, METHOD_CANCEL=2, METHOD_ACK=4, 
 	METHOD_BYE=8, METHOD_OTHER=16 };