- e2ecancel is sent hop by hop (closes SER-68)
authorAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 5 Jun 2007 15:16:44 +0000 (15:16 +0000)
committerAndrei Pelinescu-Onciul <andrei@iptel.org>
Tue, 5 Jun 2007 15:16:44 +0000 (15:16 +0000)
- delayed err reply: delay error replies from t_relay() until end of script: if
  script hasn't replied or dropped the trans., send reply(tm_errno).
- automatic 100 reply generation for invites can be turned on/off via
  a tm global parameter or on a per transaction basis (auto_inv_100,
  t_set_auto_inv_100())  (closes  SER-199)
- different error replies for too many branches, resolve or send error
- if error adding branches, reply with the lowest error
- membar_write before increasing nr_of_outgoings (branches) and membar_depends
 in which_cancel (should allow lockless which_cancel, with the possible race
 of not canceling a branch that is being created in the same time, OTOH
 this happens even if locks are used)

modules/tm/h_table.c
modules/tm/h_table.h
modules/tm/t_cancel.c
modules/tm/t_funcs.c
modules/tm/t_funcs.h
modules/tm/t_fwd.c
modules/tm/t_lookup.c
modules/tm/t_lookup.h
modules/tm/tm.c
modules/tm/uac.c

index a2a9913..3807f34 100644 (file)
@@ -63,6 +63,7 @@
 #include "fix_lumps.h" /* free_via_clen_lump */
 #include "timer.h"
 
+
 static enum kill_reason kr;
 
 /* pointer to the big table where all the transaction data
index 3f3360e..d7237ed 100644 (file)
@@ -116,8 +116,14 @@ void unlock_hash(int i);
             from t_release_transaction
    REQ_EXIST means that this request is a retransmission which does not
             affect transactional state
+   REQ_ERR_DELAYED mean that tm wants to send  reply(ser_error) but it
+            delayed it to end-of-script to allow it to be overriden.
+            If this is set and all of the above flag are not => send reply
+            on end of script. If any of the above flags is set, do not
+            send (especially REQ_RPLD and REQ_RLSD).
 */
-enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8 };
+enum kill_reason { REQ_FWDED=1, REQ_RPLD=2, REQ_RLSD=4, REQ_EXIST=8,
+                                  REQ_ERR_DELAYED=16 };
 
 
 /* #define F_RB_T_ACTIVE               0x01  (obsolete) fr or retr active */
@@ -217,7 +223,7 @@ struct totag_elem {
 
 #define T_IN_AGONY (1<<5) /* set if waiting to die (delete timer)
                              TODO: replace it with del on unref */
-
+#define T_AUTO_INV_100 (1<<6) /* send an 100 reply automatically  to inv. */
 #define T_DONT_FORK   (T_CANCELED|T_6xx)
 
 /* unsigned short should be enough for a retr. timer: max. 65535 ticks =>
index 600fb2e..7775541 100644 (file)
 void which_cancel( struct cell *t, branch_bm_t *cancel_bm )
 {
        int i;
+       int branches_no;
        
        *cancel_bm=0;
-       for( i=0 ; i<t->nr_of_outgoings ; i++ ) {
+       branches_no=t->nr_of_outgoings;
+       membar_depends(); 
+       for( i=0 ; i<branches_no ; i++ ) {
                if (should_cancel_branch(t, i, 1)) 
                        *cancel_bm |= 1<<i ;
-
        }
 }
 
index afd9aff..9306ea4 100644 (file)
@@ -48,6 +48,9 @@
  *              t_relay_to releases the transaction if t_forward_non_ack
  *              fails and t_kill fails or this is a failed replication (andrei)
  *  2007-05-02  t_relay_to() uses now t_forward_cancel for cancels (andrei)
+ *  2007-06-05  delay t_relay() replies till script end so that they can be
+ *               overwritten by the script user; generate 100 automatically
+ *               only if T_NO_100 is not set (andrei)
  */
 
 #include <limits.h>
 #include "config.h"
 #include "t_stats.h"
 
+/* if defined t_relay* error reply generation will be delayed till script
+ * end (this allows the script writter to send its own error reply) */
+#define TM_DELAYED_REPLY
+
 /* fr_timer AVP specs */
 static int     fr_timer_avp_type = 0;
 static int_str fr_timer_avp = {0};
@@ -77,6 +84,10 @@ static int_str fr_inv_timer_avp = {0};
 static str     fr_inv_timer_str;
 static int     fr_inv_timer_index = 0;
 
+int tm_error = 0; /* delayed tm error */
+
+int tm_auto_inv_100=1; /* automatically send 100 to an INVITE, default on*/
+struct msgid_var user_auto_inv_100;
 
 /* ----------------------------------------------------- */
 int send_pr_buffer(    struct retr_buf *rb, void *buf, int len
@@ -172,7 +183,7 @@ void put_on_wait(  struct cell  *Trans  )
 
 /* WARNING: doesn't work from failure route (deadlock, uses t_reply =>
  *  tries to get the reply lock again) */
-static int kill_transaction( struct cell *trans )
+int kill_transaction( struct cell *trans, int error )
 {
        char err_buffer[128];
        int sip_err;
@@ -184,7 +195,7 @@ static int kill_transaction( struct cell *trans )
                want to put the forking burden on upstream client;
                however, it may fail too due to lack of memory */
 
-       ret=err2reason_phrase( ser_error, &sip_err,
+       ret=err2reason_phrase(error, &sip_err,
                err_buffer, sizeof(err_buffer), "TM" );
        if (ret>0) {
                reply_ret=t_reply( trans, trans->uas.request, 
@@ -206,13 +217,15 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 {
        int ret;
        int new_tran;
-       int reply_ret;
        /* struct hdr_field *hdr; */
        struct cell *t;
        struct dest_info dst;
        unsigned short port;
        str host;
        short comp;
+#ifndef TM_DELAYED_REPLY
+       int reply_ret;
+#endif
 
        ret=0;
        
@@ -280,7 +293,7 @@ int t_relay_to( struct sip_msg  *p_msg , struct proxy_l *proxy, int proto,
 
        /* INVITE processing might take long, particularly because of DNS
           look-ups -- let upstream know we're working on it */
-       if (p_msg->REQ_METHOD==METHOD_INVITE )
+       if (p_msg->REQ_METHOD==METHOD_INVITE && (t->flags&T_AUTO_INV_100))
        {
                DBG( "SER: new INVITE\n");
                if (!t_reply( t, p_msg , 100 ,
@@ -295,8 +308,14 @@ handle_ret:
                DBG( "ERROR:tm:t_relay_to:  t_forward_nonack returned error \n");
                /* we don't want to pass upstream any reply regarding replicating
                 * a request; replicated branch must stop at us*/
-               if (!replicate) {
-                       reply_ret=kill_transaction( t );
+               if (likely(!replicate)) {
+#ifdef TM_DELAYED_REPLY
+                       /* current error in tm_error */
+                       tm_error=ser_error;
+                       set_kr(REQ_ERR_DELAYED);
+                       DBG("%d error reply generation delayed \n", ser_error);
+#else
+                       reply_ret=kill_transaction( t, ser_error );
                        if (reply_ret>0) {
                                /* we have taken care of all -- do nothing in
                                script */
@@ -308,6 +327,7 @@ handle_ret:
                                        "on error failed\n");
                                t_release_transaction(t);
                        }
+#endif /* TM_DELAYED_REPLY */
                }else{
                        t_release_transaction(t); /* kill it  silently */
                }
index 1686fa3..25ef1f4 100644 (file)
@@ -73,8 +73,10 @@ struct timer;
 struct entry;
 struct cell;
 
+extern int tm_error; /* delayed tm error */
 extern int noisy_ctimer;
-
+extern int tm_auto_inv_100; /*automatically send 100 to an INVITE, default on*/
+extern struct msgid_var user_auto_inv_100;
 
 /* default names for timer's AVPs  */
 #define FR_TIMER_AVP      "callee_fr_timer"
@@ -195,5 +197,6 @@ void put_on_wait(  struct cell  *Trans  );
 int t_relay_to( struct sip_msg  *p_msg ,
        struct proxy_l *proxy, int proto, int replicate ) ;
 
+int kill_transaction( struct cell *trans, int error );
 #endif
 
index 410f07b..623618d 100644 (file)
@@ -71,6 +71,7 @@
  *              incomming one. (it can be disabled with reparse_invite=0) (Miklos)
  *              t_relay_cancel() introduced -- can be used to relay CANCELs
  *              at the beginning of the script. (Miklos)
+ * 2007-06-04  running transaction are canceled hop by hop (andrei)
  */
 
 #include "defs.h"
 #include "../../dst_blacklist.h"
 #endif
 
+/* cancel hop by hop */
+#define E2E_CANCEL_HOP_BY_HOP
 
 int unmatched_cancel=UM_CANCEL_STATEFULL;
 
@@ -254,7 +257,9 @@ int add_blind_uac( /*struct cell *t*/ )
        }
        /* make sure it will be replied */
        t->flags |= T_NOISY_CTIMER_FLAG;
-       t->nr_of_outgoings++;
+       membar_write(); /* to allow lockless which_cancel() we want to be sure 
+                                          all the writes finished before updating branch number*/
+       t->nr_of_outgoings=(branch+1);
        /* start FR timer -- protocol set by default to PROTO_NONE,
        which means retransmission timer will not be started
     */
@@ -271,7 +276,8 @@ int add_blind_uac( /*struct cell *t*/ )
 
 /* 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
+   might interfere with the processes of adding multiple branches;
+   On error returns <0 & sets ser_error to the same value
 */
 int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
        struct proxy_l *proxy, int proto )
@@ -285,7 +291,7 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
        branch=t->nr_of_outgoings;
        if (branch==MAX_BRANCHES) {
                LOG(L_ERR, "ERROR: add_uac: maximum number of branches exceeded\n");
-               ret=E_CFG;
+               ret=ser_error=E_TOO_MANY_BRANCHES;
                goto error;
        }
 
@@ -316,7 +322,7 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
                                                next_hop ? next_hop: uri, proto)==0)
 #endif
                {
-                       ret=E_BAD_ADDRESS;
+                       ret=ser_error=E_BAD_ADDRESS;
                        goto error;
                }
        }
@@ -345,7 +351,10 @@ int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop,
        t->uac[branch].uri.s=t->uac[branch].request.buffer+
                request->first_line.u.request.method.len+1;
        t->uac[branch].uri.len=uri->len;
-       t->nr_of_outgoings++;
+       membar_write(); /* to allow lockless ops (e.g. which_cancel()) we want
+                                          to be sure everything above is fully written before
+                                          updating branches no. */
+       t->nr_of_outgoings=(branch+1);
 
        /* update stats */
        if (proxy){
@@ -366,7 +375,8 @@ error:
  *  new ip address (if the dns name resolves to more ips). If no more
  *   ips are found => returns -1.
  *  returns its branch id (>=0)
-   or error (<0); it doesn't send a message yet -- a reply to it
+   or error (<0) and sets ser_error if needed; it doesn't send a message 
+   yet -- a reply to it
    might interfere with the processes of adding multiple branches
    if lock_replies is 1 replies will be locked for t until the new branch
    is added (to prevent add branches races). Use 0 if the reply lock is
@@ -400,7 +410,8 @@ int add_uac_dns_fallback( struct cell *t, struct sip_msg* msg,
                                                        "branches exceeded\n");
                                if (lock_replies)
                                        UNLOCK_REPLIES(t);
-                               return E_CFG;
+                                       ret=ser_error=E_TOO_MANY_BRANCHES;
+                               return ret;
                        }
                        /* copy the dns handle into the new uac */
                        dns_srv_handle_cpy(&t->uac[t->nr_of_outgoings].dns_h,
@@ -489,7 +500,10 @@ error:
 void e2e_cancel( struct sip_msg *cancel_msg, 
        struct cell *t_cancel, struct cell *t_invite )
 {
-       branch_bm_t cancel_bm, tmp_bm;
+       branch_bm_t cancel_bm;
+#ifndef E2E_CANCEL_HOP_BY_HOP
+       branch_bm_t tmp_bm;
+#endif
        int i;
        int lowest_error;
        int ret;
@@ -515,9 +529,21 @@ void e2e_cancel( struct sip_msg *cancel_msg,
        
        /* determine which branches to cancel ... */
        which_cancel( t_invite, &cancel_bm );
-       t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings;
        /* fix label -- it must be same for reply matching */
        t_cancel->label=t_invite->label;
+#ifdef E2E_CANCEL_HOP_BY_HOP
+       for (i=0; i<t_invite->nr_of_outgoings; i++)
+               if (cancel_bm & (1<<i)) {
+                       /* it's safe to get the reply lock since e2e_cancel is
+                        * called with the cancel as the "current" transaction so
+                        * at most t_cancel REPLY_LOCK is held in this process =>
+                        * no deadlock possibility */
+                       ret=cancel_branch(t_invite, i, F_CANCEL_B_FAKE_REPLY);
+                       if (ret<0) cancel_bm &= ~(1<<i);
+                       if (ret<lowest_error) lowest_error=ret;
+               }
+#else
+       t_cancel->nr_of_outgoings=t_invite->nr_of_outgoings;
        /* ... and install CANCEL UACs */
        for (i=0; i<t_invite->nr_of_outgoings; i++)
                if ((cancel_bm & (1<<i)) && (t_invite->uac[i].last_received>=100)) {
@@ -564,6 +590,7 @@ void e2e_cancel( struct sip_msg *cancel_msg,
                        }
                }
        }
+#endif /*E2E_CANCEL_HOP_BY_HOP */
 
        /* if error occurred, let it know upstream (final reply
           will also move the transaction on wait state
@@ -571,7 +598,7 @@ void e2e_cancel( struct sip_msg *cancel_msg,
        if (lowest_error<0) {
                LOG(L_ERR, "ERROR: cancel error\n");
                /* if called from failure_route, make sure that the unsafe version
-                * is called (we are already hold the reply mutex for the cancel
+                * is called (we are already holding the reply mutex for the cancel
                 * transaction).
                 */
                if ((rmode==MODE_ONFAILURE) && (t_cancel==get_t()))
@@ -592,8 +619,8 @@ void e2e_cancel( struct sip_msg *cancel_msg,
                else
                        t_reply( t_cancel, cancel_msg, 200, CANCELING );
        } else {
-               /* if the transaction exists, but there is no more pending
-                  branch, tell upstream we're done
+               /* if the transaction exists, but there are no more pending
+                  branches, tell upstream we're done
                */
                DBG("DEBUG: e2e_cancel: e2e cancel -- no more pending branches\n");
                /* if called from failure_route, make sure that the unsafe version
@@ -763,24 +790,24 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
        /* make -Wall happy */
        current_uri.s=0;
 
-       set_kr(REQ_FWDED);
+       if (t->flags & T_CANCELED){
+               DBG("t_forward_non_ack: no forwarding on a canceled transaction\n");
+               ser_error=E_CANCELED;
+               return -1;
+       }
        if (p_msg->REQ_METHOD==METHOD_CANCEL) { 
                t_invite=t_lookupOriginalT(  p_msg );
                if (t_invite!=T_NULL_CELL) {
                        e2e_cancel( p_msg, t, t_invite );
                        UNREF(t_invite);
+                       set_kr(REQ_FWDED);
                        return 1;
                }
        }
-       if (t->flags & T_CANCELED){
-               DBG("t_forward_non_ack: no forwarding on canceled branch\n");
-               ser_error=E_CANCELED;
-               return -1;
-       }
 
        backup_si = p_msg->force_send_socket;
        /* if no more specific error code is known, use this */
-       lowest_ret=E_BUG;
+       lowest_ret=E_UNSPEC;
        /* branches added */
        added_branches=0;
        /* branch to begin with */
@@ -802,11 +829,12 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
        */
        if (first_branch==0) {
                try_new=1;
-               branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg), proxy, proto );
+               branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg),
+                                                       proxy, proto );
                if (branch_ret>=0) 
                        added_branches |= 1<<branch_ret;
                else
-                       lowest_ret=branch_ret;
+                       lowest_ret=MIN_int(lowest_ret, branch_ret);
        } else try_new=0;
 
        init_branch_iterator();
@@ -823,7 +851,7 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
                if (branch_ret>=0) 
                        added_branches |= 1<<branch_ret;
                else
-                       lowest_ret=branch_ret;
+                       lowest_ret=MIN_int(lowest_ret, branch_ret);
        }
        /* consume processed branches */
        clear_branches();
@@ -839,9 +867,10 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
                        return -1;
                }
                LOG(L_ERR, "ERROR: t_forward_nonack: failure to add branches\n");
+               ser_error=lowest_ret;
                return lowest_ret;
        }
-
+       ser_error=0; /* clear branch adding errors */
        /* send them out now */
        success_branch=0;
        lock_replies= ! ((rmode==MODE_ONFAILURE) && (t==get_t()));
@@ -858,10 +887,14 @@ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg ,
                }
        }
        if (success_branch<=0) {
-               ser_error=E_SEND;
+               if (ser_error==0)
+                               ser_error=E_SEND;
+               /* else return the last error (?) */
                /* the caller should take care and delete the transaction */
                return -1;
        }
+       ser_error=0; /* clear branch send errors, we have overall success */
+       set_kr(REQ_FWDED);
        return 1;
 }
 
index ffe61d4..8d572dd 100644 (file)
  *               needed (andrei)
  * 2007-03-17  added callbacks for retransmitted request, ack to negative 
  *              replies and replies to local transactions (andrei)
+ * 2007-06-01  support for different retransmissions intervals per transaction;
+ *             added maximum inv. and non-inv. transaction life time (andrei)
+ * 2007-06-05  added delayed error reply support in t_unref;
+*              added support for turning off 100 repl. sending on inv. (andrei)
  * 2007-06-01  support for different retransmissions intervals per transaction;
  *             added maximum inv. and non-inv. transaction life time (andrei)
  */
@@ -174,6 +178,8 @@ static struct cell *T;
 */
 unsigned int     global_msg_id;
 
+
+
 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);}
@@ -1071,6 +1077,7 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
        struct sip_msg *shm_msg;
        unsigned int timeout; /* avp timeout gets stored here (in s) */
        ticks_t lifetime;
+       int v;
 
        shm_msg=new_cell->uas.request;
        new_cell->from.s=shm_msg->from->name.s;
@@ -1087,6 +1094,11 @@ static inline void init_new_t(struct cell *new_cell, struct sip_msg *p_msg)
        new_cell->method=new_cell->uas.request->first_line.u.request.method;
        if (p_msg->REQ_METHOD==METHOD_INVITE){
                new_cell->flags |= T_IS_INVITE_FLAG;
+               if (unlikely(v=get_msgid_val(user_auto_inv_100, p_msg->id, int)))
+                       /* 1 = set, -1 = reset */
+                       new_cell->flags|=T_AUTO_INV_100 & (!(v+1)-1);
+               else
+                       new_cell->flags|=T_AUTO_INV_100 & ( !tm_auto_inv_100 -1);
                lifetime=(ticks_t)get_msgid_val(user_inv_max_lifetime,
                                                                                                p_msg->id, int);
                if (likely(lifetime==0))
@@ -1321,17 +1333,30 @@ int t_unref( struct sip_msg* p_msg  )
                return -1;
        if (p_msg->first_line.type==SIP_REQUEST){
                kr=get_kr();
-               if (kr==0 
-                               ||(p_msg->REQ_METHOD==METHOD_ACK && !(kr & REQ_RLSD))) {
+               if (unlikely(kr == REQ_ERR_DELAYED)){
+                       DBG("t_unref: delayed error reply generation(%d)\n", tm_error);
+                       if (unlikely(rmode==MODE_ONFAILURE)){
+                               BUG("tm: t_unref: called w/ kr=REQ_ERR_DELAYED in failure"
+                                               " route\n");
+                       }else if (unlikely( kill_transaction(T, tm_error)<=0 )){
+                               DBG("ERROR: t_unref: generation of a delayed stateful reply"
+                                               " failed\n");
+                               t_release_transaction(T);
+                       }
+               }else if ( unlikely (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);
                }
+               tm_error=0; /* clear it */
        }
        UNREF( T );
        set_t(T_UNDEFINED);
        return 1;
 }
 
+
+
 int t_get_trans_ident(struct sip_msg* p_msg, unsigned int* hash_index, unsigned int* label)
 {
     struct cell* t;
index 93a9a2c..1c1eeb1 100644 (file)
@@ -51,6 +51,8 @@ extern unsigned int     global_msg_id;
 extern int ruri_matching;
 extern int via1_matching;
 
+
+
 void init_t();
 int init_rb( struct retr_buf *rb, struct sip_msg *msg );
 struct cell* t_lookupOriginalT( struct sip_msg* p_msg );
index 91efd5c..5f87bc4 100644 (file)
@@ -82,6 +82,8 @@
  *  2006-10-16  added a new param.: aggregate challenges (andrei)
  *  2007-05-28  two new params: reparse_invite, ac_extra_hdrs
  *              added w_t_relay_cancel() (Miklos)
+ *  2007-06-05  added t_set_auto_inv_100() and auto_inv_100 (param);
+ *               t_set_max_lifetime(), max_{non}inv_lifetime  (andrei)
  */
 
 
@@ -191,6 +193,7 @@ static int t_set_fr_inv(struct sip_msg* msg, char* fr_inv, char* foo);
 static int t_set_fr_all(struct sip_msg* msg, char* fr_inv, char* fr);
 static int w_t_set_retr(struct sip_msg* msg, char* retr_t1, char* retr_t2);
 static int w_t_set_max_lifetime(struct sip_msg* msg, char* inv, char* noninv);
+static int t_set_auto_inv_100(struct sip_msg* msg, char* on_off, char* foo);
 static int t_branch_timeout(struct sip_msg* msg, char*, char*);
 static int t_branch_replied(struct sip_msg* msg, char*, char*);
 static int t_any_timeout(struct sip_msg* msg, char*, char*);
@@ -288,6 +291,8 @@ static cmd_export_t cmds[]={
                        REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
        {"t_set_max_lifetime", w_t_set_max_lifetime,      2, fixup_var_int_12,
                        REQUEST_ROUTE|ONREPLY_ROUTE|FAILURE_ROUTE|BRANCH_ROUTE },
+       {"t_set_auto_inv_100", t_set_auto_inv_100,       1, fixup_var_int_1,
+                                                                                                         REQUEST_ROUTE},
        {"t_branch_timeout",  t_branch_timeout,         0, 0,  FAILURE_ROUTE},
        {"t_branch_replied",  t_branch_replied,         0, 0,  FAILURE_ROUTE},
        {"t_any_timeout",     t_any_timeout,            0, 0, 
@@ -339,6 +344,7 @@ static param_export_t params[]={
        {"max_inv_lifetime",    PARAM_INT, &tm_max_inv_lifetime                  },
        {"max_noninv_lifetime", PARAM_INT, &tm_max_noninv_lifetime               },
        {"noisy_ctimer",        PARAM_INT, &noisy_ctimer                         },
+       {"auto_inv_100",        PARAM_INT, &tm_auto_inv_100                      },
        {"uac_from",            PARAM_STRING, &uac_from                          },
        {"unix_tx_timeout",     PARAM_INT, &tm_unix_tx_timeout                   },
        {"restart_fr_on_each_reply", PARAM_INT, &restart_fr_on_each_reply        },
@@ -1214,6 +1220,34 @@ static int w_t_set_max_lifetime(struct sip_msg* msg, char* p1, char* p2)
 
 
 
+/* set automatically sending 100 replies on/off for the current or
+ * next to be created transaction */
+static int t_set_auto_inv_100(struct sip_msg* msg, char* p1, char* p2)
+{
+       int state;
+       struct cell* t;
+       
+       if (get_int_fparam(&state, msg, (fparam_t*)p1) < 0) return -1;
+       t=get_t();
+       /* in MODE_REPLY and MODE_ONFAILURE T will be set to current transaction;
+        * in MODE_REQUEST T will be set only if the transaction was already
+        * created; if not -> use the static variables */
+       if (!t || t==T_UNDEFINED ){
+               if (state)
+                       set_msgid_val(user_auto_inv_100, msg->id, int, 1); /* set */
+               else
+                       set_msgid_val(user_auto_inv_100, msg->id, int, -1); /* reset */
+       }else{
+               if (state)
+                       t->flags|=T_AUTO_INV_100;
+               else
+                       t->flags&=~T_AUTO_INV_100;
+       }
+       return 1;
+}
+
+
+
 /* script function, FAILURE_ROUTE only, returns true if the 
  * choosed "failure" branch failed because of a timeout, 
  * -1 otherwise */
index 06976d5..99b5383 100644 (file)
@@ -254,6 +254,7 @@ static inline int t_uac_prepare(str* method, str* headers, str* body,
        }
        if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0){
                new_cell->flags |= T_IS_INVITE_FLAG;
+               new_cell->flags|=T_AUTO_INV_100 & (!tm_auto_inv_100 -1);
                lifetime=tm_max_inv_lifetime;
        }else
                lifetime=tm_max_noninv_lifetime;