Blob Blame History Raw
diff --git a/Makefile b/Makefile
index a215e39..23a3fac 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,8 @@ OUTPUT=sipp
 
 # C & C++ object files to be built
 OBJ= xp_parser.o message.o scenario.o screen.o call.o comp.o sipp.o stat.o \
-     actions.o variables.o infile.o deadcall.o task.o socketowner.o listener.o
+     actions.o variables.o infile.o deadcall.o task.o socketowner.o listener.o \
+     opentask.o reporttask.o watchdog.o
 
 # Libraries directories
 LIBDIR_linux=
@@ -160,43 +161,46 @@ INCDIR=$(INCDIR_$(SYSTEM))
 
 # Building without TLS and authentication (no openssl pre-requisite)
 all:
-	make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` $(OUTPUT)
+	$(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` $(OUTPUT)
 
 # Building with TLS and authentication
 ossl:
-	make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o  milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" $(OUTPUT)
+	$(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o  milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5" $(OUTPUT)
 
 #Building with PCAP play
 pcapplay:
-	make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT)
+	$(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT)
 
 pcapplay_ossl:
-	make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o  milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5"  OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap `if test -f ./ext; then echo -L./ext/lib; fi;`" PCAPPLAY="-DPCAPPLAY `if test -f ./ext; then echo -I./ext/include; fi;`" $(OUTPUT)
+	$(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o  milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5"  OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lpcap `if test -f ./ext; then echo -L./ext/lib; fi;`" PCAPPLAY="-DPCAPPLAY `if test -f ./ext; then echo -I./ext/include; fi;`" $(OUTPUT)
 
 pcapplay_hp_li_ia:
-	@_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; make pcapplay
+	@_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; $(MAKE) pcapplay
 
 pcapplay_ossl_hp_li_ia:
-	@_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; make pcapplay_ossl
+	@_HPUX_LI_FLAG=-D_HPUX_LI ; export _HPUX_LI_FLAG ; $(MAKE) pcapplay_ossl
 
 pcapplay_cygwin:
-	make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT)
+	$(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT)
 
 pcapplay_ossl_cygwin:
-	make OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o  milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5"  OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT)
+	$(MAKE) OSNAME=`uname|sed -e "s/CYGWIN.*/CYGWIN/"` MODELNAME=`uname -m|sed "s/Power Macintosh/ppc/"` OBJ_TLS="auth.o sslinit.o sslthreadsafe.o  milenage.o rijndael.o" TLS_LIBS="-lssl -lcrypto" TLS="-D_USE_OPENSSL -DOPENSSL_NO_KRB5"  OBJ_PCAPPLAY="send_packets.o prepare_pcap.o" PCAPPLAY_LIBS="-lwpcap" PCAPPLAY="-DPCAPPLAY" $(OUTPUT)
 
 $(OUTPUT): $(OBJ_TLS) $(OBJ_PCAPPLAY) $(OBJ)
 	$(CCLINK) $(LFLAGS) $(MFLAGS) $(LIBDIR_$(SYSTEM)) \
 	$(DEBUG_FLAGS) -o $@ $(OBJ_TLS) $(OBJ_PCAPPLAY) $(OBJ) $(LIBS) $(TLS_LIBS) $(PCAPPLAY_LIBS) $(EXTRAENDLIBS)
 
 debug:
-	DEBUG_FLAGS="-g -pg" ; export DEBUG_FLAGS ; make all
+	DEBUG_FLAGS="-g -pg" ; export DEBUG_FLAGS ; $(MAKE) all
 
 debug_ossl:
-	@DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; make ossl
+	@DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; $(MAKE) ossl
+
+debug_pcap:
+	@DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; make pcapplay
 
 debug_pcap_cygwin:
-	@DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; make pcapplay_ossl_cygwin
+	@DEBUG_FLAGS=-g ; export DEBUG_FLAGS ; $(MAKE) pcapplay_ossl_cygwin
 
 clean:
 	rm -f *.o $(OUTPUT) *~ $(TOCLEAN) 
@@ -204,7 +208,7 @@ clean:
 
 archive:
 	rm -f TMP_TAR_FILE.* $(ARCHIVE)
-	make clean
+	$(MAKE) clean
 	tar cf TMP_TAR_FILE.tar .
 	gzip TMP_TAR_FILE.tar
 	cp TMP_TAR_FILE.tar.gz $(ARCHIVE)
@@ -224,3 +228,7 @@ archive:
 
 .c.o:
 	$(CC) $(CFLAGS) $(MFLAGS) $(DEBUG_FLAGS) $(_HPUX_LI_FLAG) $(INCDIR) -c -o $*.o $<
+
+fortune.so: fortune.cpp
+	g++ -fPIC $(CPPFLAGS) $(MFLAGS) $(DEBUG_FLAGS) $(_HPUX_LI_FLAG) $(INCDIR) -c -o fortune.o $<
+	gcc -shared -Wl,-soname,fortune.so -o $@ fortune.o
diff --git a/actions.cpp b/actions.cpp
index 92ad968..f944233 100644
--- a/actions.cpp
+++ b/actions.cpp
@@ -66,7 +66,7 @@ const char * CAction::comparatorToString(T_Comparator comp) {
 
 bool CAction::compare(VariableTable *variableTable) {
   double lhs = variableTable->getVar(M_varInId)->getDouble();
-  double rhs = M_doubleValue;
+  double rhs = M_varIn2Id ? variableTable->getVar(M_varIn2Id)->getDouble() : M_doubleValue;
 
   switch(M_comp) {
     case E_C_EQ:
@@ -91,58 +91,60 @@ void CAction::afficheInfo()
 {
   if (M_action == E_AT_ASSIGN_FROM_REGEXP) {
     if(M_lookingPlace == E_LP_MSG) {
-      printf("Type[%d] - regexp[%s] where[%s] - checkIt[%d] - $%s",
+            printf("Type[%d] - regexp[%s] where[%s] - checkIt[%d] - checkItInverse[%d] - $%s",
              M_action,
 	     M_regularExpression,
              "Full Msg",
              M_checkIt,
+                   M_checkItInverse,
 		       display_scenario->allocVars->getName(M_varId));
     } else {
-      printf("Type[%d] - regexp[%s] where[%s-%s] - checkIt[%d] - $%d",
+            printf("Type[%d] - regexp[%s] where[%s-%s] - checkIt[%d] - checkItInverse[%d] - $%s",
              M_action,
 	     M_regularExpression,
              "Header",
              M_lookingChar,
-             M_checkIt, display_scenario->allocVars->getName(M_varId));
+                   M_checkIt,
+                   M_checkItInverse, display_scenario->allocVars->getName(M_varId));
     }
   } else if (M_action == E_AT_EXECUTE_CMD) {
-    if (M_cmdLine) {
-        printf("Type[%d] - command[%-32.32s]", M_action, M_cmdLine);
-    }
+    printf("Type[%d] - command[%-32.32s]", M_action, M_message_str[0]);
   } else if (M_action == E_AT_EXEC_INTCMD) {
       printf("Type[%d] - intcmd[%-32.32s]", M_action, strIntCmd(M_IntCmd));
   } else if (M_action == E_AT_LOG_TO_FILE) {
-      printf("Type[%d] - message[%-32.32s]", M_action, M_message[0]);
+      printf("Type[%d] - message[%-32.32s]", M_action, M_message_str[0]);
   } else if (M_action == E_AT_LOG_WARNING) {
-      printf("Type[%d] - warning[%-32.32s]", M_action, M_message[0]);
+      printf("Type[%d] - warning[%-32.32s]", M_action, M_message_str[0]);
+  } else if (M_action == E_AT_LOG_ERROR) {
+      printf("Type[%d] - error[%-32.32s]", M_action, M_message_str[0]);
   } else if (M_action == E_AT_ASSIGN_FROM_SAMPLE) {
       char tmp[40];
       M_distribution->textDescr(tmp, sizeof(tmp));
-      printf("Type[%d] - sample varId[%d] %s", M_action, display_scenario->allocVars->getName(M_varId), tmp);
+      printf("Type[%d] - sample varId[%s] %s", M_action, display_scenario->allocVars->getName(M_varId), tmp);
   } else if (M_action == E_AT_ASSIGN_FROM_VALUE) {
-      printf("Type[%d] - assign varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
+      printf("Type[%d] - assign varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
   } else if (M_action == E_AT_ASSIGN_FROM_INDEX) {
-      printf("Type[%d] - assign index[%d]", M_action, display_scenario->allocVars->getName(M_varId));
+      printf("Type[%d] - assign index[%s]", M_action, display_scenario->allocVars->getName(M_varId));
   } else if (M_action == E_AT_ASSIGN_FROM_GETTIMEOFDAY) {
-      printf("Type[%d] - assign gettimeofday[%d, %d]", M_action, display_scenario->allocVars->getName(M_varId));
+      printf("Type[%d] - assign gettimeofday[%s, %s]", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_subVarId[0]));
   } else if (M_action == E_AT_ASSIGN_FROM_STRING) {
-      printf("Type[%d] - string assign varId[%d] [%-32.32s]", M_action, display_scenario->allocVars->getName(M_varId), M_message[0]);
+      printf("Type[%d] - string assign varId[%s] [%-32.32s]", M_action, display_scenario->allocVars->getName(M_varId), M_message_str[0]);
   } else if (M_action == E_AT_JUMP) {
-      printf("Type[%d] - jump varInId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue);
+      printf("Type[%d] - jump varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue);
   } else if (M_action == E_AT_PAUSE_RESTORE) {
-      printf("Type[%d] - restore pause varInId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue);
+      printf("Type[%d] - restore pause varInId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varInId), M_doubleValue);
   } else if (M_action == E_AT_VAR_ADD) {
-      printf("Type[%d] - add varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
+      printf("Type[%d] - add varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
   } else if (M_action == E_AT_VAR_MULTIPLY) {
-      printf("Type[%d] - multiply varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
+      printf("Type[%d] - multiply varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
   } else if (M_action == E_AT_VAR_DIVIDE) {
-      printf("Type[%d] - divide varId[%d] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
+      printf("Type[%d] - divide varId[%s] %lf", M_action, display_scenario->allocVars->getName(M_varId), M_doubleValue);
   } else if (M_action == E_AT_VAR_TRIM) {
-      printf("Type[%d] - trim varId[%d]", M_action, display_scenario->allocVars->getName(M_varId));
+      printf("Type[%d] - trim varId[%s]", M_action, display_scenario->allocVars->getName(M_varId));
   } else if (M_action == E_AT_VAR_TEST) {
-      printf("Type[%d] - divide varId[%d] varInId[%d] %s %lf", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_varInId), comparatorToString(M_comp), M_doubleValue);
+      printf("Type[%d] - divide varId[%s] varInId[%s] %s %lf", M_action, display_scenario->allocVars->getName(M_varId), display_scenario->allocVars->getName(M_varInId), comparatorToString(M_comp), M_doubleValue);
   } else if (M_action == E_AT_VAR_TO_DOUBLE) {
-      printf("Type[%d] - toDouble varId[%d]", M_action, display_scenario->allocVars->getName(M_varId));
+      printf("Type[%d] - toDouble varId[%s]", M_action, display_scenario->allocVars->getName(M_varId));
 #ifdef PCAPPLAY
   } else if ((M_action == E_AT_PLAY_PCAP_AUDIO) || (M_action == E_AT_PLAY_PCAP_VIDEO)) {
       printf("Type[%d] - file[%s]", M_action, M_pcapArgs->file);
@@ -159,14 +161,15 @@ CAction::T_IntCmdType   CAction::getIntCmd ()      { return(M_IntCmd);       }
 CAction::T_Comparator   CAction::getComparator ()  { return(M_comp);	     }
 
 bool           CAction::getCheckIt()      { return(M_checkIt);      }
+bool           CAction::getCheckItInverse() { return(M_checkItInverse);      }
 bool           CAction::getCaseIndep()    { return(M_caseIndep);    }
 bool           CAction::getHeadersOnly()  { return(M_headersOnly);  }
 int            CAction::getOccurence()    { return(M_occurence);    }
 int            CAction::getVarId()        { return(M_varId);        }
 int            CAction::getVarInId()      { return(M_varInId);      }
+int            CAction::getVarIn2Id()      { return(M_varIn2Id);      }
 char*          CAction::getLookingChar()  { return(M_lookingChar);  }
 SendingMessage *CAction::getMessage(int n)      { return(M_message[n]);      }
-SendingMessage *CAction::getCmdLine()      { return(M_cmdLine);      }
 CSample*       CAction::getDistribution() { return(M_distribution); }
 double         CAction::getDoubleValue()  { return(M_doubleValue);  }
 char*          CAction::getStringValue()  { return(M_stringValue);  }
@@ -180,10 +183,14 @@ void CAction::setLookingPlace (CAction::T_LookingPlace P_value)
 { M_lookingPlace = P_value; }
 void CAction::setCheckIt      (bool           P_value) 
 { M_checkIt      = P_value; }
+void CAction::setCheckItInverse      (bool           P_value) 
+{ M_checkItInverse      = P_value; }
 void CAction::setVarId        (int            P_value) 
 { M_varId        = P_value; }
 void CAction::setVarInId      (int            P_value)
 { M_varInId        = P_value; }
+void CAction::setVarIn2Id      (int            P_value)
+{ M_varIn2Id        = P_value; }
 void CAction::setCaseIndep    (bool           P_value)
 { M_caseIndep    = P_value; }
 void CAction::setOccurence   (int            P_value) 
@@ -328,41 +335,22 @@ int CAction::executeRegExp(char* P_string, VariableTable *P_callVarTable)
 void CAction::setSubString(char** P_target, char* P_source, int P_start, int P_stop)
 {
   int sizeOf;
-  int sourceLength;
-  size_t L_size = 0;
 
   if(P_source != NULL) {
     sizeOf = P_stop - P_start;
-    if(sizeOf > 0) {
-      L_size = (size_t) sizeOf;
-      L_size += 1;
-      (*P_target) = new char[L_size];
+    (*P_target) = new char[sizeOf + 1];
 
+    if (sizeOf > 0) {
       memcpy((*P_target), &(P_source[P_start]), sizeOf);
+    }
 
-      (*P_target)[sizeOf] = '\0';
-	 }
+    (*P_target)[sizeOf] = '\0';
   } else {
     *P_target = NULL ;
   }
 }
 
 
-void CAction::setCmdLine  (char*          P_value)
-{
-  if(M_cmdLine != NULL)
-  {
-    delete [] M_cmdLine;
-    M_cmdLine = NULL;
-  }
-
-  if(P_value != NULL)
-  {
-    M_cmdLine_str = strdup(P_value);
-    M_cmdLine = new SendingMessage(M_scenario, P_value, true /* skip sanity */);
-  }
-}
-
 #ifdef PCAPPLAY
 void CAction::setPcapArgs (pcap_pkts  *  P_value)
 {
@@ -426,6 +414,7 @@ void CAction::setAction(CAction P_action)
 
   setLookingChar  ( P_action.getLookingChar()  );
   setCheckIt      ( P_action.getCheckIt()      );
+    setCheckItInverse      ( P_action.getCheckItInverse()      );
   setCaseIndep    ( P_action.getCaseIndep()    ); 
   setOccurence    ( P_action.getOccurence()   );
   setHeadersOnly  ( P_action.getHeadersOnly()  );
@@ -433,7 +422,6 @@ void CAction::setAction(CAction P_action)
     setMessage(P_action.M_message_str[L_i], L_i);
   }
   setRegExp       ( P_action.M_regularExpression);
-  setCmdLine      ( P_action.M_cmdLine_str     );
   setIntCmd       ( P_action.M_IntCmd          );
 #ifdef PCAPPLAY
   setPcapArgs     ( P_action.M_pcapArgs        );
@@ -445,12 +433,14 @@ CAction::CAction(scenario *scenario)
   M_action       = E_AT_NO_ACTION;
   M_varId        = 0;
   M_varInId        = 0;
+  M_varIn2Id        = 0;
 
   M_nbSubVarId    = 0;
   M_maxNbSubVarId = 0;
   M_subVarId      = NULL;
 
   M_checkIt      = false;
+    M_checkItInverse      = false;
   M_lookingPlace = E_LP_MSG;
   M_lookingChar  = NULL;
   M_caseIndep    = false;
@@ -460,7 +450,6 @@ CAction::CAction(scenario *scenario)
     M_message[i]   = NULL;
     M_message_str[i] = NULL;
   }
-  M_cmdLine      = NULL;
   M_IntCmd       = E_INTCMD_INVALID;
   M_doubleValue  = 0;
   M_stringValue  = NULL;
@@ -468,7 +457,6 @@ CAction::CAction(scenario *scenario)
 #ifdef PCAPPLAY
   M_pcapArgs     = NULL;
 #endif
-  M_cmdLine_str	 = NULL;
   M_scenario     = scenario;
   M_regExpSet    = false;
   M_regularExpression = NULL;
@@ -490,16 +478,6 @@ CAction::~CAction()
     free(M_message_str[i]);
     M_message_str[i] = NULL;
   }
-  if(M_cmdLine != NULL)
-  {
-    delete M_cmdLine;
-    M_cmdLine = NULL;
-  }
-  if(M_cmdLine_str != NULL)
-  {
-    delete M_cmdLine_str;
-    M_cmdLine_str = NULL;
-  }
   if(M_subVarId != NULL)
   {
     delete [] M_subVarId;
diff --git a/actions.hpp b/actions.hpp
index 42076a5..8e3f1b0 100644
--- a/actions.hpp
+++ b/actions.hpp
@@ -31,7 +31,7 @@ class CSample;
 #include "prepare_pcap.h"
 #endif
 
-#define MAX_ACTION_MESSAGE 2
+#define MAX_ACTION_MESSAGE 3
 
 class CAction
 {
@@ -48,9 +48,12 @@ class CAction
       E_AT_ASSIGN_FROM_GETTIMEOFDAY,
       E_AT_JUMP,
       E_AT_LOOKUP,
+      E_AT_INSERT,
+      E_AT_REPLACE,
       E_AT_PAUSE_RESTORE,
       E_AT_LOG_TO_FILE,
       E_AT_LOG_WARNING,
+      E_AT_LOG_ERROR,
       E_AT_EXECUTE_CMD,
       E_AT_EXEC_INTCMD,
       E_AT_VAR_ADD,
@@ -62,6 +65,8 @@ class CAction
       E_AT_VAR_STRCMP,
       E_AT_VAR_TRIM,
       E_AT_VERIFY_AUTH,
+      E_AT_SET_DEST,
+      E_AT_CLOSE_CON,
 #ifdef PCAPPLAY
       E_AT_PLAY_PCAP_AUDIO,
       E_AT_PLAY_PCAP_VIDEO,
@@ -73,6 +78,8 @@ class CAction
     {
       E_LP_MSG = 0,
       E_LP_HDR,
+      E_LP_BODY,
+      E_LP_VAR,
       E_LP_NB_LOOKING_PLACE
     };
 
@@ -108,15 +115,16 @@ class CAction
     T_LookingPlace getLookingPlace();
     T_Comparator   getComparator();
     bool           getCheckIt();
+    bool           getCheckItInverse();
     bool           getCaseIndep();
     bool           getHeadersOnly();
     int            getVarId();
     int            getVarInId();
+    int            getVarIn2Id();
     int            getOccurence();
     char*          getLookingChar();
     char*          getRegularExpression();
     SendingMessage *getMessage(int n = 0);  /* log specific function  */
-    SendingMessage *getCmdLine();  /* exec specific function */
     T_IntCmdType   getIntCmd();   /* exec specific function */
 #ifdef PCAPPLAY
     pcap_pkts     *getPcapPkts(); /* send_packets specific function */
@@ -126,8 +134,10 @@ class CAction
     void setLookingPlace (T_LookingPlace P_value);
     void setComparator   (T_Comparator   P_value);
     void setCheckIt      (bool           P_value);
+    void setCheckItInverse (bool           P_value);
     void setVarId        (int            P_value);
     void setVarInId      (int            P_value);
+    void setVarIn2Id      (int            P_value);
     void setLookingChar  (char*          P_value);
     void setAction       (CAction        P_action);
     void setCaseIndep    (bool           P_action);
@@ -137,7 +147,6 @@ class CAction
     void setRegExp       (char*		 P_value);  /* ereg specific function. */
     int  executeRegExp   (char* P_string, VariableTable *P_callVarTable);
     void setMessage      (char*          P_value, int n = 0);  /* log specific function  */
-    void setCmdLine      (char*          P_value);  /* exec specific function */
     void setIntCmd       (T_IntCmdType   P_type );  /* exec specific function */
     void setDistribution (CSample *      P_value);  /* sample specific function  */
     void setDoubleValue  (double         P_value);  /* assign value specific function  */
@@ -164,10 +173,12 @@ class CAction
       T_LookingPlace M_lookingPlace;
       T_Comparator   M_comp;
       bool           M_checkIt;
+      bool           M_checkItInverse;
       bool           M_caseIndep;
       bool           M_headersOnly;
       int            M_varId;
       int            M_varInId;
+      int            M_varIn2Id;
       int            M_occurence;
       int            M_nbSubVarId;
       int            M_maxNbSubVarId;
@@ -178,8 +189,6 @@ class CAction
       SendingMessage *M_message[MAX_ACTION_MESSAGE];
       char *	     M_message_str[MAX_ACTION_MESSAGE];
       /* exec specific member */
-      SendingMessage *M_cmdLine;
-      char*          M_cmdLine_str;
       T_IntCmdType   M_IntCmd;
       /* sample specific member. */
       CSample	     *M_distribution;
diff --git a/auth.c b/auth.c
index b504eec..3e96037 100644
--- a/auth.c
+++ b/auth.c
@@ -22,7 +22,7 @@
  *           Frederique Aurouet	
  */
 
-#if defined( __FreeBSD__) || defined(__DARWIN)
+#if defined( __FreeBSD__) || defined(__DARWIN) || defined(__SUNOS)
 #include <sys/types.h>
 #endif
 #include <stdlib.h>
@@ -31,6 +31,7 @@
 #include <ctype.h>
 #include <openssl/md5.h>
 #include "milenage.h"
+#include "screen.hpp"
 
 #define MAX_HEADER_LEN  2049
 #define MD5_HASH_SIZE 16
@@ -219,7 +220,6 @@ int createAuthHeaderMD5(char * user, char * password, int password_len, char * m
     unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1];
     unsigned char resp_hex[HASH_HEX_SIZE+1], body_hex[HASH_HEX_SIZE+1];
     char tmp[MAX_HEADER_LEN], authtype[16], cnonce[32], nc[32], opaque[64];
-    char *start, *end;
     static unsigned int mync = 1;
     int has_opaque = 0;
     MD5_CTX Md5Ctx;
@@ -318,45 +318,12 @@ int createAuthHeaderMD5(char * user, char * password, int password_len, char * m
     return 1;
 }
 
-int verifyAuthHeader(char * user, char * password, char * method, char * auth) {
-  char algo[MAX_HEADER_LEN];
-  char result[HASH_HEX_SIZE];
-  char response[HASH_HEX_SIZE + 2];
-  char realm[MAX_HEADER_LEN];
-  char nonce[MAX_HEADER_LEN];
-  char uri[MAX_HEADER_LEN];
-  char *start, *end;
-  int len;
-
-  if ((start = stristr(auth, "Digest")) == NULL) {
-    WARNING("verifyAuthHeader: authentication must be digest is %s", auth);
-    return 0;
-  }
-
-  len = getAuthParameter("algorithm", auth, algo, sizeof(algo));
-  if (algo[0] == '\0') {
-    strcpy(algo, "MD5");
-  }
-  if (strncasecmp(algo, "MD5", 3)==0) {
-    getAuthParameter("realm", auth, realm, sizeof(realm));
-    getAuthParameter("uri", auth, uri, sizeof(uri));
-    getAuthParameter("nonce", auth, nonce, sizeof(nonce));
-    createAuthResponseMD5(user,password,strlen(password),method,uri,realm,nonce,result);
-    getAuthParameter("response", auth, response, sizeof(response));
-    return !strcmp(result, response);
-  }else{
-    WARNING("createAuthHeader: authentication must use MD5 or AKAv1-MD5, value is '%s'", algo);
-    return 0;
-  }
-}
-
 int createAuthResponseMD5(char * user, char * password, int password_len, char * method,
-                     char * uri, char * realm, char *nonce, char * result) {
+                     char * uri, char * realm, char *nonce, unsigned char * result) {
     unsigned char ha1[MD5_HASH_SIZE], ha2[MD5_HASH_SIZE];
     unsigned char resp[MD5_HASH_SIZE];
     unsigned char ha1_hex[HASH_HEX_SIZE+1], ha2_hex[HASH_HEX_SIZE+1];
     char tmp[MAX_HEADER_LEN];
-    char *start, *end;
     MD5_CTX Md5Ctx;
 
     // Load in A1 
@@ -395,6 +362,39 @@ int createAuthResponseMD5(char * user, char * password, int password_len, char *
     return 1;
 }
 
+int verifyAuthHeader(char * user, char * password, char * method, char * auth) {
+  char algo[MAX_HEADER_LEN];
+  unsigned char result[HASH_HEX_SIZE];
+  char response[HASH_HEX_SIZE + 2];
+  char realm[MAX_HEADER_LEN];
+  char nonce[MAX_HEADER_LEN];
+  char uri[MAX_HEADER_LEN];
+  char *start;
+  int len;
+
+  if ((start = stristr(auth, "Digest")) == NULL) {
+    WARNING("verifyAuthHeader: authentication must be digest is %s", auth);
+    return 0;
+  }
+
+  len = getAuthParameter("algorithm", auth, algo, sizeof(algo));
+  if (algo[0] == '\0') {
+    strcpy(algo, "MD5");
+  }
+  if (strncasecmp(algo, "MD5", 3)==0) {
+    getAuthParameter("realm", auth, realm, sizeof(realm));
+    getAuthParameter("uri", auth, uri, sizeof(uri));
+    getAuthParameter("nonce", auth, nonce, sizeof(nonce));
+    createAuthResponseMD5(user,password,strlen(password),method,uri,realm,nonce,result);
+    getAuthParameter("response", auth, response, sizeof(response));
+    return !strcmp((char *)result, response);
+  }else{
+    WARNING("createAuthHeader: authentication must use MD5 or AKAv1-MD5, value is '%s'", algo);
+    return 0;
+  }
+}
+
+
 
 /*"
 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";*/
diff --git a/call.cpp b/call.cpp
index 4ba59b6..6830304 100644
--- a/call.cpp
+++ b/call.cpp
@@ -33,6 +33,9 @@
  *           Ben Evans from Open Cloud
  *           Marc Van Diest from Belgacom
  *           Michael Dwyer from Cibation
+ *           Roland Meub
+ *           Andy Aicken
+ *	     Martin H. VanLeeuwen
  */
 
 #include <iterator>
@@ -49,6 +52,8 @@
 #include "deadcall.hpp"
 #include "assert.h"
 
+#define callDebug(args...) do { if (useCallDebugf) { _callDebug( args ); } } while (0)
+
 #ifdef _USE_OPENSSL
 extern  SSL                 *ssl_list[];
 extern  struct pollfd        pollfiles[];
@@ -61,6 +66,10 @@ extern  map<string, struct sipp_socket *>     map_perip_fd;
 /* send_packets pthread wrapper */
 void *send_wrapper(void *);
 #endif
+int call::dynamicId       = 0;
+int call::maxDynamicId    = 10000+2000*4;      // FIXME both param to be in command line !!!!
+int call::startDynamicId  = 10000;             // FIXME both param to be in command line !!!!
+int call::stepDynamicId   = 4;                // FIXME both param to be in command line !!!!
 
 /************** Call map and management routines **************/
 static unsigned int next_number = 1;
@@ -142,13 +151,13 @@ uint32_t get_remote_ip_media(char *msg)
  * Look for "c=IN IP6 " pattern in the message and extract the following value
  * which should be IPv6 address
  */
-uint8_t get_remote_ipv6_media(char *msg, struct in6_addr addr)
+uint8_t get_remote_ipv6_media(char *msg, struct in6_addr *addr)
 {
     char pattern[] = "c=IN IP6 ";
     char *begin, *end;
     char ip[128];
 
-    memset(&addr, 0, sizeof(addr));
+    memset(addr, 0, sizeof(*addr));
     memset(ip, 0, 128);
 
     begin = strstr(msg, pattern);
@@ -161,7 +170,7 @@ uint8_t get_remote_ipv6_media(char *msg, struct in6_addr addr)
     if (!end)
       return 0;
     strncpy(ip, begin, end - begin);
-    if (!inet_pton(AF_INET6, ip, &addr)) {
+    if (!inet_pton(AF_INET6, ip, addr)) {
       return 0;
     }
     return 1;
@@ -177,7 +186,7 @@ uint16_t get_remote_port_media(char *msg, int pattype)
 {
     char *pattern;
     char *begin, *end;
-    char number[6];
+    char number[7];
 
     if (pattype == PAT_AUDIO) {
       pattern = "m=audio ";
@@ -208,7 +217,7 @@ void call::get_remote_media_addr(char *msg) {
   uint16_t video_port, audio_port;
   if (media_ip_is_ipv6) {
   struct in6_addr ip_media;
-    if (get_remote_ipv6_media(msg, ip_media)) {
+    if (get_remote_ipv6_media(msg, &ip_media)) {
       audio_port = get_remote_port_media(msg, PAT_AUDIO);
       if (audio_port) {
         /* We have audio in the SDP: set the to_audio addr */
@@ -257,27 +266,61 @@ void call::get_remote_media_addr(char *msg) {
 
 /******* Very simple hash for retransmission detection  *******/
 
-unsigned long hash(char * msg) {
+unsigned long call::hash(char * msg) {
   unsigned long hash = 0;
   int c;
 
-  while (c = *msg++)
-    hash = c + (hash << 6) + (hash << 16) - hash;
+  if (rtcheck == RTCHECK_FULL) {
+    while ((c = *msg++))
+      hash = c + (hash << 6) + (hash << 16) - hash;
+  } else if (rtcheck == RTCHECK_LOOSE) {
+    /* Based on section 11.5 (bullet 2) of RFC2543 we only take into account
+     * the To, From, Call-ID, and CSeq values. */
+      char *hdr = get_header_content(msg,"To:");
+      while ((c = *hdr++))
+	hash = c + (hash << 6) + (hash << 16) - hash;
+      hdr = get_header_content(msg,"From:");
+      while ((c = *hdr++))
+	hash = c + (hash << 6) + (hash << 16) - hash;
+      hdr = get_header_content(msg,"Call-ID:");
+      while ((c = *hdr++))
+	hash = c + (hash << 6) + (hash << 16) - hash;
+      hdr = get_header_content(msg,"CSeq:");
+      while ((c = *hdr++))
+	hash = c + (hash << 6) + (hash << 16) - hash;
+      /* For responses, we should also consider the code and body (if any),
+       * because they are not nearly as well defined as the request retransmission. */
+      if (!strncmp(msg, "SIP/2.0", strlen("SIP/2.0"))) {
+	/* Add the first line into the hash. */
+	hdr = msg + strlen("SIP/2.0");
+	while ((c = *hdr++) && (c != '\r'))
+	  hash = c + (hash << 6) + (hash << 16) - hash;
+	/* Add the body (if any) into the hash. */
+	hdr = strstr(msg, "\r\n\r\n");
+	if (hdr) {
+	  hdr += strlen("\r\n\r\n");
+	  while ((c = *hdr++))
+	    hash = c + (hash << 6) + (hash << 16) - hash;
+	}
+      }
+  } else {
+    ERROR("Internal error: Invalid rtcheck %d\n", rtcheck);
+  }
 
   return hash;
 }
 
 /******************* Call class implementation ****************/
 call::call(char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest) : listener(p_id, true) {
-  init(main_scenario, NULL, dest, p_id, userId, use_ipv6, false);
+  init(main_scenario, NULL, dest, p_id, userId, use_ipv6, false, false);
 }
 
 call::call(char *p_id, struct sipp_socket *socket, struct sockaddr_storage *dest) : listener(p_id, true) {
-  init(main_scenario, socket, dest, p_id, 0 /* No User. */, socket->ss_ipv6, false /* Not Auto. */);
+  init(main_scenario, socket, dest, p_id, 0 /* No User. */, socket->ss_ipv6, false /* Not Auto. */, false);
 }
 
-call::call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic) : listener(p_id, true) {
-  init(call_scenario, socket, dest, p_id, userId, ipv6, isAutomatic);
+call::call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitialization) : listener(p_id, true) {
+  init(call_scenario, socket, dest, p_id, userId, ipv6, isAutomatic, isInitialization);
 }
 
 call *call::add_call(int userId, bool ipv6, struct sockaddr_storage *dest)
@@ -312,17 +355,22 @@ call *call::add_call(int userId, bool ipv6, struct sockaddr_storage *dest)
   }
   call_id[count] = 0;
 
-  return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */);
+  return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */, false);
 }
 
 
-void call::init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic)
+void call::init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall)
 {
   this->call_scenario = call_scenario;
   zombie = false;
+
+  debugBuffer = NULL;
+  debugLength = 0;
+
   msg_index = 0;
   last_send_index = 0;
   last_send_msg = NULL;
+  last_send_len = 0;
 
   last_recv_hash = 0;
   last_recv_index = -1;
@@ -412,16 +460,24 @@ void call::init(scenario * call_scenario, struct sipp_socket *socket, struct soc
     userVars->putTable();
   }
 
-  if (call_scenario->maxTxnUsed > 0) {
-    txnID = (char **)malloc(sizeof(char *) * call_scenario->maxTxnUsed);
-    memset(txnID, 0, sizeof(char *) * call_scenario->maxTxnUsed);
+  if (call_scenario->transactions.size() > 0) {
+    transactions = (struct txnInstanceInfo *)malloc(sizeof(txnInstanceInfo) * call_scenario->transactions.size());
+    memset(transactions, 0, sizeof(struct txnInstanceInfo) * call_scenario->transactions.size());
   } else {
-    txnID = NULL;
+    transactions = NULL;
   }
 
   // If not updated by a message we use the start time 
   // information to compute rtd information
-  for (i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
+  start_time_rtd = (unsigned long long *)malloc(sizeof(unsigned long long) * call_scenario->stats->nRtds());
+  if (!start_time_rtd) {
+    ERROR("Could not allocate RTD times!");
+  }
+  rtd_done = (bool *)malloc(sizeof(bool) * call_scenario->stats->nRtds());
+  if (!start_time_rtd) {
+    ERROR("Could not allocate RTD done!");
+  }
+  for (i = 0; i < call_scenario->stats->nRtds(); i++) {
     start_time_rtd[i] = getmicroseconds();
     rtd_done[i] = false;
   }
@@ -445,6 +501,7 @@ void call::init(scenario * call_scenario, struct sipp_socket *socket, struct soc
   } else {
     m_lineNumber = NULL;
   }
+  this->initCall = isInitCall;
 
 #ifdef PCAPPLAY
   memset(&(play_args_a.to), 0, sizeof(struct sockaddr_storage));
@@ -483,17 +540,46 @@ void call::init(scenario * call_scenario, struct sipp_socket *socket, struct soc
     }
   }
 
+  callDebug("Starting call %s\n", id);
+
   setRunning();
 }
 
+int call::_callDebug(char *fmt, ...) {
+    va_list ap;
+
+    if (!useCallDebugf) {
+      return 0;
+    }
+
+    /* First we figure out how much to allocate. */
+    va_start(ap, fmt);
+    int ret = vsnprintf(NULL, 0, fmt, ap);
+    va_end(ap);
+
+    debugBuffer = (char *)realloc(debugBuffer, debugLength + ret + TIME_LENGTH + 2);
+    if (!debugBuffer) {
+      ERROR("Could not allocate buffer (%d bytes) for callDebug file!", debugLength + ret + TIME_LENGTH + 2);
+    }
+
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    debugLength += snprintf(debugBuffer + debugLength, TIME_LENGTH + 2, "%s ", CStat::formatTime(&now));
+
+    va_start(ap, fmt);
+    debugLength += vsnprintf(debugBuffer + debugLength, ret + 1, fmt, ap);
+    va_end(ap);
+
+    return ret;
+}
+
 call::~call()
 {
   computeStat(CStat::E_ADD_CALL_DURATION, clock_tick - start_time);
 
   if(comp_state) { comp_free(&comp_state); }
 
-
-  if (call_remote_socket) {
+  if (call_remote_socket && (call_remote_socket != main_remote_socket)) {
     sipp_close_socket(call_remote_socket);
   }
 
@@ -505,18 +591,14 @@ call::~call()
     delete m_lineNumber;
   }
   if (userId) {
-    if (call_scenario->stats->GetStat(CStat::CPT_C_CurrentCall) >= open_calls_allowed) {
-      retiredUsers.push_front(userId);
-    } else {
-      freeUsers.push_front(userId);
-    }
+    opentask::freeUser(userId);
   }
 
-  if (txnID) {
-    for (int i = 0; i < call_scenario->maxTxnUsed; i++) {
-      free(txnID[i]);
+  if (transactions) {
+    for (unsigned int i = 0; i < call_scenario->transactions.size(); i++) {
+      free(transactions[i].txnID);
     }
-    free(txnID);
+    free(transactions);
   }
 
   if(last_recv_msg) { free(last_recv_msg); }
@@ -541,17 +623,38 @@ call::~call()
   if (use_tdmmap) {
     tdm_map[tdm_map_number] = false;
   }
+  
+# ifdef PCAPPLAY
+  if (media_thread != 0) {
+    pthread_cancel(media_thread);
+    pthread_join(media_thread, NULL);
+  }
+#endif
+
+
+  free(start_time_rtd);
+  free(rtd_done);
+  free(debugBuffer);
 }
 
 void call::computeStat (CStat::E_Action P_action) {
+  if (initCall) {
+    return;
+  }
   call_scenario->stats->computeStat(P_action);
 }
 
 void call::computeStat (CStat::E_Action P_action, unsigned long P_value) {
+  if (initCall) {
+    return;
+  }
   call_scenario->stats->computeStat(P_action, P_value);
 }
 
 void call::computeStat (CStat::E_Action P_action, unsigned long P_value, int which) {
+  if (initCall) {
+    return;
+  }
   call_scenario->stats->computeStat(P_action, P_value, which);
 }
 
@@ -560,16 +663,16 @@ void call::dump() {
   char s[MAX_HEADER_LEN];
   sprintf(s, "%s: State %d", id, msg_index);
   if (next_retrans) {
-    sprintf(s, "%s (next retrans %ld)", s, next_retrans);
+    sprintf(s, "%s (next retrans %u)", s, next_retrans);
   }
   if (paused_until) {
-    sprintf(s, "%s (paused until %ld)", s, paused_until);
+    sprintf(s, "%s (paused until %u)", s, paused_until);
   }
   if (recv_timeout) {
-    sprintf(s, "%s (recv timeout %ld)", s, recv_timeout);
+    sprintf(s, "%s (recv timeout %u)", s, recv_timeout);
   }
   if (send_timeout) {
-    sprintf(s, "%s (send timeout %ld)", s, send_timeout);
+    sprintf(s, "%s (send timeout %u)", s, send_timeout);
   }
   WARNING("%s", s);
 }
@@ -584,13 +687,13 @@ bool call::connect_socket_if_needed()
   if(transport == T_UDP) {
     struct sockaddr_storage saddr;
 
-    if(toolMode != MODE_CLIENT)
+    if(sendMode != MODE_CLIENT)
       return true;
 
     char peripaddr[256];
     if (!peripsocket) {
       if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) {
-	ERROR_NO("Unable to get a UDP socket");
+	 ERROR_NO("Unable to get a UDP socket (1)");
       }
     } else {
       char *tmp = peripaddr;
@@ -600,7 +703,7 @@ bool call::connect_socket_if_needed()
       if (i == map_perip_fd.end()) {
 	// Socket does not exist
 	if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) {
-	  ERROR_NO("Unable to get a UDP socket");
+	  ERROR_NO("Unable to get a UDP socket (2)");
 	} else {
 	  /* Ensure that it stays persistent, because it is recorded in the map. */
 	  call_socket->ss_count++;
@@ -729,10 +832,12 @@ bool call::lost(int index)
   return (((double)rand() / (double)RAND_MAX) < (percent / 100.0));
 }
 
-int call::send_raw(char * msg, int index) 
+int call::send_raw(char * msg, int index, int len) 
 {
   struct sipp_socket *sock;
   int rc;
+
+  callDebug("Sending %s message for call %s (index %d, hash %u):\n%s\n\n", TRANSPORT_TO_STRING(transport), id, index, hash(msg), msg);
  
   if (useShortMessagef == 1) {
       struct timeval currentTime;
@@ -744,6 +849,7 @@ int call::send_raw(char * msg, int index)
  
   if((index!=-1) && (lost(index))) {
     TRACE_MSG("%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport));
+    callDebug("%s message voluntary lost (while sending) (index %d, hash %u).\n", TRANSPORT_TO_STRING(transport), index, hash(msg));
     
     if(comp_state) { comp_free(&comp_state); }
     call_scenario->messages[index] -> nb_lost++;
@@ -752,31 +858,48 @@ int call::send_raw(char * msg, int index)
   
   sock = call_socket;
 
-  if ((use_remote_sending_addr) && (toolMode == MODE_SERVER)) {
+  if ((use_remote_sending_addr) && (sendMode == MODE_SERVER)) {
     if (!call_remote_socket) {
-      struct sockaddr_storage *L_dest = &remote_sending_sockaddr;
+      if (multisocket || !main_remote_socket) {
+	struct sockaddr_storage *L_dest = &remote_sending_sockaddr;
 
-      if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) {
-	ERROR_NO("Unable to get a socket for rsa option");
-      }
+	if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) {
+	  ERROR_NO("Unable to get a socket for rsa option");
+	}
 
-      sipp_customize_socket(call_remote_socket);
+	sipp_customize_socket(call_remote_socket);
 
-      if(transport != T_UDP) {
-	if (sipp_connect_socket(call_remote_socket, L_dest)) {
-	  if(errno == EINVAL){
-	    /* This occurs sometime on HPUX but is not a true INVAL */
-	    ERROR("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport));
-	  } else {
-	    ERROR_NO("Unable to connect a socket for rsa option");
+	if(transport != T_UDP) {
+	  if (sipp_connect_socket(call_remote_socket, L_dest)) {
+	    if(errno == EINVAL){
+	      /* This occurs sometime on HPUX but is not a true INVAL */
+	      ERROR("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport));
+	    } else {
+	      ERROR_NO("Unable to connect a socket for rsa option");
+	    }
 	  }
 	}
+	if (!multisocket) {
+	  main_remote_socket = call_remote_socket;
+	}
+      }
+
+      if (!multisocket) {
+	call_remote_socket = main_remote_socket;
+	main_remote_socket->ss_count++;
       }
     }
     sock=call_remote_socket ;
   }
 
-  rc = write_socket(sock, msg, strlen(msg), WS_BUFFER, &call_peer);
+  // If the length hasn't been explicitly specified, treat the message as a string
+  if (len==0) {
+    len = strlen(msg);
+  }
+  
+  assert(sock);
+
+  rc = write_socket(sock, msg, len, WS_BUFFER, &call_peer);
   if(rc == -1 && errno == EWOULDBLOCK) {
     return -1;
   }
@@ -792,10 +915,10 @@ int call::send_raw(char * msg, int index)
 
 /* This method is used to send messages that are not */
 /* part of the XML scenario                          */
-void call::sendBuffer(char * msg)
+void call::sendBuffer(char * msg, int len)
 {
   /* call send_raw but with a special scenario index */
-  if (send_raw(msg, -1) < 0) {
+  if (send_raw(msg, -1, len) < 0) {
     if (sendbuffer_warn) {
       ERROR_NO("Error sending raw message");
     } else {
@@ -905,7 +1028,7 @@ char * call::get_header(char* message, char * name, bool content)
     src = message;
     dest = last_header;
 
-    while(src = strcasestr2(src, src_tmp)) {
+    while((src = strcasestr2(src, src_tmp))) {
       if (content || !first_time) {
         /* just want the header's content */
         src += strlen(name) + 1;
@@ -1078,14 +1201,15 @@ char * call::get_last_request_uri ()
   
 }
 
-char * call::send_scene(int index, int *send_status)
+char * call::send_scene(int index, int *send_status, int *len)
 {
-  static char msg_buffer[SIPP_MAX_MSG_SIZE];
-
 #define MAX_MSG_NAME_SIZE 30
   static char msg_name[MAX_MSG_NAME_SIZE];
   char *L_ptr1 ;
   char *L_ptr2 ;
+  int uselen = 0;
+
+  assert(send_status);
 
   /* Socket port must be known before string substitution */
   if (!connect_socket_if_needed()) {
@@ -1102,13 +1226,16 @@ char * call::send_scene(int index, int *send_status)
 
   assert(call_scenario->messages[index]->send_scheme);
 
+  if (!len) {
+	len = &uselen;
+  }
+
   char * dest;
-  dest = createSendingMessage(call_scenario->messages[index] -> send_scheme, index);
-  strcpy(msg_buffer, dest);
+  dest = createSendingMessage(call_scenario->messages[index] -> send_scheme, index, len);
 
   if (dest) {
     L_ptr1=msg_name ;
-    L_ptr2=msg_buffer ;
+    L_ptr2=dest ;
     while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t'))  {
       *L_ptr1 = *L_ptr2;
       L_ptr1 ++;
@@ -1122,27 +1249,23 @@ char * call::send_scene(int index, int *send_status)
     ack_is_pending = false ;
   }
 
-  if(send_status) {
-    *send_status = send_raw(msg_buffer, index);
-  } else {
-    send_raw(msg_buffer, index);
-  }
+  *send_status = send_raw(dest, index, *len);
 
-  return msg_buffer;
+  return dest;
 }
 
-void call::do_bookkeeping(int index) {
+void call::do_bookkeeping(message *curmsg) {
   /* If this message increments a counter, do it now. */
-  if(int counter = call_scenario->messages[index] -> counter) {
+  if(int counter = curmsg -> counter) {
     computeStat(CStat::E_ADD_GENERIC_COUNTER, 1, counter - 1);
   }
 
   /* If this message can be used to compute RTD, do it now */
-  if(int rtd = call_scenario->messages[index] -> start_rtd) {
+  if(int rtd = curmsg -> start_rtd) {
     start_time_rtd[rtd - 1] = getmicroseconds();
   }
 
-  if(int rtd = call_scenario->messages[index] -> stop_rtd) {
+  if(int rtd = curmsg -> stop_rtd) {
     if (!rtd_done[rtd - 1]) {
       unsigned long long start = start_time_rtd[rtd - 1];
       unsigned long long end = getmicroseconds();
@@ -1154,7 +1277,7 @@ void call::do_bookkeeping(int index) {
       computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION,
 	  (end - start) / 1000, rtd - 1);
 
-      if (!call_scenario->messages[index] -> repeat_rtd) {
+      if (!curmsg -> repeat_rtd) {
 	rtd_done[rtd - 1] = true;
       }
     }
@@ -1165,7 +1288,7 @@ void call::tcpClose() {
   terminate(CStat::E_FAILED_TCP_CLOSED);
 }
 
-bool call::terminate(CStat::E_Action reason) {
+void call::terminate(CStat::E_Action reason) {
   char reason_str[100];
 
   stopListening();
@@ -1176,19 +1299,35 @@ bool call::terminate(CStat::E_Action reason) {
       case call::E_AR_REGEXP_DOESNT_MATCH:
 	computeStat(CStat::E_CALL_FAILED);
 	computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH);
-	if (deadcall_wait) {
+	if (deadcall_wait && !initCall) {
 	  sprintf(reason_str, "regexp match failure at index %d", msg_index);
 	  new deadcall(id, reason_str);
 	}
 	break;
+            case call::E_AR_REGEXP_SHOULDNT_MATCH:
+                computeStat(CStat::E_CALL_FAILED);
+                computeStat(CStat::E_FAILED_REGEXP_SHOULDNT_MATCH);
+                if (deadcall_wait && !initCall) {
+                    sprintf(reason_str, "regexp matched, but shouldn't at index %d", msg_index);
+                    new deadcall(id, reason_str);
+                }
+                break;
       case call::E_AR_HDR_NOT_FOUND:
 	computeStat(CStat::E_CALL_FAILED);
 	computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND);
-	if (deadcall_wait) {
+	if (deadcall_wait && !initCall) {
 	  sprintf(reason_str, "regexp header not found at index %d", msg_index);
 	  new deadcall(id, reason_str);
 	}
 	break;
+      case E_AR_CONNECT_FAILED:
+	computeStat(CStat::E_CALL_FAILED);
+	computeStat(CStat::E_FAILED_TCP_CONNECT);
+	if (deadcall_wait && !initCall) {
+	  sprintf(reason_str, "connection failed %d", msg_index);
+	  new deadcall(id, reason_str);
+	}
+	break;
       case call::E_AR_NO_ERROR:
       case call::E_AR_STOP_CALL:
 	/* Do nothing. */
@@ -1197,7 +1336,7 @@ bool call::terminate(CStat::E_Action reason) {
   } else {
     if (reason == CStat::E_CALL_SUCCESSFULLY_ENDED || timewait) {
       computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED);
-      if (deadcall_wait) {
+      if (deadcall_wait && !initCall) {
 	new deadcall(id, "successful");
       }
     } else {
@@ -1205,7 +1344,7 @@ bool call::terminate(CStat::E_Action reason) {
       if (reason != CStat::E_NO_ACTION) {
 	computeStat(reason);
       }
-      if (deadcall_wait) {
+      if (deadcall_wait && !initCall) {
 	sprintf(reason_str, "failed at index %d", msg_index);
 	new deadcall(id, reason_str);
       }
@@ -1216,24 +1355,29 @@ bool call::terminate(CStat::E_Action reason) {
 
 bool call::next()
 {
-  int test = call_scenario->messages[msg_index]->test;
+  msgvec * msgs = &call_scenario->messages;
+  if (initCall) {
+    msgs = &call_scenario->initmessages;
+  }
+
+  int test = (*msgs)[msg_index]->test;
   /* What is the next message index? */
   /* Default without branching: use the next message */
   int new_msg_index = msg_index+1;
   /* If branch needed, overwrite this default */
-  if ( (call_scenario->messages[msg_index]->next >= 0) &&
+  if ( ((*msgs)[msg_index]->next >= 0) &&
        ((test == -1) || M_callVariableTable->getVar(test)->isSet())
      ) {
     /* Branching possible, check the probability */
-    int chance = call_scenario->messages[msg_index]->chance;
+    int chance = (*msgs)[msg_index]->chance;
     if ((chance <= 0) || (rand() > chance )) {
       /* Branch == overwrite with the 'next' attribute value */
-      new_msg_index = call_scenario->messages[msg_index]->next;
+      new_msg_index = (*msgs)[msg_index]->next;
     }
   }
   msg_index=new_msg_index;
   recv_timeout = 0;
-  if(msg_index >= call_scenario->length) {
+  if(msg_index >= (int)((*msgs).size())) {
     terminate(CStat::E_CALL_SUCCESSFULLY_ENDED);
     return false;
   }
@@ -1241,100 +1385,8 @@ bool call::next()
   return run();
 }
 
-bool call::run()
-{
-  bool            bInviteTransaction = false;
-  int             actionResult = 0;
-
-  assert(running);
-
-  if (zombie) {
-    delete this;
-    return false;
-  }
-
-  clock_tick = getmilliseconds();
-
-  if(msg_index >= call_scenario->length) {
-    ERROR("Scenario overrun for call %s (%p) (index = %d)\n",
-             id, this, msg_index);
-  }
-
-  message *curmsg = call_scenario->messages[msg_index];
-
-  /* Manages retransmissions or delete if max retrans reached */
-  if(next_retrans && (next_retrans < clock_tick)) {
-    nb_retrans++;
-
-    if ( (0 == strncmp (last_send_msg, "INVITE", 6)) )
-    {
-      bInviteTransaction = true;
-    }
-
-    if((nb_retrans > (bInviteTransaction ? max_invite_retrans : max_non_invite_retrans)) ||
-       (nb_retrans > max_udp_retrans)) {
-      call_scenario->messages[last_send_index] -> nb_timeout ++;
-      if (call_scenario->messages[last_send_index]->on_timeout >= 0) {  // action on timeout
-          WARNING("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ",
-                      id, msg_index, call_scenario->messages[last_send_index]->on_timeout);
-          msg_index = call_scenario->messages[last_send_index]->on_timeout;
-          next_retrans = 0;
-          recv_timeout = 0;
-          if (msg_index < call_scenario->length) {
-		return true;
-	  }
-
-          // here if asked to go to the last label  delete the call
-          computeStat(CStat::E_CALL_FAILED);
-          computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
-          if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
-            // Abort the call by sending proper SIP message
-            return(abortCall());
-          } else {
-            // Just delete existing call
-            delete this;
-            return false;
-          }
-      }
-      computeStat(CStat::E_CALL_FAILED);
-      computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
-      if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
-        // Abort the call by sending proper SIP message
-        WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id);
-        return(abortCall());
-      } else {
-        // Just delete existing call
-        delete this;
-        return false;
-      }
-    } else {
-      nb_last_delay *= 2;
-      if (DEFAULT_T2_TIMER_VALUE < nb_last_delay)
-      {
-        if (!bInviteTransaction)
-        {
-          nb_last_delay = DEFAULT_T2_TIMER_VALUE;
-      }
-      }
-      if(send_raw(last_send_msg, last_send_index) < -1) {
-        return false;
-      }
-      call_scenario->messages[last_send_index] -> nb_sent_retrans++;
-      computeStat(CStat::E_RETRANSMISSION);
-      next_retrans = clock_tick + nb_last_delay;
-    }
-  }
-
-  if(paused_until) {
-    /* Process a pending pause instruction until delay expiration */
-    if(paused_until > clock_tick) {
-      setPaused();
-      return true;
-    }
-    /* Our pause is over. */
-    paused_until = 0;
-    return next();
-  } else if(curmsg -> pause_distribution || curmsg->pause_variable != -1) {
+bool call::executeMessage(message *curmsg) {
+  if(curmsg -> pause_distribution || curmsg->pause_variable != -1) {
     unsigned int pause;
     if (curmsg->pause_distribution) {
       pause  = (int)(curmsg -> pause_distribution -> sample());
@@ -1357,9 +1409,11 @@ bool call::run()
 
     /* Increment the number of sessions in pause state */
     curmsg->sessions++;
-    do_bookkeeping(msg_index);
-    actionResult = executeAction(NULL, msg_index);
-    return run(); /* In case delay is 0 */
+    do_bookkeeping(curmsg);
+    executeAction(NULL, curmsg);
+    callDebug("Pausing call until %d (is now %d).\n", paused_until, clock_tick);
+    setPaused();
+    return true;
   }
   else if(curmsg -> M_type == MSG_TYPE_SENDCMD) {
     int send_status;
@@ -1368,7 +1422,7 @@ bool call::run()
       return true;
     }
 
-    send_status = sendCmdMessage(msg_index);
+    send_status = sendCmdMessage(curmsg);
 
     if(send_status != 0) { /* Send error */
       return false; /* call deleted */
@@ -1376,18 +1430,20 @@ bool call::run()
     curmsg -> M_nbCmdSent++;
     next_retrans = 0;
 
-    do_bookkeeping(msg_index);
-    actionResult = executeAction(NULL, msg_index);
+    do_bookkeeping(curmsg);
+    executeAction(NULL, curmsg);
     return(next());
   }
   else if(curmsg -> M_type == MSG_TYPE_NOP) {
-    do_bookkeeping(msg_index);
-    actionResult = executeAction(NULL, msg_index);
+    callDebug("Executing NOP at index %d.\n", curmsg->index);
+    do_bookkeeping(curmsg);
+    executeAction(NULL, curmsg);
     return(next());
   }
 
   else if(curmsg -> send_scheme) {
     char * msg_snd;
+    int msgLen;
     int send_status;
 
     /* Do not send a new message until the previous one which had
@@ -1399,7 +1455,7 @@ bool call::run()
     }
 
     /* Handle counters and RTDs for this message. */
-    do_bookkeeping(msg_index);
+    do_bookkeeping(curmsg);
 
     /* decide whether to increment cseq or not 
      * basically increment for anything except response, ACK or CANCEL 
@@ -1415,19 +1471,19 @@ bool call::run()
           incr_cseq = 1;
     }
     
-    msg_snd = send_scene(msg_index, &send_status);
+    msg_snd = send_scene(msg_index, &send_status, &msgLen);
     if(send_status == -1 && errno == EWOULDBLOCK) {
       if (incr_cseq) --cseq;
       /* Have we set the timeout yet? */
       if (send_timeout) {
 	/* If we have actually timed out. */
 	if (clock_tick > send_timeout) {
-	  WARNING("Call-Id: %s, send timeout on message %d: aborting call",
-	      id, msg_index);
+	  WARNING("Call-Id: %s, send timeout on message %s:%d: aborting call",
+	      id, curmsg->desc, curmsg->index);
 	  computeStat(CStat::E_CALL_FAILED);
 	  computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND);
 	  if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
-	    return (abortCall());
+	    return (abortCall(true));
 	  } else {
 	    delete this;
 	    return false;
@@ -1442,21 +1498,25 @@ bool call::run()
       }
       return true; /* No step, nothing done, retry later */
     } else if(send_status < 0) { /* Send error */
-      /* The timeout will not be sent, so the timeout is no longer needed. */
-      send_timeout = 0;
-      /* The call was already deleted by connect_socket_if_needed or send_raw. */
+      /* The call was already deleted by connect_socket_if_needed or send_raw,
+       * so we should no longer access members. */
       return false;
     }
     /* We have sent the message, so the timeout is no longer needed. */
     send_timeout = 0;
 
-    last_send_index = msg_index;
-    last_send_msg = (char *) realloc(last_send_msg, strlen(msg_snd) + 1);
-    strcpy(last_send_msg, msg_snd);
+    last_send_index = curmsg->index;
+    last_send_len = msgLen;
+    last_send_msg = (char *) realloc(last_send_msg, msgLen+1);
+    memcpy(last_send_msg, msg_snd, msgLen);
+    last_send_msg[msgLen] = '\0';
 
     if (curmsg->start_txn) {
-      txnID[curmsg->start_txn - 1] = (char *)realloc(txnID[curmsg->start_txn - 1], MAX_HEADER_LEN);
-      extract_transaction(txnID[curmsg->start_txn - 1], last_send_msg);
+      transactions[curmsg->start_txn - 1].txnID = (char *)realloc(transactions[curmsg->start_txn - 1].txnID, MAX_HEADER_LEN);
+      extract_transaction(transactions[curmsg->start_txn - 1].txnID, last_send_msg);
+    }
+    if (curmsg->ack_txn) {
+      transactions[curmsg->ack_txn - 1].ackIndex = curmsg->index;
     }
 
     if(last_recv_index >= 0) {
@@ -1464,7 +1524,9 @@ bool call::run()
        * chance that we will be asked to retransmit this message */
       recv_retrans_hash       = last_recv_hash;
       recv_retrans_recv_index = last_recv_index;
-      recv_retrans_send_index = msg_index;
+      recv_retrans_send_index = curmsg->index;
+
+      callDebug("Set Retransmission Hash: %u (recv index %d, send index %d)\n", recv_retrans_hash, recv_retrans_recv_index, recv_retrans_send_index);
 
       /* Prevent from detecting the cause relation between send and recv 
        * in the next valid send */
@@ -1482,7 +1544,7 @@ bool call::run()
       next_retrans = 0;
     }
 
-    actionResult = executeAction(msg_snd, msg_index);
+    executeAction(msg_snd, curmsg);
 
     /* Update scenario statistics */
     curmsg -> nb_sent++;
@@ -1498,7 +1560,7 @@ bool call::run()
       free(msg);
       return ret;
     } else if (recv_timeout) {
-      if(recv_timeout > clock_tick || recv_timeout > getmilliseconds()) {
+      if(recv_timeout > getmilliseconds()) {
 	setPaused();
 	return true;
       }
@@ -1506,27 +1568,29 @@ bool call::run()
       curmsg->nb_timeout++;
       if (curmsg->on_timeout < 0) {
         // if you set a timeout but not a label, the call is aborted 
-        WARNING("Call-Id: %s, receive timeout on message %d without label to jump to (ontimeout attribute): aborting call",
-                   id, msg_index);
+        WARNING("Call-Id: %s, receive timeout on message %s:%d without label to jump to (ontimeout attribute): aborting call",
+                   id, curmsg->desc, curmsg->index);
         computeStat(CStat::E_CALL_FAILED);
         computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
         if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
-          return (abortCall());
+          return (abortCall(true));
         } else {
           delete this;
           return false;
         }
       }
-      WARNING("Call-Id: %s, receive timeout on message %d, jumping to label %d",
-                  id, msg_index, curmsg->on_timeout);
+      WARNING("Call-Id: %s, receive timeout on message %s:%d, jumping to label %d",
+                  id, curmsg->desc, curmsg->index, curmsg->on_timeout);
+      /* FIXME: We should do something like set index here, but it probably
+       * does not matter too much as only nops are allowed in the init stanza. */
       msg_index = curmsg->on_timeout;
       recv_timeout = 0;
-      if (msg_index < call_scenario->length) return true;
+      if (msg_index < (int)call_scenario->messages.size()) return true;
       // special case - the label points to the end - finish the call
       computeStat(CStat::E_CALL_FAILED);
       computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
       if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
-        return (abortCall());
+        return (abortCall(true));
       } else {
         delete this;
         return false;
@@ -1543,10 +1607,133 @@ bool call::run()
 	/* We are going to wait forever. */
 	setPaused();
     }
+  } else {
+    WARNING("Unknown message type at %s:%d: %d", curmsg->desc, curmsg->index, curmsg->M_type);
   }
   return true;
 }
 
+bool call::run()
+{
+  bool            bInviteTransaction = false;
+
+  assert(running);
+
+  if (zombie) {
+    delete this;
+    return false;
+  }
+
+  getmilliseconds();
+
+  message *curmsg;
+  if (initCall) {
+    if(msg_index >= (int)call_scenario->initmessages.size()) {
+      ERROR("Scenario initialization overrun for call %s (%p) (index = %d)\n", id, this, msg_index);
+    }
+    curmsg = call_scenario->initmessages[msg_index];
+  } else {
+    if(msg_index >= (int)call_scenario->messages.size()) {
+      ERROR("Scenario overrun for call %s (%p) (index = %d)\n", id, this, msg_index);
+    }
+    curmsg = call_scenario->messages[msg_index];
+  }
+
+  callDebug("Processing message %d of type %d for call %s at %u.\n", msg_index, curmsg->M_type, id, clock_tick);
+
+  if (curmsg->condexec != -1) {
+    bool exec = M_callVariableTable->getVar(curmsg->condexec)->isSet();
+    if (curmsg->condexec_inverse) {
+	exec = !exec;
+    }
+    if (!exec) {
+     callDebug("Conditional variable %s %s set, so skipping message %d.\n", call_scenario->allocVars->getName(curmsg->condexec), curmsg->condexec_inverse ? "" : "not", msg_index);
+     return next();
+    }
+  }
+
+  /* Manages retransmissions or delete if max retrans reached */
+  if(next_retrans && (next_retrans < clock_tick)) {
+    nb_retrans++;
+
+    if ( (0 == strncmp (last_send_msg, "INVITE", 6)) )
+    {
+      bInviteTransaction = true;
+    }
+
+    int rtAllowed = min(bInviteTransaction ? max_invite_retrans : max_non_invite_retrans, max_udp_retrans);
+
+    callDebug("Retransmisison required (%d retransmissions, max %d)\n", nb_retrans, rtAllowed);
+
+    if(nb_retrans > rtAllowed) {
+      call_scenario->messages[last_send_index] -> nb_timeout ++;
+      if (call_scenario->messages[last_send_index]->on_timeout >= 0) {  // action on timeout
+          WARNING("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ",
+                      id, msg_index, call_scenario->messages[last_send_index]->on_timeout);
+          msg_index = call_scenario->messages[last_send_index]->on_timeout;
+          next_retrans = 0;
+          recv_timeout = 0;
+          if (msg_index < (int)call_scenario->messages.size()) {
+		return true;
+	  }
+
+          // here if asked to go to the last label  delete the call
+          computeStat(CStat::E_CALL_FAILED);
+          computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
+          if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
+            // Abort the call by sending proper SIP message
+            return(abortCall(true));
+          } else {
+            // Just delete existing call
+            delete this;
+            return false;
+          }
+      }
+      computeStat(CStat::E_CALL_FAILED);
+      computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
+      if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
+        // Abort the call by sending proper SIP message
+        WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id);
+        return(abortCall(true));
+      } else {
+        // Just delete existing call
+        delete this;
+        return false;
+      }
+    } else {
+      nb_last_delay *= 2;
+      if (DEFAULT_T2_TIMER_VALUE < nb_last_delay)
+      {
+        if (!bInviteTransaction)
+        {
+          nb_last_delay = DEFAULT_T2_TIMER_VALUE;
+      }
+      }
+      if(send_raw(last_send_msg, last_send_index, last_send_len) < -1) {
+        return false;
+      }
+      call_scenario->messages[last_send_index] -> nb_sent_retrans++;
+      computeStat(CStat::E_RETRANSMISSION);
+      next_retrans = clock_tick + nb_last_delay;
+    }
+  }
+
+  if(paused_until) {
+    /* Process a pending pause instruction until delay expiration */
+    if(paused_until > clock_tick) {
+      callDebug("Call is paused until %d (now %d).\n", paused_until, clock_tick);
+      setPaused();
+      callDebug("Running: %d (wake %d).\n", running, wake());
+      return true;
+    }
+    /* Our pause is over. */
+    callDebug("Pause complete, waking up.\n");
+    paused_until = 0;
+    return next();
+  }
+  return executeMessage(curmsg);
+}
+
 char *default_message_names[] = {
 	"3pcc_abort",
 	"ack",
@@ -1695,6 +1882,9 @@ bool call::process_unexpected(char * msg)
              TRANSPORT_TO_STRING(transport),
              msg);
 
+  callDebug("Unexpected %s message received (index %d, hash %u):\n\n%s\n",
+             TRANSPORT_TO_STRING(transport), msg_index, hash(msg), msg);
+
   if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) {
     // if twin socket call => reset the other part here 
     if (twinSippSocket && (msg_index > 0)) {
@@ -1708,7 +1898,7 @@ bool call::process_unexpected(char * msg)
     computeStat(CStat::E_CALL_FAILED);
     computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
     if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
-      return (abortCall());
+      return (abortCall(true));
     } else {
       delete this;
       return false;
@@ -1721,27 +1911,27 @@ bool call::process_unexpected(char * msg)
 
 void call::abort() {
   WARNING("Aborted call with Call-ID '%s'", id);
-  abortCall();
+  abortCall(false);
 }
 
-bool call::abortCall()
+bool call::abortCall(bool writeLog)
 {
   int is_inv;
 
-  char * src_send = NULL ;
   char * src_recv = NULL ;
 
+  callDebug("Aborting call %s (index %d).\n", id, msg_index);
+
   if (last_send_msg != NULL) {
     is_inv = !strncmp(last_send_msg, "INVITE", 6);
   } else {
     is_inv = false;
   }  
-  if ((toolMode != MODE_SERVER) && (msg_index > 0)) {
+  if ((creationMode != MODE_SERVER) && (msg_index > 0)) {
     if ((call_established == false) && (is_inv)) {
       src_recv = last_recv_msg ;
       char   L_msg_buffer[SIPP_MAX_MSG_SIZE];
       L_msg_buffer[0] = '\0';
-      char * L_param = L_msg_buffer;
 
       // Answer unexpected errors (4XX, 5XX and beyond) with an ACK 
       // Contributed by F. Tarek Rogers
@@ -1751,8 +1941,6 @@ bool call::abortCall()
         /* Call is not established and the reply is not a 4XX, 5XX */
         /* And we already received a message. */
         if (ack_is_pending == true) {
-          char * cseq = NULL;
-
           /* If an ACK is expected from the other side, send it
            * and send a BYE afterwards                           */
           ack_is_pending = false;
@@ -1777,17 +1965,21 @@ bool call::abortCall()
        * because the earlier check depends on the first message being an INVITE
        * (although it could be something like a message message, therefore we
        * check that we received a message. */
-      char * src_recv = last_recv_msg ;
       char   L_msg_buffer[SIPP_MAX_MSG_SIZE];
       L_msg_buffer[0] = '\0';
-      char * L_param = L_msg_buffer;
       sendBuffer(createSendingMessage(get_default_message("bye"), -1));
     }
   }
 
+  if (writeLog && useCallDebugf) {
+    TRACE_CALLDEBUG ("-------------------------------------------------------------------------------\n", id);
+    TRACE_CALLDEBUG ("Call debugging information for call %s:\n", id);
+    TRACE_CALLDEBUG("%s", debugBuffer);
+  }
+
   stopListening();
   deadcall *deadcall_ptr = NULL;
-  if (deadcall_wait) {
+  if (deadcall_wait && !initCall) {
     char reason[100];
     sprintf(reason, "aborted at index %d", msg_index);
     deadcall_ptr = new deadcall(id, reason);
@@ -1806,7 +1998,7 @@ bool call::rejectCall()
 }
 
 
-int call::sendCmdMessage(int index)
+int call::sendCmdMessage(message *curmsg)
 {
   char * dest;
   char delimitor[2];
@@ -1817,8 +2009,6 @@ int call::sendCmdMessage(int index)
   char * peer_dest;
   struct sipp_socket **peer_socket;
 
-  message *curmsg = call_scenario->messages[index];
-
   if(curmsg -> M_sendCmdData) {
     // WARNING("---PREPARING_TWIN_CMD---%s---", scenario[index] -> M_sendCmdData);
     dest = createSendingMessage(curmsg -> M_sendCmdData, -1);
@@ -1874,12 +2064,12 @@ int call::sendCmdBuffer(char* cmd)
 }
 
 
-char* call::createSendingMessage(SendingMessage *src, int P_index) {
+char* call::createSendingMessage(SendingMessage *src, int P_index, int *msgLen) {
   static char msg_buffer[SIPP_MAX_MSG_SIZE+2];
-  return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer));
+  return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer), msgLen);
 }
 
-char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len)
+char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len, int *msgLen)
 {
   char * length_marker = NULL;
   char * auth_marker = NULL;
@@ -1902,7 +2092,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
 	  dest += snprintf(dest, left, "%s", ptr);
 	  supresscrlf = false;
 	} else {
-	  dest += snprintf(dest, left, "%s", comp->literal);
+	  memcpy(dest, comp->literal, comp->literalLen);
+	  dest += comp->literalLen;
+          *dest = '\0';
 	}
 	break;
       case E_Message_Remote_IP:
@@ -1919,7 +2111,7 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
 	break;
       case E_Message_Local_Port:
 	int port;
-	if((transport == T_UDP) && (multisocket) && (toolMode != MODE_SERVER)) {
+	if((transport == T_UDP) && (multisocket) && (sendMode != MODE_SERVER)) {
 	  port = call_port;
 	} else {
 	  port =  local_port;
@@ -2001,6 +2193,12 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
       case E_Message_Call_Number:
 	dest += snprintf(dest, left, "%u", number);
 	break;
+      case E_Message_DynamicId:
+        dest += snprintf(dest, left, "%u", call::dynamicId);
+        // increment at each request
+        dynamicId += stepDynamicId;
+        if ( this->dynamicId > maxDynamicId ) { call::dynamicId = call::startDynamicId; } ;
+      break;
       case E_Message_Call_ID:
 	dest += snprintf(dest, left, "%s", id);
 	break;
@@ -2068,6 +2266,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
       case E_Message_UserID:
 	dest += snprintf(dest, left, "%d", userId);
 	break;
+      case E_Message_SippVersion:
+	dest += snprintf(dest, left, "%s", SIPP_VERSION);
+	break;
       case E_Message_Variable: {
 	 int varId = comp->varId;
 	 CCallVariable *var = M_callVariableTable->getVar(varId);
@@ -2084,6 +2285,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
 	 } else if (var->isBool()) {
 	   dest += sprintf(dest, "false");
 	 }
+	 if (*(dest - 1) == '\n') {
+	   supresscrlf = true;
+	 }
 	 break;
       }
       case E_Message_Fill: {
@@ -2103,6 +2307,24 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
 	*dest = '\0';
 	break;
       }
+      case E_Message_File: {
+        char buffer[MAX_HEADER_LEN];
+	createSendingMessage(comp->comp_param.filename, -2, buffer, sizeof(buffer));
+	FILE *f = fopen(buffer, "r");
+	if (!f) {
+	  ERROR("Could not open '%s': %s\n", buffer, strerror(errno));
+	}
+	int ret;
+	while ((ret = fread(dest, 1, left, f)) > 0) {
+		left -= ret;
+		dest += ret;
+	}
+	if (ret < 0) {
+	  ERROR("Error reading '%s': %s\n", buffer, strerror(errno));
+	}
+	fclose(f);
+	break;
+      }
       case E_Message_Injection: {
 	char *orig_dest = dest;
 	getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, comp->comp_param.field_param.line, dest);
@@ -2137,6 +2359,10 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
 	}
 	break;
       }
+      case E_Message_Custom: {
+	dest += comp->comp_param.fxn(this, comp, dest, left);
+	break;
+      }
       case E_Message_Last_Message:
         if(last_recv_msg && strlen(last_recv_msg)) {
 	  dest += sprintf(dest, "%s", last_recv_msg);
@@ -2175,8 +2401,12 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
   }
   /* Need the body for length and auth-int calculation */
   char *body;
+  char *auth_body = NULL;
   if (length_marker || auth_marker) {
     body = strstr(msg_buffer, "\r\n\r\n");
+    if (body) {
+	auth_body += strlen("\r\n\r\n");
+    }
   }
 
   /* Fix up the length. */
@@ -2187,7 +2417,7 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
 
     if (body && dest - body > 4 && dest - body < 100004) {
       char tmp = length_marker[5];
-      sprintf(length_marker, "%5u", dest - body - 4 + len_offset);
+      sprintf(length_marker, "%5u", (unsigned)(dest - body - 4 + len_offset));
       length_marker[5] = tmp;
     } else {
       // Other cases: Content-Length is 0
@@ -2195,6 +2425,10 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
     }
   }
 
+  if (msgLen) {
+    *msgLen = dest - msg_buffer;
+  }
+
   /*
    * The authentication substitution must be done outside the above
    * loop because auth-int will use the body (which must have already
@@ -2224,8 +2458,8 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
     while(isspace(*tmp) || isdigit(*tmp)) tmp++;
     sscanf(tmp,"%s", method);
 
-    if (!body) {
-      body = "";
+    if (!auth_body) {
+      auth_body = "";
     }
 
     /* Determine the type of credentials. */
@@ -2254,7 +2488,7 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
     createSendingMessage(auth_comp->comp_param.auth_param.aka_AMF, -2, my_aka_AMF, sizeof(my_aka_AMF));
     createSendingMessage(auth_comp->comp_param.auth_param.aka_OP, -2, my_aka_OP, sizeof(my_aka_OP));
 
-    if (createAuthHeader(my_auth_user, my_auth_pass, method, uri, body, dialog_authentication,
+    if (createAuthHeader(my_auth_user, my_auth_pass, method, uri, auth_body, dialog_authentication,
 	  my_aka_OP, my_aka_AMF, my_aka_K, result + authlen) == 0) {
       ERROR("%s", result + authlen);
     }
@@ -2264,6 +2498,9 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf
     memmove(auth_marker + authlen, auth_marker + auth_marker_len, strlen(auth_marker + auth_marker_len) + 1);
     /* Copy our result into the hole. */
     memcpy(auth_marker, result, authlen);
+    if (msgLen) {
+	*msgLen += (authlen -  auth_marker_len);
+    }
 #endif
   }
 
@@ -2280,12 +2517,14 @@ bool call::process_twinSippCom(char * msg)
   bool            found = false;
   T_ActionResult  actionResult;
 
+  callDebug("Processing incoming command for call-ID %s:\n%s\n\n", id, msg);
+
   setRunning();
 
   if (checkInternalCmd(msg) == false) {
 
     for(search_index = msg_index;
-      search_index < call_scenario->length;
+      search_index < (int)call_scenario->messages.size();
       search_index++) {
       if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECVCMD) {
 	if(call_scenario->messages[search_index] -> optional) {
@@ -2293,6 +2532,7 @@ bool call::process_twinSippCom(char * msg)
 	}
 	/* The received message is different from the expected one */
 	TRACE_MSG("Unexpected control message received (I was expecting a different type of message):\n%s\n", msg);
+	callDebug("Unexpected control message received (I was expecting a different type of message):\n%s\n\n", msg);
 	return rejectCall();
       } else {
 	if(extendedTwinSippMode){                   // 3pcc extended mode
@@ -2313,7 +2553,7 @@ bool call::process_twinSippCom(char * msg)
 
     if (found) {
       call_scenario->messages[search_index]->M_nbCmdRecv ++;
-      do_bookkeeping(search_index);
+      do_bookkeeping(call_scenario->messages[search_index]);
 
       // variable treatment
       // Remove \r, \n at the end of a received command
@@ -2322,7 +2562,7 @@ bool call::process_twinSippCom(char * msg)
       (msg[strlen(msg)-2] == '\r') ) {
         msg[strlen(msg)-2] = 0;
       }
-      actionResult = executeAction(msg, search_index);
+      actionResult = executeAction(msg, call_scenario->messages[search_index]);
 
       if(actionResult != call::E_AR_NO_ERROR) {
         // Store last action result if it is an error
@@ -2330,10 +2570,14 @@ bool call::process_twinSippCom(char * msg)
         call::last_action_result = actionResult;
         if (actionResult == E_AR_STOP_CALL) {
             return rejectCall();
-        }
+        } else if (actionResult == E_AR_CONNECT_FAILED) {
+	  terminate(CStat::E_FAILED_TCP_CONNECT);
+	  return false;
+	}
       }
     } else {
       TRACE_MSG("Unexpected control message received (no such message found):\n%s\n", msg);
+      callDebug("Unexpected control message received (no such message found):\n%s\n\n", msg);
       return rejectCall();
     }
     msg_index = search_index; //update the state machine
@@ -2367,7 +2611,7 @@ bool call::checkInternalCmd(char * cmd)
 
   if (strcmp(L_ptr1, "abort_call") == 0) {
     *L_ptr2 = L_backup;
-    abortCall();
+    abortCall(true);
     computeStat(CStat::E_CALL_FAILED);
     return (true);
   }
@@ -2409,10 +2653,10 @@ bool call::check_peer_src(char * msg, int search_index)
 void call::extract_cseq_method (char* method, char* msg)
 {
   char* cseq ;
-  if (cseq = strstr (msg, "CSeq"))
+  if ((cseq = strstr (msg, "CSeq")))
   {
     char * value ;
-    if ( value = strchr (cseq,  ':'))
+    if (( value = strchr (cseq,  ':')))
     {
       value++;
       while ( isspace(*value)) value++;  // ignore any white spaces after the :
@@ -2430,7 +2674,6 @@ void call::extract_cseq_method (char* method, char* msg)
 
 void call::extract_transaction (char* txn, char* msg)
 {
-  char *otxn = txn;
   char *via = get_header_content(msg, "via:");
   if (!via) {
     txn[0] = '\0';
@@ -2599,7 +2842,6 @@ void call::computeRouteSetAndRemoteTargetUri (char* rr, char* contact, bool bReq
 
 bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn)
 {
-  int        result;
   message *curmsg = call_scenario->messages[index];
 
   if ((curmsg -> recv_request)) {
@@ -2618,7 +2860,7 @@ bool call::matches_scenario(unsigned int index, int reply_code, char * request,
   } else if (curmsg->recv_response && (curmsg->recv_response == reply_code)) {
     /* This is a potential candidate, we need to match transactions. */
     if (curmsg->response_txn) {
-      if (txnID[curmsg->response_txn - 1] && !strcmp(txnID[curmsg->response_txn - 1], txn)) {
+      if (transactions[curmsg->response_txn - 1].txnID && !strcmp(transactions[curmsg->response_txn - 1].txnID, txn)) {
 	return true;
       } else {
 	return false;
@@ -2643,7 +2885,7 @@ void call::queue_up(char *msg) {
   queued_msg = strdup(msg);
 }
 
-bool call::process_incoming(char * msg)
+bool call::process_incoming(char * msg, struct sockaddr_storage *src)
 {
   int             reply_code;
   static char     request[65];
@@ -2655,11 +2897,19 @@ bool call::process_incoming(char * msg)
   bool            found = false;
   T_ActionResult  actionResult;
 
+  getmilliseconds();
+  callDebug("Processing %d byte incoming message for call-ID %s (hash %u):\n%s\n\n", strlen(msg), id, hash(msg), msg);
+
   setRunning();
 
   /* Ignore the messages received during a pause if -pause_msg_ign is set */
   if(call_scenario->messages[msg_index] -> M_type == MSG_TYPE_PAUSE && pause_msg_ign) return(true);
 
+  /* Get our destination if we have none. */
+  if (call_peer.ss_family == AF_UNSPEC && src) {
+    memcpy(&call_peer, src, SOCK_ADDR_SIZE(src));
+  }
+
   /* Authorize nop as a first command, even in server mode */
   if((msg_index == 0) && (call_scenario->messages[msg_index] -> M_type == MSG_TYPE_NOP)) {
     queue_up (msg);
@@ -2680,6 +2930,7 @@ bool call::process_incoming(char * msg)
       if(lost(recv_retrans_recv_index)) {
 	TRACE_MSG("%s message (retrans) lost (recv).",
 	      TRANSPORT_TO_STRING(transport));
+	callDebug("%s message (retrans) lost (recv) (hash %u)\n", TRANSPORT_TO_STRING(transport), hash(msg));
 
 	if(comp_state) { comp_free(&comp_state); }
 	call_scenario->messages[recv_retrans_recv_index] -> nb_lost++;
@@ -2688,9 +2939,9 @@ bool call::process_incoming(char * msg)
 
       call_scenario->messages[recv_retrans_recv_index] -> nb_recv_retrans++;
 
-      send_scene(recv_retrans_send_index, &status);
+      send_scene(recv_retrans_send_index, &status, NULL);
 
-      if(status == 0) {
+      if(status >= 0) {
 	call_scenario->messages[recv_retrans_send_index] -> nb_sent_retrans++;
 	computeStat(CStat::E_RETRANSMISSION);
       } else if(status < 0) {
@@ -2755,7 +3006,7 @@ bool call::process_incoming(char * msg)
     // extract the cseq method from the response
     extract_cseq_method (responsecseqmethod, msg);
     extract_transaction (txn, msg);
-  } else if(ptr = strchr(msg, ' ')) {
+  } else if((ptr = strchr(msg, ' '))) {
     if((ptr - msg) < 64) {
       memcpy(request, msg, ptr - msg);
       request[ptr - msg] = 0;
@@ -2787,7 +3038,7 @@ bool call::process_incoming(char * msg)
   /* Try to find it in the expected non mandatory responses
    * until the first mandatory response  in the scenario */
   for(search_index = msg_index;
-      search_index < call_scenario->length;
+      search_index < (int)call_scenario->messages.size();
       search_index++) {
     if(!matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) {
       if(call_scenario->messages[search_index] -> optional) {
@@ -2819,22 +3070,37 @@ bool call::process_incoming(char * msg)
         } else {
 	  if (int checkTxn = call_scenario->messages[search_index]->response_txn) {
 	    /* This is a reply to an old transaction. */
-	    if (!strcmp(txnID[checkTxn - 1], txn)) {
+	    if (!strcmp(transactions[checkTxn - 1].txnID, txn)) {
 		/* This reply is provisional, so it should have no effect if we recieve it out-of-order. */
 		if (reply_code >= 100 && reply_code <= 199) {
 		  TRACE_MSG("-----------------------------------------------\n"
 		      "Ignoring provisional %s message for transaction %s:\n\n%s\n",
-		      TRANSPORT_TO_STRING(transport), call_scenario->txnRevMap[checkTxn - 1], msg);
+		      TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg);
+		  callDebug("Ignoring provisional %s message for transaction %s (hash %u):\n\n%s\n",
+		      TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg);
 		  return true;
-		} else if (call_scenario->messages[search_index + 1]->M_type == MSG_TYPE_SEND && call_scenario->messages[search_index + 1]->send_scheme->isAck()) {
+		} else if (int ackIndex = transactions[checkTxn - 1].ackIndex) {
 		  /* This is the message before an ACK, so verify that this is an invite transaction. */
-		  if (!strcmp(responsecseqmethod, "INVITE")) {
-		    sendBuffer(createSendingMessage(call_scenario->messages[search_index+1] -> send_scheme, (search_index+1)));
+		  assert (call_scenario->transactions[checkTxn - 1].isInvite);
+		  sendBuffer(createSendingMessage(call_scenario->messages[ackIndex] -> send_scheme, ackIndex));
+		  return true;
+		} else {
+		  assert (!call_scenario->transactions[checkTxn - 1].isInvite);
+		  /* This is a non-provisional message for the transaction, and
+		   * we have already gotten our allowable response.  Just make sure
+		   * that it is not a retransmission of the final response. */
+		  if (transactions[checkTxn - 1].txnResp == hash(msg)) {
+		    /* We have gotten this retransmission out-of-order, let's just ignore it. */
+		    TRACE_MSG("-----------------------------------------------\n"
+			"Ignoring final %s message for transaction %s:\n\n%s\n",
+			TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg);
+		    callDebug("Ignoring final %s message for transaction %s (hash %u):\n\n%s\n",
+			TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg);
+		    WARNING("Ignoring final %s message for transaction %s (hash %u):\n\n%s\n",
+			TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg);
 		    return true;
 		  }
 		}
-		/* This is a non-provisional message for the transaction, and
-		 * we have already gotten our allowable response. */
 	    }
 	  } else {
 	    /*
@@ -2902,14 +3168,21 @@ bool call::process_incoming(char * msg)
   if(lost(search_index)) {
     TRACE_MSG("%s message lost (recv).",
                TRANSPORT_TO_STRING(transport));
+    callDebug("%s message lost (recv) (hash %u).\n",
+               TRANSPORT_TO_STRING(transport), hash(msg));
     if(comp_state) { comp_free(&comp_state); }
     call_scenario->messages[search_index] -> nb_lost++;
     return true;
   }
 
+  /* If we are part of a transaction, mark this as the final response. */
+  if (int checkTxn = call_scenario->messages[search_index]->response_txn) {
+    transactions[checkTxn - 1].txnResp = hash(msg);
+  }
+
 
   /* Handle counters and RTDs for this message. */
-  do_bookkeeping(search_index);
+  do_bookkeeping(call_scenario->messages[search_index]);
 
   /* Increment the recv counter */
   call_scenario->messages[search_index] -> nb_recv++;
@@ -2918,7 +3191,7 @@ bool call::process_incoming(char * msg)
   if (found) {
     //WARNING("---EXECUTE_ACTION_ON_MSG---%s---", msg);
 
-    actionResult = executeAction(msg, search_index);
+    actionResult = executeAction(msg, call_scenario->messages[search_index]);
 
     if(actionResult != call::E_AR_NO_ERROR) {
       // Store last action result if it is an error
@@ -2926,6 +3199,9 @@ bool call::process_incoming(char * msg)
       call::last_action_result = actionResult;
       if (actionResult == E_AR_STOP_CALL) {
           return rejectCall();
+      } else if (actionResult == E_AR_CONNECT_FAILED) {
+	  terminate(CStat::E_FAILED_TCP_CONNECT);
+	  return false;
       }
     }
   }
@@ -2976,7 +3252,7 @@ bool call::process_incoming(char * msg)
   if (call_scenario->messages[search_index] -> bShouldRecordRoutes &&
           NULL == dialog_route_set ) {
 
-      next_req_url = (char*) calloc(1, MAX_HEADER_LEN);
+      next_req_url = (char*) realloc(next_req_url, MAX_HEADER_LEN);
 
       char rr[MAX_HEADER_LEN];
       memset(rr, 0, sizeof(rr));
@@ -3039,6 +3315,7 @@ bool call::process_incoming(char * msg)
    * in our messages. */
   last_recv_index = search_index;
   last_recv_hash = cookie;
+  callDebug("Set Last Recv Hash: %u (recv index %d)\n", last_recv_hash, last_recv_index);
   last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1);
   strcpy(last_recv_msg, msg);
 
@@ -3063,7 +3340,7 @@ bool call::process_incoming(char * msg)
     /* We are just waiting for a message to be received, if any of the
      * potential messages have a timeout we set it as our timeout. We
      * start from the next message and go until any non-receives. */
-    for(search_index++; search_index < call_scenario->length; search_index++) {
+    for(search_index++; search_index < (int)call_scenario->messages.size(); search_index++) {
       if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECV) {
 	break;
       }
@@ -3092,275 +3369,464 @@ double call::get_rhs(CAction *currentAction) {
   }
 }
 
-call::T_ActionResult call::executeAction(char * msg, int scenarioIndex)
+call::T_ActionResult call::executeAction(char * msg, message *curmsg)
 {
   CActions*  actions;
   CAction*   currentAction;
 
-  actions = call_scenario->messages[scenarioIndex]->M_actions;
+  actions = curmsg->M_actions;
   // looking for action to do on this message
-  if(actions != NULL) {
-    for(int i=0; i<actions->getActionSize(); i++) {
-      currentAction = actions->getAction(i);
-      if(currentAction != NULL) {
-        if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) {
-	  char msgPart[MAX_SUB_MESSAGE_LENGTH];
-
-	  /* Where to look. */
-	  char *haystack;
-
-	  if(currentAction->getLookingPlace() == CAction::E_LP_HDR) {
-	    extractSubMessage (msg,
-		currentAction->getLookingChar(),
-		msgPart,
-		currentAction->getCaseIndep(),
-		currentAction->getOccurence(),
-		currentAction->getHeadersOnly());
-	    if(currentAction->getCheckIt() == true && (strlen(msgPart) < 0)) {
-	      // the sub message is not found and the checking action say it
-	      // MUST match --> Call will be marked as failed but will go on
-	      WARNING("Failed regexp match: header %s not found in message %s\n", currentAction->getLookingChar(), msg);
-	      return(call::E_AR_HDR_NOT_FOUND);
-	    }
-	    haystack = msgPart;
-	  } else {
-	    haystack = msg;
-	  }
-	  currentAction->executeRegExp(haystack, M_callVariableTable);
-
-	  if( (!(M_callVariableTable->getVar(currentAction->getVarId())->isSet())) && (currentAction->getCheckIt() == true) ) {
-	    // the message doesn't match and the checkit action say it MUST match
-	    // Allow easier regexp debugging
-	    WARNING("Failed regexp match: looking in '%s', with regexp '%s'",
-		haystack, currentAction->getRegularExpression());
-	    return(call::E_AR_REGEXP_DOESNT_MATCH);
+  if(actions == NULL) {
+    return(call::E_AR_NO_ERROR);
+  }
+
+  for(int i=0; i<actions->getActionSize(); i++) {
+    currentAction = actions->getAction(i);
+    if(currentAction == NULL) {
+      continue;
+    }
+
+    if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) {
+      char msgPart[MAX_SUB_MESSAGE_LENGTH];
+
+      /* Where to look. */
+      char *haystack;
+
+      if(currentAction->getLookingPlace() == CAction::E_LP_HDR) {
+	extractSubMessage (msg,
+	    currentAction->getLookingChar(),
+	    msgPart,
+	    currentAction->getCaseIndep(),
+	    currentAction->getOccurence(),
+	    currentAction->getHeadersOnly());
+	if(currentAction->getCheckIt() == true && (strlen(msgPart) < 0)) {
+	  // the sub message is not found and the checking action say it
+	  // MUST match --> Call will be marked as failed but will go on
+	  WARNING("Failed regexp match: header %s not found in message %s\n", currentAction->getLookingChar(), msg);
+	  return(call::E_AR_HDR_NOT_FOUND);
+	}
+	haystack = msgPart;
+      } else if(currentAction->getLookingPlace() == CAction::E_LP_BODY) {
+	haystack = strstr(msg, "\r\n\r\n");
+	if (!haystack) {
+	  if (currentAction->getCheckIt() == true) {
+	    WARNING("Failed regexp match: body not found in message %s\n", msg);
+	    return(call::E_AR_HDR_NOT_FOUND);
 	  }
-        } else /* end action == E_AT_ASSIGN_FROM_REGEXP */ 
-            if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) {
-	      double operand = get_rhs(currentAction);
-	      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(operand);
-        } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_INDEX) {
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(msg_index);
-        } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY) {
-	  struct timeval tv;
-	  gettimeofday(&tv, NULL);
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)tv.tv_sec);
-	  M_callVariableTable->getVar(currentAction->getSubVarId(0))->setDouble((double)tv.tv_usec);
-        } else if (currentAction->getActionType() == CAction::E_AT_LOOKUP) {
-	  /* Create strings from the sending messages. */
-	  char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2));
-	  char *key = strdup(createSendingMessage(currentAction->getMessage(1), -2));
-	  double value = -1;
-
-	  str_int_map::iterator index_it = infIndex[file]->find(key);
-	  if (index_it != infIndex[file]->end()) {
-		value = index_it->second;
+	  msgPart[0] = '\0';
+	  haystack = msgPart;
+	}
+	haystack += strlen("\r\n\r\n");
+      } else if(currentAction->getLookingPlace() == CAction::E_LP_MSG) {
+	haystack = msg;
+      } else if(currentAction->getLookingPlace() == CAction::E_LP_VAR) {
+	/* Get the input variable. */
+	haystack = M_callVariableTable->getVar(currentAction->getVarInId())->getString();
+	if (!haystack) {
+	  if (currentAction->getCheckIt() == true) {
+	    WARNING("Failed regexp match: variable $%d not set\n", currentAction->getVarInId());
+	    return(call::E_AR_HDR_NOT_FOUND);
 	  }
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
-	  free(file);
-	  free(key);
-#ifdef _USE_OPENSSL
-        } else if (currentAction->getActionType() == CAction::E_AT_VERIFY_AUTH) {
-	  bool result;
-	  char *lf;
-	  char *end;
-
-	  lf = strchr(msg, '\n');
-	  end = strchr(msg, ' ');
-
-	  if (!lf || !end) {
-	    result = false;
-	  } else if (lf < end) {
-	    result = false;
+	}
+      } else {
+	ERROR("Invalid looking place: %d\n", currentAction->getLookingPlace());
+      }
+      currentAction->executeRegExp(haystack, M_callVariableTable);
+
+      if( (!(M_callVariableTable->getVar(currentAction->getVarId())->isSet())) && (currentAction->getCheckIt() == true) ) {
+	// the message doesn't match and the checkit action say it MUST match
+	// Allow easier regexp debugging
+	WARNING("Failed regexp match: looking in '%s', with regexp '%s'",
+	    haystack, currentAction->getRegularExpression());
+	return(call::E_AR_REGEXP_DOESNT_MATCH);
+            } else if ( ((M_callVariableTable->getVar(currentAction->getVarId())->isSet())) &&
+                        (currentAction->getCheckItInverse() == true) )
+            {
+                // The inverse of the above
+                WARNING("Regexp matched but should not: looking in '%s', with regexp '%s'",
+                        haystack, currentAction->getRegularExpression());
+                return(call::E_AR_REGEXP_SHOULDNT_MATCH);
+      }
+    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) {
+      double operand = get_rhs(currentAction);
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(operand);
+    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_INDEX) {
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(msg_index);
+    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY) {
+      struct timeval tv;
+      gettimeofday(&tv, NULL);
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)tv.tv_sec);
+      M_callVariableTable->getVar(currentAction->getSubVarId(0))->setDouble((double)tv.tv_usec);
+    } else if (currentAction->getActionType() == CAction::E_AT_LOOKUP) {
+      /* Create strings from the sending messages. */
+      char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2));
+      char *key = strdup(createSendingMessage(currentAction->getMessage(1), -2));
+
+      if (inFiles.find(file) == inFiles.end()) {
+	ERROR("Invalid injection file for insert: %s", file);
+      }
+
+      double value = inFiles[file]->lookup(key);
+
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
+      free(file);
+      free(key);
+    } else if (currentAction->getActionType() == CAction::E_AT_INSERT) {
+      /* Create strings from the sending messages. */
+      char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2));
+      char *value = strdup(createSendingMessage(currentAction->getMessage(1), -2));
+
+      if (inFiles.find(file) == inFiles.end()) {
+	ERROR("Invalid injection file for insert: %s", file);
+      }
+
+      inFiles[file]->insert(value);
+
+      free(file);
+      free(value);
+    } else if (currentAction->getActionType() == CAction::E_AT_REPLACE) {
+      /* Create strings from the sending messages. */
+      char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2));
+      char *line = strdup(createSendingMessage(currentAction->getMessage(1), -2));
+      char *value = strdup(createSendingMessage(currentAction->getMessage(2), -2));
+
+      if (inFiles.find(file) == inFiles.end()) {
+	ERROR("Invalid injection file for replace: %s", file);
+      }
+
+      char *endptr;
+      int lineNum = (int)strtod(line, &endptr);
+      if (*endptr) {
+	ERROR("Invalid line number for replace: %s", line);
+      }
+
+      inFiles[file]->replace(lineNum, value);
+
+      free(file);
+      free(line);
+      free(value);
+    } else if (currentAction->getActionType() == CAction::E_AT_CLOSE_CON) {
+      if (call_socket) {
+	sipp_socket_invalidate(call_socket);
+	sipp_close_socket(call_socket);
+	call_socket = NULL;
+      }
+    } else if (currentAction->getActionType() == CAction::E_AT_SET_DEST) {
+      /* Change the destination for this call. */
+      char *str_host = strdup(createSendingMessage(currentAction->getMessage(0), -2));
+      char *str_port = strdup(createSendingMessage(currentAction->getMessage(1), -2));
+      char *str_protocol = strdup(createSendingMessage(currentAction->getMessage(2), -2));
+
+      char *endptr;
+      int port = (int)strtod(str_port, &endptr);
+      if (*endptr) {
+	ERROR("Invalid port for setdest: %s", str_port);
+      }
+
+      int protocol;
+      if (!strcmp(str_protocol, "udp") || !strcmp(str_protocol, "UDP")) {
+	protocol = T_UDP;
+      } else if (!strcmp(str_protocol, "tcp") || !strcmp(str_protocol, "TCP")) {
+	protocol = T_TCP;
+      } else if (!strcmp(str_protocol, "tls") || !strcmp(str_protocol, "TLS")) {
+	protocol = T_TLS;
+      } else {
+	ERROR("Unknown transport for setdest: '%s'", str_protocol);
+      }
+
+      if (!call_socket && protocol == T_TCP && transport == T_TCP) {
+	bool existing;
+	if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) {
+	  ERROR_NO("Unable to get a TCP socket");
+	}
+
+	if (!existing) {
+	  sipp_customize_socket(call_socket);
+	}
+      }
+
+
+      if (protocol != call_socket->ss_transport) {
+	  ERROR("Can not switch protocols during setdest.");
+      }
+
+      if (protocol == T_UDP) {
+	/* Nothing to do. */
+      } else if (protocol == T_TLS) {
+	ERROR("Changing destinations is not supported for TLS.");
+      } else if (protocol == T_TCP) {
+	if (!multisocket) {
+	  ERROR("Changing destinations for TCP requires multisocket mode.");
+	}
+	if (call_socket->ss_count > 1) {
+	  ERROR("Can not change destinations for a TCP socket that has more than one user.");
+	}
+      }
+
+      struct addrinfo   hints;
+      struct addrinfo * local_addr;
+      memset((char*)&hints, 0, sizeof(hints));
+      hints.ai_flags  = AI_PASSIVE;
+      hints.ai_family = PF_UNSPEC;
+      is_ipv6 = false;
+
+      if (getaddrinfo(str_host, NULL, &hints, &local_addr) != 0) {
+	ERROR("Unknown host '%s' for setdest", str_host);
+      }
+      if (_RCAST(struct sockaddr_storage *, local_addr->ai_addr)->ss_family != call_peer.ss_family) {
+	ERROR("Can not switch between IPv4 and IPV6 using setdest!");
+      }
+      memcpy(&call_peer, local_addr->ai_addr, SOCK_ADDR_SIZE(_RCAST(struct sockaddr_storage *,local_addr->ai_addr)));
+      if (call_peer.ss_family == AF_INET) {
+	(_RCAST(struct sockaddr_in *,&call_peer))->sin_port = htons(port);
+      } else {
+	(_RCAST(struct sockaddr_in6 *,&call_peer))->sin6_port = htons(port);
+      }
+      memcpy(&call_socket->ss_dest, &call_peer, SOCK_ADDR_SIZE(_RCAST(struct sockaddr_storage *,&call_peer)));
+
+      free(str_host);
+      free(str_port);
+      free(str_protocol);
+
+      if (protocol == T_TCP) {
+	close(call_socket->ss_fd);
+	call_socket->ss_fd = -1;
+	call_socket->ss_changed_dest = true;
+	if (sipp_reconnect_socket(call_socket)) {
+	  if (reconnect_allowed()) {
+	    if(errno == EINVAL){
+	      /* This occurs sometime on HPUX but is not a true INVAL */
+	      WARNING("Unable to connect a TCP socket, remote peer error");
+	    } else {
+	      WARNING("Unable to connect a TCP socket");
+	    }
+	    /* This connection failed.  We must be in multisocket mode, because
+	     * otherwise we would already have a call_socket.  This call can not
+	     * succeed, but does not affect any of our other calls. We do decrement
+	     * the reconnection counter however. */
+	    if (reset_number != -1) {
+	      reset_number--;
+	    }
+
+	    return E_AR_CONNECT_FAILED;
 	  } else {
-	    char *auth = get_header(msg, "Authorization:", true);
-	    char *method = (char *)malloc(end - msg + 1);
-	    strncpy(method, msg, end - msg);
-	    method[end - msg] = '\0';
-
-	    /* Generate the username to verify it against. */
-            char *tmp = createSendingMessage(currentAction->getMessage(0), -2 /* do not add crlf*/);
-	    char *username = strdup(tmp);
-	    /* Generate the password to verify it against. */
-            tmp= createSendingMessage(currentAction->getMessage(1), -2 /* do not add crlf*/);
-	    char *password = strdup(tmp);
-
-	    result = verifyAuthHeader(username, password, method, auth);
-
-	    free(username);
-	    free(password);
+	    if(errno == EINVAL){
+	      /* This occurs sometime on HPUX but is not a true INVAL */
+	      ERROR("Unable to connect a TCP socket, remote peer error");
+	    } else {
+	      ERROR_NO("Unable to connect a TCP socket");
+	    }
 	  }
+	}
+      }
+#ifdef _USE_OPENSSL
+    } else if (currentAction->getActionType() == CAction::E_AT_VERIFY_AUTH) {
+      bool result;
+      char *lf;
+      char *end;
+
+      lf = strchr(msg, '\n');
+      end = strchr(msg, ' ');
+
+      if (!lf || !end) {
+	result = false;
+      } else if (lf < end) {
+	result = false;
+      } else {
+	char *auth = get_header(msg, "Authorization:", true);
+	char *method = (char *)malloc(end - msg + 1);
+	strncpy(method, msg, end - msg);
+	method[end - msg] = '\0';
+
+	/* Generate the username to verify it against. */
+	char *tmp = createSendingMessage(currentAction->getMessage(0), -2 /* do not add crlf*/);
+	char *username = strdup(tmp);
+	/* Generate the password to verify it against. */
+	tmp= createSendingMessage(currentAction->getMessage(1), -2 /* do not add crlf*/);
+	char *password = strdup(tmp);
+
+	result = verifyAuthHeader(username, password, method, auth);
+
+	free(username);
+	free(password);
+      }
 
-	  M_callVariableTable->getVar(currentAction->getVarId())->setBool(result);
+      M_callVariableTable->getVar(currentAction->getVarId())->setBool(result);
 #endif
-        } else if (currentAction->getActionType() == CAction::E_AT_JUMP) {
-	  double operand = get_rhs(currentAction);
-	  msg_index = (int)operand - 1;
-        } else if (currentAction->getActionType() == CAction::E_AT_PAUSE_RESTORE) {
-	  double operand = get_rhs(currentAction);
-	  paused_until = (int)operand;
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) {
-	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
-	  double operand = get_rhs(currentAction);
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand);
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) {
-	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
-	  double operand = get_rhs(currentAction);
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand);
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) {
-	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
-	  double operand = get_rhs(currentAction);
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand);
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) {
-	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
-	  double operand = get_rhs(currentAction);
-	  if (operand == 0) {
-	    WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId());
-	  } else {
-	    M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value / operand);
-	  }
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) {
-	  double value = currentAction->compare(M_callVariableTable);
-	  M_callVariableTable->getVar(currentAction->getVarId())->setBool(value);
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) {
-	  char *rhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString();
-	  char *lhs = currentAction->getStringValue();
-	  int value = strcmp(rhs, lhs);
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)value);
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_TRIM) {
-	  CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId());
-	  char *in = var->getString();
-	  char *p = in;
-	  while (isspace(*p)) {
-		p++;
-	  }
-	  char *q = strdup(p);
-	  var->setString(q);
-	  int l = strlen(q);
-	  for (int i = l - 1; i >= 0 & isspace(q[i]); i--) {
-		q[i] = '\0';
-	  }
-        } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) {
-	  double value;
+    } else if (currentAction->getActionType() == CAction::E_AT_JUMP) {
+      double operand = get_rhs(currentAction);
+      msg_index = (int)operand - 1;
+    } else if (currentAction->getActionType() == CAction::E_AT_PAUSE_RESTORE) {
+      double operand = get_rhs(currentAction);
+      paused_until = (int)operand;
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) {
+      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
+      double operand = get_rhs(currentAction);
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand);
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) {
+      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
+      double operand = get_rhs(currentAction);
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand);
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) {
+      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
+      double operand = get_rhs(currentAction);
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand);
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) {
+      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
+      double operand = get_rhs(currentAction);
+      if (operand == 0) {
+	WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId());
+      } else {
+	M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value / operand);
+      }
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) {
+      double value = currentAction->compare(M_callVariableTable);
+      M_callVariableTable->getVar(currentAction->getVarId())->setBool(value);
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) {
+      char *rhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString();
+      char *lhs;
+      if (currentAction->getVarIn2Id()) {
+	lhs = M_callVariableTable->getVar(currentAction->getVarIn2Id())->getString();
+      } else {
+	lhs = currentAction->getStringValue();
+      }
+      int value = strcmp(rhs, lhs);
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)value);
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_TRIM) {
+      CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId());
+      char *in = var->getString();
+      char *p = in;
+      while (isspace(*p)) {
+	p++;
+      }
+      char *q = strdup(p);
+      var->setString(q);
+      int l = strlen(q);
+      for (int i = l - 1; i >= 0 & isspace(q[i]); i--) {
+	q[i] = '\0';
+      }
+    } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) {
+      double value;
+
+      if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) {
+	M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
+      } else {
+	WARNING("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId());
+      }
+    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) {
+      double value = currentAction->getDistribution()->sample();
+      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
+    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) {
+      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
+      char *str = strdup(x);
+      if (!str) {
+	ERROR("Out of memory duplicating string for assignment!");
+      }
+      M_callVariableTable->getVar(currentAction->getVarId())->setString(str);
+    } else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) {
+      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
+      LOG_MSG("%s\n", x);
+    } else if (currentAction->getActionType() == CAction::E_AT_LOG_WARNING) {
+      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
+      WARNING("%s", x);
+    } else if (currentAction->getActionType() == CAction::E_AT_LOG_ERROR) {
+      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
+      ERROR("%s", x);
+    } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) {
+      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
+      // TRACE_MSG("Trying to execute [%s]", x);
+      pid_t l_pid;
+      switch(l_pid = fork())
+      {
+	case -1:
+	  // error when forking !
+	  ERROR_NO("Forking error main");
+	  break;
 
-	  if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) {
-	    M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
+	case 0:
+	  // first child process - execute the command
+	  if((l_pid = fork()) < 0) {
+	    ERROR_NO("Forking error child");
 	  } else {
-	    WARNING("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId());
+	    if( l_pid == 0){
+	      int ret;
+	      ret = system(x); // second child runs
+	      if(ret == -1) {
+		WARNING("system call error for %s",x);
+	      }
+	    }
+	    exit(EXIT_OTHER); 
 	  }
-	} else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) {
-	  double value = currentAction->getDistribution()->sample();
-	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
-	} else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) {
-            char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
-	    char *str = strdup(x);
-	    if (!str) {
-		ERROR("Out of memory duplicating string for assignment!");
+	  break;
+	default:
+	  // parent process continue
+	  // reap first child immediately
+	  pid_t ret;
+	  while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) {
+	    if (ret != -1) {
+	      ERROR("waitpid returns %1d for child %1d",ret,l_pid);
 	    }
-	    M_callVariableTable->getVar(currentAction->getVarId())->setString(str);
-	} else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) {
-            char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
-            LOG_MSG("%s\n", x);
-	} else if (currentAction->getActionType() == CAction::E_AT_LOG_WARNING) {
-            char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
-            WARNING("%s", x);
-        } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) {
-
-            if (currentAction->getCmdLine()) {
-                char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
-                // TRACE_MSG("Trying to execute [%s]", x);
-                pid_t l_pid;
-                switch(l_pid = fork())
-                {
-                    case -1:
-                        // error when forking !
-                        ERROR_NO("Forking error main");
-                        break;
-
-                    case 0:
-                       // first child process - execute the command
-                       if((l_pid = fork()) < 0) {
-                         ERROR_NO("Forking error child");
-                       } else {
-                         if( l_pid == 0){
-                         int ret;
-                         ret = system(x); // second child runs
-                         if(ret == -1) {
-                           WARNING("system call error for %s",x);
-                          }
-                        }
-                       exit(EXIT_OTHER); 
-                       }
-                       break;
-                    default:
-                       // parent process continue
-                       // reap first child immediately
-                       pid_t ret;
-                       while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) {
-                       if (ret != -1) {
-                          ERROR("waitpid returns %1d for child %1d",ret,l_pid);
-                         }
-                       }
-                       break;
-                }
-            }
-        } else /* end action == E_AT_EXECUTE_CMD */
-            if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) {
-                switch (currentAction->getIntCmd())
-                {
-                    case CAction::E_INTCMD_STOP_ALL:
-                        quitting = 1;
-                        break;
-                    case CAction::E_INTCMD_STOP_NOW:
-                        screen_exit(EXIT_TEST_RES_INTERNAL);
-                        break;
-                    case CAction::E_INTCMD_STOPCALL:
-                    default:
-                        return(call::E_AR_STOP_CALL);
-                        break;
-                }
+	  }
+	  break;
+      }
+    } else if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) {
+      switch (currentAction->getIntCmd())
+      {
+	case CAction::E_INTCMD_STOP_ALL:
+	  quitting = 1;
+	  break;
+	case CAction::E_INTCMD_STOP_NOW:
+	  screen_exit(EXIT_TEST_RES_INTERNAL);
+	  break;
+	case CAction::E_INTCMD_STOPCALL:
+	default:
+	  return(call::E_AR_STOP_CALL);
+	  break;
+      }
 #ifdef PCAPPLAY
-        } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) ||
-                   (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) {
-          play_args_t *play_args;
-          if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) {
-            play_args = &(this->play_args_a);
-          } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) {
-            play_args = &(this->play_args_v);
-          }
-          play_args->pcap = currentAction->getPcapPkts();
-          /* port number is set in [auto_]media_port interpolation */
-          if (media_ip_is_ipv6) {
-            struct sockaddr_in6 *from = (struct sockaddr_in6 *)(void *) &(play_args->from);
-            from->sin6_family = AF_INET6;
-            inet_pton(AF_INET6, media_ip, &(from->sin6_addr));
-          }
-          else {
-            struct sockaddr_in *from = (struct sockaddr_in *)(void *) &(play_args->from);
-            from->sin_family = AF_INET;
-            from->sin_addr.s_addr = inet_addr(media_ip);
-          }
-          /* Create a thread to send RTP packets */
-          pthread_attr_t attr;
-          pthread_attr_init(&attr);
+    } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) ||
+	(currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) {
+      play_args_t *play_args;
+      if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) {
+	play_args = &(this->play_args_a);
+      } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) {
+	play_args = &(this->play_args_v);
+      }
+      play_args->pcap = currentAction->getPcapPkts();
+      /* port number is set in [auto_]media_port interpolation */
+      if (media_ip_is_ipv6) {
+	struct sockaddr_in6 *from = (struct sockaddr_in6 *)(void *) &(play_args->from);
+	from->sin6_family = AF_INET6;
+	inet_pton(AF_INET6, media_ip, &(from->sin6_addr));
+      }
+      else {
+	struct sockaddr_in *from = (struct sockaddr_in *)(void *) &(play_args->from);
+	from->sin_family = AF_INET;
+	from->sin_addr.s_addr = inet_addr(media_ip);
+      }
+      /* Create a thread to send RTP packets */
+      pthread_attr_t attr;
+      pthread_attr_init(&attr);
 #ifndef PTHREAD_STACK_MIN
 #define PTHREAD_STACK_MIN	16384
 #endif
-          //pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
-          pthread_attr_setdetachstate(&attr,
-				PTHREAD_CREATE_DETACHED);
-          int ret = pthread_create(&media_thread, &attr, send_wrapper,
-		       (void *) play_args);
-          if(ret)
-            ERROR("Can create thread to send RTP packets");
-          pthread_attr_destroy(&attr);
+      //pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
+      if (media_thread != 0) {
+        // If a media_thread is already active, kill it before starting a new one
+        pthread_cancel(media_thread);
+        pthread_join(media_thread, NULL);
+        media_thread = 0;
+      }
+      int ret = pthread_create(&media_thread, &attr, send_wrapper,
+	  (void *) play_args);
+      if(ret)
+	ERROR("Can create thread to send RTP packets");
+      pthread_attr_destroy(&attr);
 #endif
-        } else {
-          ERROR("call::executeAction unknown action");
-        }
-      } // end if current action != null
-    } // end for
-  }
+    } else {
+      ERROR("call::executeAction unknown action");
+    }
+  } // end for
   return(call::E_AR_NO_ERROR);
 }
 
@@ -3449,9 +3915,6 @@ void call::getFieldFromInputFile(const char *fileName, int field, SendingMessage
 }
 
 call::T_AutoMode call::checkAutomaticResponseMode(char * P_recv) {
-
-  int L_res = E_AM_DEFAULT ;
-
   if (strcmp(P_recv, "BYE")==0) {
     return E_AM_UNEXP_BYE;
   } else if (strcmp(P_recv, "CANCEL") == 0) {
diff --git a/call.hpp b/call.hpp
index 6e86799..b09d45b 100644
--- a/call.hpp
+++ b/call.hpp
@@ -16,6 +16,7 @@
  *  Author : Richard GAYRAUD - 04 Nov 2003
  *           From Hewlett Packard Company.
  *	     Charles P. Wright from IBM Research
+ *           Andy Aicken
  */
 
 #ifndef __CALL__
@@ -46,6 +47,10 @@
 #define DEFAULT_T2_TIMER_VALUE  4000
 #define SIP_TRANSACTION_TIMEOUT 32000
 
+/* Retransmission check methods. */
+#define RTCHECK_FULL	1
+#define RTCHECK_LOOSE	2
+
 #ifdef __HPUX
   extern int createAuthHeader(char * user, char * password, char * method, char * uri, char * msgbody, char * auth, char * aka_OP, char * aka_AMF, char * aka_K, char * result);
 #else
@@ -53,6 +58,12 @@
   extern "C" { int verifyAuthHeader(char * user, char * password, char * method, char * auth); }
 #endif
 
+struct txnInstanceInfo {
+  char *txnID;
+  unsigned long txnResp;
+  int ackIndex;
+};
+
 class call : virtual public task, virtual public listener, public virtual socketowner {
 public:
   /* These are wrappers for various circumstances, (private) init does the real work. */
@@ -60,21 +71,21 @@ public:
   call(char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest);
   call(char *p_id, struct sipp_socket *socket, struct sockaddr_storage *dest);
   static call *add_call(int userId, bool ipv6, struct sockaddr_storage *dest);
-  call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic);
+  call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall);
 
   virtual ~call();
 
-  virtual bool process_incoming(char * msg);
+  virtual bool process_incoming(char * msg, struct sockaddr_storage *src = NULL);
   virtual bool  process_twinSippCom(char * msg);
 
   virtual bool run();
   /* Terminate this call, depending on action results and timewait. */
-  virtual bool terminate(CStat::E_Action reason);
+  virtual void terminate(CStat::E_Action reason);
   virtual void tcpClose();
 
   /* When should this call wake up? */
   virtual unsigned int wake();
-  virtual bool  abortCall();                  // call aborted with BYE or CANCEL
+  virtual bool  abortCall(bool writeLog); // call aborted with BYE or CANCEL
   virtual void abort();
 
   /* Dump call info to error log. */
@@ -93,15 +104,27 @@ public:
 
   void setLastMsg(const char *msg);
   bool  automaticResponseMode(T_AutoMode P_case, char* P_recv);
+  const char *getLastReceived() { return last_recv_msg; };
 
 private:
   /* This is the core constructor function. */
-  void init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic);
+  void init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall);
+  /* This this call for initialization? */
+  bool initCall;
 
   struct sockaddr_storage call_peer;
 
   scenario *call_scenario;
   unsigned int   number;
+
+public:
+  static   int   maxDynamicId;    // max value for dynamicId; this value is reached !
+  static   int   startDynamicId;  // offset for first dynamicId  FIXME:in CmdLine
+  static   int   stepDynamicId;   // step of increment for dynamicId
+  static   int   dynamicId;       // a counter for general use, incrementing  by  stepDynamicId starting at startDynamicId  wrapping at maxDynamicId  GLOBALY
+private:
+
+
   unsigned int   tdm_map_number;
 
   int		msg_index;
@@ -112,6 +135,7 @@ private:
    * are kept in this index.) */
   int		 last_send_index;
   char         * last_send_msg;
+  int        last_send_len;
 
   /* How long until sending this message times out. */
   unsigned int   send_timeout;
@@ -128,8 +152,8 @@ private:
    * a cause relationship, so the next time this cookie will be recvd,
    * we will retransmit the same message we sent this time */
   unsigned long  recv_retrans_hash;
-  unsigned int   recv_retrans_recv_index;
-  unsigned int   recv_retrans_send_index;
+  int   recv_retrans_recv_index;
+  int   recv_retrans_send_index;
   unsigned int   recv_timeout;
 
   /* holds the route set */
@@ -160,10 +184,9 @@ private:
   unsigned int   paused_until;
 
   unsigned long  start_time;
-  unsigned long  long start_time_rtd[MAX_RTD_INFO_LENGTH];
+  unsigned long long *start_time_rtd;
+  bool           *rtd_done;
 
-  bool           rtd_done[MAX_RTD_INFO_LENGTH];
-  
   char           *peer_tag;
   
   struct sipp_socket *call_remote_socket;
@@ -186,14 +209,16 @@ private:
   VariableTable *M_callVariableTable;
 
   /* Our transaction IDs. */
-  char **txnID;
+  struct txnInstanceInfo *transactions;
 
   /* result of execute action */
   enum T_ActionResult
     {
       E_AR_NO_ERROR = 0,
       E_AR_REGEXP_DOESNT_MATCH,
+      E_AR_REGEXP_SHOULDNT_MATCH,
       E_AR_STOP_CALL,
+      E_AR_CONNECT_FAILED,
       E_AR_HDR_NOT_FOUND
     };
 
@@ -206,7 +231,8 @@ private:
   void computeRouteSetAndRemoteTargetUri (char* rrList, char* contact, bool bRequestIncoming);
   bool matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn);
 
-  T_ActionResult executeAction(char * msg, int scenarioIndex);
+  bool executeMessage(message *curmsg);
+  T_ActionResult executeAction(char * msg, message *message);
   void  extractSubMessage(char * msg, char * matchingString, char* result, bool case_indep, 
 							     int occurrence, bool headers); 
   bool  rejectCall();
@@ -215,9 +241,9 @@ private:
   // P_index use for message index in scenario and ctrl of CRLF
   // P_index = -2 No ctrl of CRLF
   // P_index = -1 Add crlf to end of message
-  char* createSendingMessage(SendingMessage *src, int P_index);
+  char* createSendingMessage(SendingMessage *src, int P_index, int *msgLen=NULL);
   char* createSendingMessage(char * src, int P_index, bool skip_sanity = false);
-  char* createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buflen);
+  char* createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buflen, int *msgLen=NULL);
 
   // method for the management of unexpected messages 
   bool  checkInternalCmd(char* cmd);  // check of specific internal command
@@ -228,12 +254,12 @@ private:
 		int search_index);    // 3pcc extended mode:check if 
 				      // the twin message received
 				      // comes from the expected sender
-  void   sendBuffer(char *buf);        // send a message out of a scenario
+  void   sendBuffer(char *buf, int len = 0);     // send a message out of a scenario
                                       // execution
 
   T_AutoMode  checkAutomaticResponseMode(char * P_recv);
 
-  int   sendCmdMessage(int index); // 3PCC
+  int   sendCmdMessage(message *curmsg); // 3PCC
 
   int   sendCmdBuffer(char* cmd); // for 3PCC, send a command out of a 
                                   // scenario execution
@@ -252,13 +278,13 @@ private:
   /* rc == true means call not deleted by processing */
   bool next();
   bool process_unexpected(char * msg);
-  void do_bookkeeping(int index);
+  void do_bookkeeping(message *curmsg);
 
   void  extract_cseq_method (char* responseCseq, char* msg);
   void  extract_transaction (char* txn, char* msg);
 
-  int   send_raw(char * msg, int index);
-  char * send_scene(int index, int *send_status);
+  int   send_raw(char * msg, int index, int len);
+  char * send_scene(int index, int *send_status, int *msgLen);
   bool   connect_socket_if_needed();
 
   char * compute_cseq(char * src);
@@ -268,6 +294,7 @@ private:
   char * get_header(char* message, char * name, bool content);
   char * get_first_line(char* message);
   char * get_last_request_uri();
+  unsigned long hash(char * msg);
 
   typedef std::map <std::string, int> file_line_map;
   file_line_map *m_lineNumber;
@@ -292,8 +319,13 @@ private:
   SSL_CTX   *m_ctx_ssl ;
   BIO       *m_bio     ;
 #endif
+
+  int _callDebug(char *fmt, ...);
+  char *debugBuffer;
+  int debugLength;
 };
 
+
 /* Default Message Functions. */
 void init_default_messages();
 void free_default_messages();
diff --git a/deadcall.cpp b/deadcall.cpp
index 67c57d0..3b4c08e 100644
--- a/deadcall.cpp
+++ b/deadcall.cpp
@@ -62,7 +62,7 @@ deadcall::~deadcall() {
   free(reason);
 }
 
-bool deadcall::process_incoming(char * msg) {
+bool deadcall::process_incoming(char * msg, struct sockaddr_storage *src) {
   char buffer[MAX_HEADER_LEN];
 
   CStat::globalStat(CStat::E_DEAD_CALL_MSGS);
@@ -97,10 +97,6 @@ bool deadcall::run() {
   }
 }
 
-void deadcall::abort() {
-  delete this;
-}
-
 unsigned int deadcall::wake() {
   return expiration;
 }
diff --git a/deadcall.hpp b/deadcall.hpp
index 780507c..5492e4c 100644
--- a/deadcall.hpp
+++ b/deadcall.hpp
@@ -5,7 +5,7 @@ public:
   deadcall(char *id, char * reason);
   ~deadcall();
 
-  virtual bool process_incoming(char * msg);
+  virtual bool process_incoming(char * msg, struct sockaddr_storage *);
   virtual bool  process_twinSippCom(char * msg);
 
   virtual bool run();
@@ -13,12 +13,10 @@ public:
   /* When should this call wake up? */
   virtual unsigned int wake();
 
-  virtual void abort();
-
   /* Dump call info to error log. */
   virtual void dump();
 
 protected:
-  int expiration;
+  unsigned long expiration;
   char *reason;
 };
diff --git a/fortune.cpp b/fortune.cpp
new file mode 100644
index 0000000..000e47e
--- /dev/null
+++ b/fortune.cpp
@@ -0,0 +1,85 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author : Richard GAYRAUD - 04 Nov 2003
+ *           Olivier Jacques
+ *           From Hewlett Packard Company.
+ *           Charles P Wright from IBM Research
+ */
+#include "sipp.hpp"
+
+/* This is a fun sample of creating your own extensible keyword. */
+int fortune(call *call, MessageComponent *comp, char *buf, int len) {
+	int pipes[2];
+	char localbuf[SIPP_MAX_MSG_SIZE];
+	char *p = localbuf;
+	int ret;
+	int written = 0;
+
+	if (pipe(pipes) == -1) {
+		ERROR("Could not create pipes!\n");
+	}
+
+	switch (fork()) {
+	case -1:
+		ERROR("Fork failed: %s\n", strerror(errno));
+	case 0:
+		/* We are the child. */
+		close(pipes[0]);
+		dup2(pipes[1], fileno(stdout));
+		dup2(pipes[1], fileno(stderr));
+		close(fileno(stdin));
+		system("/usr/bin/fortune");
+		exit (127);
+	default:
+		/* We are the parent*/
+		close(pipes[1]);
+		while ((ret = read(pipes[0], p, sizeof(localbuf) - (p - localbuf))) > 0) {
+			p += ret;
+		}
+		*p = '\0';
+		close(pipes[0]);
+
+		if (len > p - localbuf) {
+			len = p -localbuf;
+		}
+
+		p = localbuf;
+		while(len-- > 0) {
+			if (*p == '\n') {
+				if (len < 3) {
+					break;
+				}
+				*buf++ = '\r';
+				*buf++ = '\n';
+				*buf++ = ' ';
+				written += 3;
+				p++;
+			} else {
+				*buf++ = *p++;
+				written++;
+			}
+		}
+		break;
+	}
+
+	return written;
+}
+
+/* On initialization we register our keywords. */
+extern "C" int init(void) {
+	registerKeyword("fortune", fortune);
+	return 0;
+}
diff --git a/infile.cpp b/infile.cpp
index afdcef7..cbdbbfd 100644
--- a/infile.cpp
+++ b/infile.cpp
@@ -130,6 +130,9 @@ FileContents::FileContents(const char *fileName) {
   }
 
   delete inFile;
+
+  indexMap = NULL;
+  indexField = -1;
 }
 
 int FileContents::getLine(int line, char *dest, int len) {
@@ -275,3 +278,89 @@ void FileContents::dump(void)
         WARNING("%s:%d reads [%s]", fileName, i, fileLines[i].c_str());
     }
 }
+
+void FileContents::index(int field) {
+  this->indexField = field;
+
+  indexMap = new str_int_map;
+  for (int line = 0; line < numLines(); line++) {
+    reIndex(line);
+  }
+}
+
+int FileContents::lookup(char *key) {
+  if (indexField == -1) {
+    ERROR("Invalid Index File: %s", fileName);
+  }
+  if (!indexMap) {
+    ERROR("Invalid Index File: %s", fileName);
+  }
+
+  str_int_map::iterator index_it = indexMap->find(key);
+  if (index_it == indexMap->end()) {
+    return -1;
+  }
+  return index_it->second;
+}
+
+
+void FileContents::insert(char *value) {
+  if (printfFile) {
+    ERROR("Can not insert or replace into a printf file: %s", fileName);
+  }
+  fileLines.push_back(value);
+  realLinesInFile++;
+  numLinesInFile++;
+  if (indexField != -1) {
+    reIndex(realLinesInFile - 1);
+  }
+  char line[1024];
+  getLine(realLinesInFile - 1, line, sizeof(line));
+  char tmp[1024];
+  getField(realLinesInFile - 1, 0, tmp, sizeof(tmp));
+}
+
+void FileContents::replace(int line, char *value) {
+  if (printfFile) {
+    ERROR("Can not insert or replace into a printf file: %s", fileName);
+  }
+  if (line >= realLinesInFile || line < 0) {
+    ERROR("Invalid line number (%d) for file: %s (%d lines)", line, fileName, realLinesInFile);
+  }
+  deIndex(line);
+  fileLines[line] = value;
+  reIndex(line);
+}
+
+void FileContents::reIndex(int line) {
+  if (indexField == -1) {
+    return;
+  }
+  assert(line >= 0);
+  assert(line < realLinesInFile);
+
+  char tmp[SIPP_MAX_MSG_SIZE];
+  getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE);
+  str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp));
+  if (index_it != indexMap->end()) {
+    indexMap->erase(index_it);
+  }
+  indexMap->insert(pair<str_int_map::key_type,int>(str_int_map::key_type(tmp), line));
+}
+
+void FileContents::deIndex(int line) {
+  if (indexField == -1) {
+    return;
+  }
+  assert(line >= 0);
+  assert(line < realLinesInFile);
+
+  char tmp[SIPP_MAX_MSG_SIZE];
+  getField(line, indexField, tmp, SIPP_MAX_MSG_SIZE);
+  str_int_map::iterator index_it = indexMap->find(str_int_map::key_type(tmp));
+  if (index_it != indexMap->end()) {
+    if (index_it->second == line) {
+      indexMap->erase(index_it);
+    }
+  }
+}
diff --git a/infile.hpp b/infile.hpp
index fa6a9fb..894d305 100644
--- a/infile.hpp
+++ b/infile.hpp
@@ -32,7 +32,14 @@ public:
   int numLines();
   int nextLine(int userId);
   void dump();
+  void index(int field);
+  int lookup(char *key);
+  void insert(char *value);
+  void replace(int line, char *value);
 private:
+  void reIndex(int line);
+  void deIndex(int line);
+
   typedef enum {
     InputFileSequentialOrder = 0,
     InputFileRandomOrder,
@@ -48,6 +55,9 @@ private:
   int printfMultiple;
   int numLinesInFile;
   int realLinesInFile;
+
+  int indexField;
+  str_int_map *indexMap;
 };
 
 #endif
diff --git a/listener.hpp b/listener.hpp
index d1d7746..ba89585 100644
--- a/listener.hpp
+++ b/listener.hpp
@@ -34,7 +34,7 @@ public:
   listener(char *id, bool listening);
   virtual ~listener();
   char *getId();
-  virtual bool process_incoming(char * msg) = 0;
+  virtual bool process_incoming(char * msg, struct sockaddr_storage *src) = 0;
   virtual bool process_twinSippCom(char * msg) = 0;
 
 protected:
diff --git a/message.cpp b/message.cpp
index 68f73d0..f94eed4 100644
--- a/message.cpp
+++ b/message.cpp
@@ -33,6 +33,7 @@
  *           Ben Evans from Open Cloud
  *           Marc Van Diest from Belgacom
  *	     Stefan Esser
+ *           Andy Aicken
  */
 
 #include "sipp.hpp"
@@ -43,6 +44,9 @@ struct KeywordMap {
 	MessageCompType type;
 };
 
+typedef std::map<std::string, customKeyword> kw_map;
+kw_map keyword_map;
+
 /* These keywords take no parameters. */
 struct KeywordMap SimpleKeywords[] = {
   {"remote_ip", E_Message_Remote_IP },
@@ -60,6 +64,7 @@ struct KeywordMap SimpleKeywords[] = {
   {"media_port", E_Message_Media_Port },
   {"media_ip_type", E_Message_Media_IP_Type },
   {"call_number", E_Message_Call_Number },
+  {"dynamic_id", E_Message_DynamicId }, // wrapping global counter
   {"call_id", E_Message_Call_ID },
   {"cseq", E_Message_CSEQ },
   {"pid", E_Message_PID },
@@ -69,7 +74,6 @@ struct KeywordMap SimpleKeywords[] = {
   {"next_url", E_Message_Next_Url },
   {"len", E_Message_Len },
   {"peer_tag_param", E_Message_Peer_Tag_Param },
-  {"last_peer_tag_param", E_Message_Last_Peer_Tag_Param },
   {"last_Request_URI", E_Message_Last_Request_URI },
   {"last_cseq_number", E_Message_Last_CSeq_Number },
   {"last_message", E_Message_Last_Message },
@@ -79,18 +83,17 @@ struct KeywordMap SimpleKeywords[] = {
   {"users", E_Message_Users },
   {"userid", E_Message_UserID },
   {"timestamp", E_Message_Timestamp },
+  {"sipp_version", E_Message_SippVersion },
 };
 
 #define KEYWORD_SIZE 256
 
 SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sanity) {
-  char *osrc = src;
+    char *osrc = src;
     char * literal;
+    int    literalLen;
     char * dest;
     char * key;
-    char * length_marker = NULL;
-    int    offset = 0;
-    int    len_offset = 0;
     char   current_line[MAX_HEADER_LEN];
     char * line_mark = NULL;
     char * tsrc;
@@ -99,6 +102,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
     this->msg_scenario = msg_scenario;
     
     dest = literal = (char *)malloc(strlen(src) + num_cr + 1);
+    literalLen = 0;
  
     current_line[0] = '\0';
     *dest = 0;
@@ -134,22 +138,29 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
         *dest++ = *src++;
       } else {
 	/* We have found a keyword, store the literal that we have been generating. */
-	*dest = '\0';
-	literal = (char *)realloc(literal, strlen(literal) + 1);
-	if (!literal) { ERROR("Out of memory!"); }
+        literalLen = dest - literal;
+	if (literalLen) {
+	  *dest = '\0';
+	  literal = (char *)realloc(literal, literalLen + 1);
+	  if (!literal) { ERROR("Out of memory!"); }
 
-	MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
-	if (!newcomp) { ERROR("Out of memory!"); }
+	  MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
+	  if (!newcomp) { ERROR("Out of memory!"); }
 
-	newcomp->type = E_Message_Literal;
-	newcomp->literal = literal;
-	messageComponents.push_back(newcomp);
+	  newcomp->type = E_Message_Literal;
+	  newcomp->literal = literal;
+	  newcomp->literalLen = literalLen; // length without the terminator 
+	  messageComponents.push_back(newcomp);
+	} else {
+	  free(literal);
+	}
 
 	dest = literal = (char *)malloc(strlen(src) + num_cr + 1);
+	literalLen = 0;
 	*dest = '\0';
 
 	/* Now lets determine which keyword we have. */
-	newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
+	MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
 	if (!newcomp) { ERROR("Out of memory!"); }
 
         char keyword [KEYWORD_SIZE+1];
@@ -161,6 +172,9 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
 			do {
 				tsrc++;
 			} while(*tsrc && *tsrc != '\"');
+			if (!*tsrc) {
+				break;
+			}
 		}
 		if (*tsrc == '[')
 			break;
@@ -206,8 +220,26 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
 	    }
 	}
 
+	char *spc = NULL;
+	char ospc;
+	if ((spc = strchr(keyword, ' '))) {
+		ospc = *spc;
+		*spc = '\0';
+	}
+	kw_map::iterator it = keyword_map.find(keyword);
+	if (spc) {
+	  *spc = ospc;
+	}
+
+	if (it != keyword_map.end()) {
+	  newcomp->type = E_Message_Custom;
+	  newcomp->comp_param.fxn = it->second;
+	  messageComponents.push_back(newcomp);
+	  continue;
+	}
+
 	bool simple_keyword = false;
-	for (int i = 0; i < sizeof(SimpleKeywords)/sizeof(SimpleKeywords[0]); i++) {
+	for (unsigned int i = 0; i < sizeof(SimpleKeywords)/sizeof(SimpleKeywords[0]); i++) {
 	  if (!strcmp(keyword, SimpleKeywords[i].keyword)) {
 		newcomp->type = SimpleKeywords[i].type;
 		simple_keyword = true;
@@ -246,6 +278,17 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
 	    /* Turn this into a new message component. */
 	    newcomp->comp_param.field_param.line = new SendingMessage(msg_scenario, line, true);
 	  }
+        } else if(!strncmp(keyword, "file", strlen("file"))) {
+	  newcomp->type = E_Message_File;
+
+	  /* Parse out the interesting things like file and number. */
+	  char fileName[KEYWORD_SIZE];
+	  getKeywordParam(keyword, "name=", fileName);
+	  if (fileName[0] == '\0') {
+	    ERROR("No name specified for 'file' keyword!\n");
+	  }
+	  /* Turn this into a new message component. */
+	  newcomp->comp_param.filename = new SendingMessage(msg_scenario, fileName, true);
         } else if(*keyword == '$') {
 	  newcomp->type = E_Message_Variable;
 	  if (!msg_scenario) {
@@ -264,6 +307,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
 	  getKeywordParam(keyword, "variable=", varName);
 
 	  newcomp->literal = strdup(filltext);
+	  newcomp->literalLen = strlen(newcomp->literal);
 	  if (!msg_scenario) {
 	    ERROR("SendingMessage with variable usage outside of scenario!");
 	  }
@@ -271,6 +315,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
      } else if(!strncmp(keyword, "last_", strlen("last_"))) {
        newcomp->type = E_Message_Last_Header;
        newcomp->literal = strdup(keyword + strlen("last_"));
+       newcomp->literalLen = strlen(newcomp->literal);
      } else if(!strncmp(keyword, "authentication", strlen("authentication"))) {
        parseAuthenticationKeyword(msg_scenario, newcomp, keyword);
      }
@@ -297,6 +342,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
             if(!strcmp(keyword, msg1)) {
 	      newcomp->type = E_Message_Literal;
 	      newcomp->literal = strdup(msg2);
+	      newcomp->literalLen = strlen(newcomp->literal);
               break;
             }
             ++i;
@@ -312,7 +358,8 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
     }
     if (literal[0]) {
       *dest++ = '\0';
-      literal = (char *)realloc(literal, strlen(literal) + 1);
+      literalLen = dest - literal;
+      literal = (char *)realloc(literal, literalLen);
       if (!literal) { ERROR("Out of memory!"); } 
 
       MessageComponent *newcomp = (MessageComponent *)calloc(1, sizeof(MessageComponent));
@@ -320,6 +367,7 @@ SendingMessage::SendingMessage(scenario *msg_scenario, char *src, bool skip_sani
 
       newcomp->type = E_Message_Literal;
       newcomp->literal = literal;
+      newcomp->literalLen = literalLen-1;
       messageComponents.push_back(newcomp);
     } else {
       free(literal);
@@ -436,7 +484,7 @@ void SendingMessage::getKeywordParam(char * src, char * param, char * output)
 
   len = 0;
   key = NULL;
-  if(tmp = strstr(src, param)) {
+  if ((tmp = strstr(src, param))) {
     tmp += strlen(param);
     key = tmp;
     if ((*key == '0') && (*(key+1) == 'x')) {
@@ -532,3 +580,12 @@ int SendingMessage::numComponents() {
 struct MessageComponent *SendingMessage::getComponent(int i) {
   return messageComponents[i];
 }
+
+/* This is very simplistic and does not yet allow any arguments, but it is a start. */
+int registerKeyword(char *keyword, customKeyword fxn) {
+	if (keyword_map.find(keyword) != keyword_map.end()) {
+		ERROR("Can not register keyword '%s', already registered!\n", keyword);
+	}
+	keyword_map[keyword] = fxn;
+	return 0;
+}
diff --git a/message.hpp b/message.hpp
index e1225ae..65dd9ac 100644
--- a/message.hpp
+++ b/message.hpp
@@ -32,6 +32,7 @@
  *           Jan Andres from Freenet
  *           Ben Evans from Open Cloud
  *           Marc Van Diest from Belgacom
+ *           Andy Aicken
  */
 
 #ifndef __MESSAGE__
@@ -58,6 +59,7 @@ typedef enum {
   E_Message_Media_Port,
   E_Message_Media_IP_Type,
   E_Message_Call_Number,
+  E_Message_DynamicId,   // general usage, global, autoincrementing and wrapping counter
   E_Message_Call_ID,
   E_Message_CSEQ,
   E_Message_PID,
@@ -67,7 +69,6 @@ typedef enum {
   E_Message_Next_Url,
   E_Message_Len,
   E_Message_Peer_Tag_Param,
-  E_Message_Last_Peer_Tag_Param,
   E_Message_Routes,
   E_Message_Variable,
   E_Message_Fill,
@@ -82,6 +83,9 @@ typedef enum {
   E_Message_Users,
   E_Message_UserID,
   E_Message_Timestamp,
+  E_Message_SippVersion,
+  E_Message_File,
+  E_Message_Custom,
 } MessageCompType;
 
 class SendingMessage {
@@ -119,10 +123,17 @@ class SendingMessage {
     static void getKeywordParam(char * src, char * param, char * output);
 };
 
+/* Custom Keyword Function Type. */
+struct MessageComponent;
+class call;
+typedef int (*customKeyword)(call *, struct MessageComponent *, char *, int);
+/* Custom Keyword Registration Function. */
+int registerKeyword(char *keyword, customKeyword fxn);
 
 struct MessageComponent {
   MessageCompType type;
   char *literal;
+  int literalLen;
   int offset;
   int varId;
   union u {
@@ -140,6 +151,8 @@ struct MessageComponent {
       int field;
       SendingMessage *line;
     } field_param;
+    SendingMessage *filename;
+    customKeyword fxn;
   } comp_param;
 };
 
diff --git a/opentask.cpp b/opentask.cpp
new file mode 100644
index 0000000..fda9f2f
--- /dev/null
+++ b/opentask.cpp
@@ -0,0 +1,257 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author : Richard GAYRAUD - 04 Nov 2003
+ *           Marc LAMBERTON
+ *           Olivier JACQUES
+ *           Herve PELLAN
+ *           David MANSUTTI
+ *           Francois-Xavier Kowalski
+ *           Gerard Lyonnaz
+ *           From Hewlett Packard Company.
+ *           F. Tarek Rogers
+ *           Peter Higginson
+ *           Vincent Luba
+ *           Shriram Natarajan
+ *           Guillaume Teissier from FTR&D
+ *           Clement Chen
+ *           Wolfgang Beck
+ *           Charles P Wright from IBM Research
+ */
+#include "sipp.hpp"
+
+class opentask *opentask::instance = NULL;
+unsigned long opentask::calls_since_last_rate_change = 0;
+unsigned long opentask::last_rate_change_time = 0;
+
+void opentask::initialize() {
+  assert(instance == NULL);
+  instance = new opentask();
+}
+
+opentask::opentask() {
+  setRunning();
+}
+
+opentask::~opentask() {
+  instance = NULL;
+}
+
+void opentask::dump() {
+  WARNING("Uniform rate call generation task: %d", rate);
+}
+
+unsigned int opentask::wake() {
+  if (paused) {
+    return 0;
+  } else if (users >= 0) {
+    /* We need to wait until another call is terminated. */
+    return 0;
+  } else {
+    /* We need to compute when the next call is going to be opened. */
+    return (unsigned long) (last_rate_change_time + ((calls_since_last_rate_change + 1) / (rate/rate_period_ms)));
+  }
+}
+
+bool opentask::run() {
+  int calls_to_open = 0;
+
+  if (quitting) {
+    delete this;
+    return false;
+  }
+
+  if (paused) {
+    setPaused();
+    return true;
+  }
+
+  long l=0;
+  unsigned long long current_calls = main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall);
+  unsigned long long total_calls = main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated);
+
+  if (users >= 0) {
+    calls_to_open = ((l = (users - current_calls)) > 0) ? l : 0;
+  } else {
+    calls_to_open = (unsigned int)
+      ((l=(long)floor(((clock_tick - last_rate_change_time) * rate/rate_period_ms)
+		      - calls_since_last_rate_change))>0?l:0);
+  }
+
+  if (total_calls + calls_to_open > stop_after) {
+    calls_to_open = stop_after - total_calls;
+  }
+
+  if (open_calls_allowed && (current_calls + calls_to_open > open_calls_allowed)) {
+    calls_to_open = open_calls_allowed - current_calls;
+  }
+
+  if (calls_to_open < 0) {
+    calls_to_open = 0;
+  }
+
+  unsigned int start_clock = getmilliseconds();
+
+
+  while(calls_to_open--)
+  {
+      /* Associate a user with this call, if we are in users mode. */
+      int userid = 0;
+      if (users >= 0) {
+	userid = freeUsers.back();
+	freeUsers.pop_back();
+      }
+
+      // adding a new OUTGOING CALL
+      main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL);
+      call * call_ptr = call::add_call(userid, is_ipv6, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr);
+      if(!call_ptr) {
+	ERROR("Out of memory allocating call!");
+      }
+
+      calls_since_last_rate_change++;
+
+      outbound_congestion = false;
+
+      if (!multisocket) {
+	switch(transport) {
+	  case T_UDP:
+	    call_ptr->associate_socket(main_socket);
+	    main_socket->ss_count++;
+	    break;
+	  case T_TCP:
+	  case T_TLS:
+	    call_ptr->associate_socket(tcp_multiplex);
+	    tcp_multiplex->ss_count++;
+	    break;
+	}
+      }
+      if (getmilliseconds() > start_clock) {
+	break;
+      }
+    }
+
+    /* We can pause. */
+    if (calls_to_open <= 0) {
+      setPaused();
+    } else {
+      /* Stay running. */
+    }
+
+    // Quit after asked number of calls is reached
+    if(total_calls >= stop_after) {
+      quitting = 1;
+      return false;
+    }
+
+    return true;
+}
+
+void opentask::set_paused(bool new_paused)
+{
+  if (!instance) {
+    /* Doesn't do anything, we must be in server mode. */
+    return;
+  }
+  if (new_paused) {
+    instance->setPaused();
+  } else {
+    instance->setRunning();
+    if (users >= 0) {
+      set_users(users);
+    } else {
+      set_rate(rate);
+    }
+  }
+  paused = new_paused;
+}
+
+void opentask::set_rate(double new_rate)
+{
+  if (!instance) {
+    /* Doesn't do anything, we must be in server mode. */
+  }
+
+  rate = new_rate;
+  if(rate < 0) {
+    rate = 0;
+  }
+
+  last_rate_change_time = getmilliseconds();
+  calls_since_last_rate_change = 0;
+
+  if(!open_calls_user_setting) {
+
+    int call_duration_min =  main_scenario->duration;
+
+    if(duration > call_duration_min) call_duration_min = duration;
+
+    if(call_duration_min < 1000) call_duration_min = 1000;
+
+    open_calls_allowed = (int)((3.0 * rate * call_duration_min) / (double)rate_period_ms);
+    if(!open_calls_allowed) {
+      open_calls_allowed = 1;
+    }
+  }
+}
+
+void opentask::set_users(int new_users)
+{
+  if (!instance) {
+    /* Doesn't do anything, we must be in server mode. */
+    return;
+  }
+
+  if (new_users < 0) {
+    new_users = 0;
+  }
+  assert(users >= 0);
+
+  if (users < new_users ) {
+    while (users < new_users) {
+      int userid;
+      if (!retiredUsers.empty()) {
+	userid = retiredUsers.back();
+	retiredUsers.pop_back();
+      } else {
+	userid = users + 1;
+	userVarMap[userid] = new VariableTable(userVariables);
+      }
+      freeUsers.push_front(userid);
+      users++;
+    }
+  }
+
+  users = open_calls_allowed = new_users;
+
+  last_rate_change_time = clock_tick;
+  calls_since_last_rate_change = 0;
+
+  assert(open_calls_user_setting);
+
+  instance->setRunning();
+}
+
+void opentask::freeUser(int userId) {
+  if (main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall) > open_calls_allowed) {
+    retiredUsers.push_front(userId);
+  } else {
+    freeUsers.push_front(userId);
+    /* Wake up the call creation thread. */
+    if (instance) {
+      instance->setRunning();
+    }
+  }
+}
diff --git a/opentask.hpp b/opentask.hpp
new file mode 100644
index 0000000..3e70fa3
--- /dev/null
+++ b/opentask.hpp
@@ -0,0 +1,62 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author : Richard GAYRAUD - 04 Nov 2003
+ *           Marc LAMBERTON
+ *           Olivier JACQUES
+ *           Herve PELLAN
+ *           David MANSUTTI
+ *           Francois-Xavier Kowalski
+ *           Gerard Lyonnaz
+ *           From Hewlett Packard Company.
+ *           F. Tarek Rogers
+ *           Peter Higginson
+ *           Vincent Luba
+ *           Shriram Natarajan
+ *           Guillaume Teissier from FTR&D
+ *           Clement Chen
+ *           Wolfgang Beck
+ *           Charles P Wright from IBM Research
+ */
+
+#ifndef OPENTASK_HPP
+#define OPENTASK_HPP
+
+#include "task.hpp"
+
+class opentask : public task {
+public:
+  static void initialize();
+  static void set_rate(double new_rate);
+  static void set_users(int new_users);
+  static void set_paused(bool paused);
+  static void freeUser(int userId);
+
+  bool run();
+  void dump();
+
+protected:
+  unsigned int wake();
+
+private:
+  opentask();
+  virtual ~opentask();
+
+  static class opentask *instance;
+  static unsigned long calls_since_last_rate_change;
+  static unsigned long last_rate_change_time;
+};
+
+#endif
diff --git a/reporttask.cpp b/reporttask.cpp
new file mode 100644
index 0000000..f89393a
--- /dev/null
+++ b/reporttask.cpp
@@ -0,0 +1,117 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author : Richard GAYRAUD - 04 Nov 2003
+ *           Marc LAMBERTON
+ *           Olivier JACQUES
+ *           Herve PELLAN
+ *           David MANSUTTI
+ *           Francois-Xavier Kowalski
+ *           Gerard Lyonnaz
+ *           From Hewlett Packard Company.
+ *           F. Tarek Rogers
+ *           Peter Higginson
+ *           Vincent Luba
+ *           Shriram Natarajan
+ *           Guillaume Teissier from FTR&D
+ *           Clement Chen
+ *           Wolfgang Beck
+ *           Charles P Wright from IBM Research
+ */
+#include "sipp.hpp"
+
+class stattask *stattask::instance = NULL;
+class screentask *screentask::instance = NULL;
+
+void stattask::initialize() {
+  assert(instance == NULL);
+  if (dumpInFile || useCountf || rate_increase) {
+    instance = new stattask();
+  }
+}
+
+void screentask::initialize() {
+  assert(instance == NULL);
+  if (report_freq) {
+    instance = new screentask();
+  }
+}
+
+void stattask::dump() {
+  WARNING("Statistics reporting task.");
+}
+void screentask::dump() {
+  WARNING("Screen update task.");
+}
+
+void screentask::report(bool last) {
+    print_statistics(last);
+    display_scenario->stats->computeStat(CStat::E_RESET_PD_COUNTERS);
+    last_report_time  = getmilliseconds();
+    scheduling_loops = 0;
+}
+
+bool screentask::run() {
+  if (quitting > 11) {
+    delete this;
+    return false;
+  }
+
+  if (getmilliseconds() - last_report_time >= report_freq) {
+    report(false);
+  }
+
+  setPaused();
+  return true;
+}
+
+unsigned int screentask::wake() {
+  return last_report_time + report_freq;
+}
+
+void stattask::report() {
+    if(dumpInFile) {
+      main_scenario->stats->dumpData();
+    }
+    if (useCountf) {
+      print_count_file(countf, 0);
+    }
+
+    main_scenario->stats->computeStat(CStat::E_RESET_PL_COUNTERS);
+    last_dump_time = clock_tick;
+}
+
+bool stattask::run() {
+  /* Statistics Logs. */
+  if((getmilliseconds() - last_dump_time) >= report_freq_dumpLog)  {
+    if (rate_increase) {
+      rate += rate_increase;
+      if (rate_max && (rate > rate_max)) {
+	rate = rate_max;
+	if (rate_quit) {
+	  quitting += 10;
+	}
+      }
+      opentask::set_rate(rate);
+    }
+    report();
+  }
+  setPaused();
+  return true;
+}
+
+unsigned int stattask::wake() {
+  return last_dump_time + report_freq_dumpLog;
+}
diff --git a/reporttask.hpp b/reporttask.hpp
new file mode 100644
index 0000000..0e834be
--- /dev/null
+++ b/reporttask.hpp
@@ -0,0 +1,61 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author : Richard GAYRAUD - 04 Nov 2003
+ *           Marc LAMBERTON
+ *           Olivier JACQUES
+ *           Herve PELLAN
+ *           David MANSUTTI
+ *           Francois-Xavier Kowalski
+ *           Gerard Lyonnaz
+ *           From Hewlett Packard Company.
+ *           F. Tarek Rogers
+ *           Peter Higginson
+ *           Vincent Luba
+ *           Shriram Natarajan
+ *           Guillaume Teissier from FTR&D
+ *           Clement Chen
+ *           Wolfgang Beck
+ *           Charles P Wright from IBM Research
+ */
+
+#ifndef REPORTTASK_HPP
+#define REPORTTASK_HPP
+
+#include "task.hpp"
+
+class screentask : public task {
+public:
+  unsigned int wake();
+  static void report(bool last);
+  static void initialize();
+  bool run();
+  void dump();
+private:
+  static class screentask *instance;
+};
+
+class stattask : public task {
+public:
+  unsigned int wake();
+  static void report();
+  static void initialize();
+  bool run();
+  void dump();
+private:
+  static class stattask *instance;
+};
+
+#endif
diff --git a/scenario.cpp b/scenario.cpp
index bb6433b..d65cbf9 100644
--- a/scenario.cpp
+++ b/scenario.cpp
@@ -24,6 +24,7 @@
  *           Wolfgang Beck
  *           Marc Van Diest from Belgacom
  *	     Charles P. Wright from IBM Research
+ *	     Michael Stovenour
  */
 
 #include <stdlib.h>
@@ -36,9 +37,10 @@
 
 /************************ Class Constructor *************************/
 
-message::message()
+message::message(int index, const char *desc)
 {
-  //ugly memset(this, 0, sizeof(message));
+  this->index = index;
+  this->desc = desc;
   pause_distribution = NULL;
   pause_variable = -1;
   pause_desc = NULL;
@@ -68,9 +70,13 @@ message::message()
   hide = 0;
   display_str = NULL;
   test = -1;
+  condexec = -1;
+  condexec_inverse = false;
   chance = 0;/* meaning always */
   next = -1;
+  nextLabel = NULL;
   on_timeout = -1;
+  onTimeoutLabel = NULL;
   timewait = false;
 
 /* 3pcc extended mode */
@@ -100,6 +106,7 @@ message::message()
   /* How to match responses to this message. */
   start_txn = 0;
   response_txn = 0;
+  ack_txn = 0;
   recv_response_for_cseq_method_list = NULL;
 }
 
@@ -107,73 +114,45 @@ message::~message()
 {
   if(M_actions != NULL)
     delete(M_actions);
-  M_actions = NULL;
 
   if(send_scheme != NULL)
     delete send_scheme;
-  send_scheme = NULL;
 
   if(recv_request != NULL)
     free (recv_request);
-  recv_request = NULL;
 
   if(regexp_compile != NULL)
     regfree(regexp_compile);
     free(regexp_compile);
-  regexp_compile = NULL;
-
-  if(peer_dest != NULL)
-     free (peer_dest);
-  peer_dest = NULL; 
 
-  if(peer_src != NULL)
-     delete (peer_src);
-  peer_src = NULL;
-
-  if(pause_desc != NULL)
-     free(pause_desc);
-  pause_desc = NULL;
 
   if (pause_distribution) {
     delete pause_distribution;
   }
 
-  if(display_str != NULL)
-     free(display_str);
-  display_str = NULL;
-
   if(M_sendCmdData != NULL)
     delete M_sendCmdData;
-  M_sendCmdData = NULL;
 
+  free(display_str);
+  free(peer_dest);
+  free(peer_src);
+  free(pause_desc);
   free(recv_response_for_cseq_method_list);
 }
 
-void scenario::expand(int length) {
-  assert(length >= this->length);
-  if (length == this->length) {
-    return;
-  }
-
-  messages = (message **)realloc(messages, sizeof(message *) * length);
-  if (!messages) {
-    ERROR("Out of memory allocating scenario messages.");
-  }
-
-  for (int i = this->length; i < length; i++) {
-    messages[i] = NULL;
-  }
-}
-
 /******** Global variables which compose the scenario file **********/
 
 scenario      *main_scenario;
 scenario      *ooc_scenario;
 scenario      *display_scenario;
 
-int           toolMode  = MODE_CLIENT;
-bool	      rtd_stopped[MAX_RTD_INFO_LENGTH];
-bool	      rtd_started[MAX_RTD_INFO_LENGTH];
+/* This mode setting refers to whether we open calls autonomously (MODE_CLIENT)
+ * or in response to requests (MODE_SERVER). */
+int           creationMode  = MODE_CLIENT;
+/* Send mode. Do we send to a fixed address or to the last one we got. */
+int           sendMode  = MODE_CLIENT;
+/* This describes what our 3PCC behavior is. */
+int	      thirdPartyMode = MODE_3PCC_NONE;
 
 /*************** Helper functions for various types *****************/
 long get_long(const char *ptr, const char *what) {
@@ -338,7 +317,7 @@ double xp_get_bool(const char *name, const char *what, bool defval) {
   return xp_get_bool(name, what);
 }
 
-int scenario::get_txn(const char *txnName, const char *what, bool start) {
+int scenario::get_txn(const char *txnName, const char *what, bool start, bool isInvite, bool isAck) {
   /* Check the name's validity. */
   if (txnName[0] == '\0') {
     ERROR("Variable names may not be empty for %s\n", what);
@@ -351,26 +330,42 @@ int scenario::get_txn(const char *txnName, const char *what, bool start) {
   str_int_map::iterator txn_it = txnMap.find(txnName);
   if (txn_it != txnMap.end()) {
     if (start) {
-      txnStarted[txn_it->second]++;
+      /* We need to fill in the invite field. */
+      transactions[txn_it->second - 1].started++;
+    } else if (isAck) {
+      transactions[txn_it->second - 1].acks++;
     } else {
-      txnResponses[txn_it->second]++;
+      transactions[txn_it->second - 1].responses++;
     }
     return txn_it->second;
   }
 
   /* Assign this variable the next slot. */
-  int txnNum = ++maxTxnUsed;
+  struct txnControlInfo transaction;
 
-  txnMap[txnName] = txnNum;
-  txnRevMap[txnNum] = strdup(txnName);
+  transaction.name = strdup(txnName);
   if (start) {
-    txnStarted[txnNum] = 1;
-    txnResponses[txnNum] = 0;
+    transaction.started = 1;
+    transaction.responses = 0;
+    transaction.acks = 0;
+    transaction.isInvite = isInvite;
+  } else if (isAck) {
+    /* Does not start or respond to this txn. */
+    transaction.started = 0;
+    transaction.responses = 0;
+    transaction.acks = 1;
+    transaction.isInvite = false;
   } else {
-    txnStarted[txnNum] = 0;
-    txnResponses[txnNum] = 1;
+    transaction.started = 0;
+    transaction.responses = 1;
+    transaction.acks = 0;
+    transaction.isInvite = false;
   }
 
+  transactions.push_back(transaction);
+  int txnNum = transactions.size();
+  txnMap[txnName] = txnNum;
+
   return txnNum;
 }
 
@@ -392,7 +387,6 @@ int scenario::get_var(const char *varName, const char *what) {
 
 int scenario::xp_get_var(const char *name, const char *what) {
   char *ptr;
-  char *helptext;
 
   if (!(ptr = xp_get_value(name))) {
     ERROR("%s is missing the required '%s' variable parameter.", what, name);
@@ -401,9 +395,29 @@ int scenario::xp_get_var(const char *name, const char *what) {
   return get_var(ptr, what);
 }
 
+int xp_get_optional(const char *name, const char *what) {
+  char *ptr = xp_get_value(name);
+
+  if (!(ptr = xp_get_value(name))) {
+    return OPTIONAL_FALSE;
+  }
+
+  if(!strcmp(ptr, "true")) {
+    return OPTIONAL_TRUE;
+  } else if(!strcmp(ptr, "global")) {
+    return OPTIONAL_GLOBAL;
+  } else if(!strcmp(ptr, "false")) {
+    return OPTIONAL_FALSE;
+  } else {
+    ERROR("Could not understand optional value for %s: %s", what, ptr);
+  }
+
+  return OPTIONAL_FALSE;
+}
+
+
 int scenario::xp_get_var(const char *name, const char *what, int defval) {
   char *ptr;
-  char *helptext;
 
   if (!(ptr = xp_get_value(name))) {
 	return defval;
@@ -495,93 +509,72 @@ char *double_time_string(double ms) {
 
 /* For backwards compatibility, we assign "true" to slot 1, false to 0, and
  * allow other valid integers. */
-int get_rtd(const char *ptr) {
-  char *endptr;
-  int ret;
-
-  if(!strcmp(ptr, (char *)"true"))
-    return 1;
+int scenario::get_rtd(const char *ptr, bool start) {
   if(!strcmp(ptr, (char *)"false"))
     return 0;
 
-  ret = strtol(ptr, &endptr, 0);
-  if (*endptr) {
-    ERROR("rtd \"%s\" is not a valid integer!\n", ptr);
-  }
-
-  if (ret > MAX_RTD_INFO_LENGTH) {
-    ERROR("rtd %d exceeds MAX_RTD_INFO_LENGTH %d!\n", ret, MAX_RTD_INFO_LENGTH);
-  }
+  if(!strcmp(ptr, (char *)"true"))
+    return stats->findRtd("1", start);
 
-  return ret;
+  return stats->findRtd(ptr, start);
 }
 
 /* Get a counter */
-long get_counter(const char *ptr, const char *what) {
-  long ret;
-
-  ret = get_long(ptr, what);
-  if (ret < 1 || ret > MAX_COUNTER) {
-    ERROR("Counter %ld exceeds MAX_COUNTER %d!\n", ret, MAX_COUNTER);
+int scenario::get_counter(const char *ptr, const char *what) {
+  /* Check the name's validity. */
+  if (ptr[0] == '\0') {
+    ERROR("Counter names names may not be empty for %s\n", what);
+  }
+  if (strcspn(ptr, "$,") != strlen(ptr)) {
+    ERROR("Counter names may not contain $ or , for %s\n", what);
   }
 
-  return ret;
+  return stats->findCounter(ptr, true);
 }
 
 
 /* Some validation functions. */
 
-/* If you start an RTD, then you should be interested in collecting statistics for it. */
-void scenario::validate_rtds() {
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    if (rtd_started[i] && !rtd_stopped[i]) {
-      ERROR("You have started Response Time Duration %d, but have never stopped it!", i + 1);
-    }
-  }
-}
-
 void scenario::validate_variable_usage() {
   allocVars->validate();
 }
 
 void scenario::validate_txn_usage() {
-  for (int i = 1; i <= maxTxnUsed; i++) {
-    if(txnStarted[i] == 0) {
-      ERROR("Transaction %s is never started!\n", txnRevMap[i]);
-    } else if(txnResponses[i] == 0) {
-      ERROR("Transaction %s has no responses defined!\n", txnRevMap[i]);
+  for (unsigned int i = 0; i < transactions.size(); i++) {
+    if(transactions[i].started == 0) {
+      ERROR("Transaction %s is never started!\n", transactions[i].name);
+    } else if(transactions[i].responses == 0) {
+      ERROR("Transaction %s has no responses defined!\n", transactions[i].name);
+    }
+    if (transactions[i].isInvite && transactions[i].acks == 0) {
+      ERROR("Transaction %s is an INVITE transaction without an ACK!\n", transactions[i].name);
+    }
+    if (!transactions[i].isInvite && (transactions[i].acks > 0)) {
+      ERROR("Transaction %s is a non-INVITE transaction with an ACK!\n", transactions[i].name);
     }
   }
 }
 
 /* Apply the next and ontimeout labels according to our map. */
-void scenario::apply_labels() {
-  for (int i = 0; i <= length; i++) {
-    int_str_map::iterator it;
-    if ((it = nextLabels.find(i)) != nextLabels.end()) {
-      str_int_map::iterator label_it = labelMap.find(it->second);
-      if (label_it == labelMap.end()) {
-	ERROR("The label '%s' was not defined (index %d, next attribute)\n", it->second, i);
+void scenario::apply_labels(msgvec v, str_int_map labels) {
+  for (unsigned int i = 0; i < v.size(); i++) {
+    if (v[i]->nextLabel) {
+      str_int_map::iterator label_it = labels.find(v[i]->nextLabel);
+      if (label_it == labels.end()) {
+	ERROR("The label '%s' was not defined (index %d, next attribute)\n", v[i]->nextLabel, i);
       }
-      messages[i]->next = label_it->second;
+      v[i]->next = label_it->second;
     }
-    if ((it = ontimeoutLabels.find(i)) != ontimeoutLabels.end()) {
-      str_int_map::iterator label_it = labelMap.find(it->second);
-      if (label_it == labelMap.end()) {
-	ERROR("The label '%s' was not defined (index %d, ontimeout attribute)\n", it->second, i);
+    if (v[i]->onTimeoutLabel) {
+      str_int_map::iterator label_it = labels.find(v[i]->onTimeoutLabel);
+      if (label_it == labels.end()) {
+	ERROR("The label '%s' was not defined (index %d, ontimeout attribute)\n", v[i]->onTimeoutLabel, i);
       }
-      messages[i]->on_timeout = label_it->second;
+      v[i]->on_timeout = label_it->second;
     }
   }
 }
 
-void scenario::init_rtds()
-{
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    rtd_started[i] = rtd_stopped[i] = false;
-  }
-}
-
 int get_cr_number(char *src)
 {
   int res=0;
@@ -610,7 +603,7 @@ char *clean_cdata(char *ptr, int *removed_crlf = NULL) {
        (*ptr == '\t') ||
        (*ptr == '\n'))) {
     if(*ptr == '\n' && removed_crlf) {
-      *removed_crlf++;
+      (*removed_crlf)++;
     }
     *ptr-- = 0;
   }
@@ -622,16 +615,16 @@ char *clean_cdata(char *ptr, int *removed_crlf = NULL) {
   if(ptr == msg) {
     ERROR("Empty cdata in xml scenario file");
   }
-  while(ptr = strstr(msg, "\n ")) {
+  while ((ptr = strstr(msg, "\n "))) {
     memmove(ptr + 1, ptr + 2, strlen(ptr) - 1);
   }
-  while(ptr = strstr(msg, " \n")) {
+  while ((ptr = strstr(msg, " \n"))) {
     memmove(ptr, ptr + 1, strlen(ptr));
   }
-  while(ptr = strstr(msg, "\n\t")) {
+  while ((ptr = strstr(msg, "\n\t"))) {
     memmove(ptr + 1, ptr + 2, strlen(ptr) - 1);
   }
-  while(ptr = strstr(msg, "\t\n")) {
+  while ((ptr = strstr(msg, "\t\n"))) {
     memmove(ptr, ptr + 1, strlen(ptr));
   }
 
@@ -642,16 +635,23 @@ char *clean_cdata(char *ptr, int *removed_crlf = NULL) {
 
 /********************** Scenario File analyser **********************/
 
+void scenario::checkOptionalRecv(char *elem, unsigned int scenario_file_cursor) {
+  if (last_recv_optional) {
+    ERROR("<recv> before <%s> sequence without a mandatory message. Please remove one 'optional=true' (element %d).", elem, scenario_file_cursor);
+  }
+  last_recv_optional = false;
+}
+
 scenario::scenario(char * filename, int deflt)
 {
   char * elem;
   char *method_list = NULL;
   unsigned int scenario_file_cursor = 0;
   int    L_content_length = 0 ;
-  unsigned int recv_count = 0;
-  unsigned int recv_opt_count = 0;
   char * peer; 
 
+  last_recv_optional = false;
+
   if(filename) {
     if(!xp_set_xml_buffer_from_file(filename)) {
       ERROR("Unable to load or parse '%s' xml scenario file", filename);
@@ -665,7 +665,6 @@ scenario::scenario(char * filename, int deflt)
   stats = new CStat();
   allocVars = new AllocVariableTable(userVariables);
 
-  init_rtds();
   hidedefault = false;
 
   elem = xp_open_element(0);
@@ -682,26 +681,25 @@ scenario::scenario(char * filename, int deflt)
     name = strdup("");
   }
 
-  length = 0;
-  messages = NULL;
   duration = 0;
-  maxTxnUsed = 0;
   found_timewait = false;
 
   scenario_file_cursor = 0;
 
-  while(elem = xp_open_element(scenario_file_cursor)) {
+  while ((elem = xp_open_element(scenario_file_cursor))) {
     char * ptr;
     scenario_file_cursor ++;
 
     if(!strcmp(elem, "CallLengthRepartition")) {
-      ptr = xp_get_value((char *)"value");
+      ptr = xp_get_string("value", "CallLengthRepartition");
       stats->setRepartitionCallLength(ptr);
+      free(ptr);
     } else if(!strcmp(elem, "ResponseTimeRepartition")) {
-      ptr = xp_get_value((char *)"value");
+      ptr = xp_get_string("value", "ResponseTimeRepartition");
       stats->setRepartitionResponseTime(ptr);
+      free(ptr);
     } else if(!strcmp(elem, "Global")) {
-      ptr = xp_get_value((char *)"variables");
+      ptr = xp_get_string("variables", "Global");
 
       char **       currentTabVarName = NULL;
       int           currentNbVarNames;
@@ -711,8 +709,9 @@ scenario::scenario(char * filename, int deflt)
 	globalVariables->find(currentTabVarName[i], true);
       }
       freeStringTable(currentTabVarName, currentNbVarNames);
+      free(ptr);
     } else if(!strcmp(elem, "User")) {
-      ptr = xp_get_value((char *)"variables");
+      ptr = xp_get_string("variables", "User");
 
       char **       currentTabVarName = NULL;
       int           currentNbVarNames;
@@ -722,6 +721,22 @@ scenario::scenario(char * filename, int deflt)
 	userVariables->find(currentTabVarName[i], true);
       }
       freeStringTable(currentTabVarName, currentNbVarNames);
+      free(ptr);
+    } else if(!strcmp(elem, "Reference")) {
+      ptr = xp_get_string("variables", "Reference");
+
+      char **       currentTabVarName = NULL;
+      int           currentNbVarNames;
+
+      createStringTable(ptr, &currentTabVarName, &currentNbVarNames);
+      for (int i = 0; i < currentNbVarNames; i++) {
+	int id = allocVars->find(currentTabVarName[i], false);
+	if (id == -1) {
+	  ERROR("Could not reference non-existant variable '%s'", currentTabVarName[i]);
+	}
+      }
+      freeStringTable(currentTabVarName, currentNbVarNames);
+      free(ptr);
     } else if(!strcmp(elem, "DefaultMessage")) {
       char *id = xp_get_string("id", "DefaultMessage");
       if(!(ptr = xp_get_cdata())) {
@@ -732,29 +747,45 @@ scenario::scenario(char * filename, int deflt)
       free(id);
       /* XXX: This should really be per scenario. */
     } else if(!strcmp(elem, "label")) {
-      ptr = xp_get_value((char *)"id");
+      ptr = xp_get_string("id", "label");
       if (labelMap.find(ptr) != labelMap.end()) {
 	ERROR("The label name '%s' is used twice.", ptr);
       }
-      labelMap[ptr] = length;
+      labelMap[ptr] = messages.size();
+      free(ptr);
+    } else if (!strcmp(elem, "init")) {
+      /* We have an init section, which must be full of nops or labels. */
+      int nop_cursor = 0;
+      char *initelem;
+      while ((initelem = xp_open_element(nop_cursor++))) {
+	if (!strcmp(initelem, "nop")) {
+	  /* We should parse this. */
+	  message *nopmsg = new message(initmessages.size(), "scenario initialization");
+	  initmessages.push_back(nopmsg);
+	  nopmsg->M_type = MSG_TYPE_NOP;
+	  getCommonAttributes(nopmsg);
+	} else if (!strcmp(initelem, "label")) {
+	  /* Add an init label. */
+	  ptr = xp_get_value((char *)"id");
+	  if (initLabelMap.find(ptr) != initLabelMap.end()) {
+	    ERROR("The label name '%s' is used twice.", ptr);
+	  }
+	  initLabelMap[ptr] = initmessages.size();
+	} else {
+	  ERROR("Invalid element in an init stanza: '%s'", initelem);
+	}
+	xp_close_element();
+      }
     } else { /** Message Case */
       if (found_timewait) {
 	ERROR("<timewait> can only be the last message in a scenario!\n");
       }
-      expand(length + 1);
-      messages[length] = new message();
+      message *curmsg = new message(messages.size(), name ? name : "unknown scenario");
+      messages.push_back(curmsg);
 
       if(!strcmp(elem, "send")) {
-        if (recv_count) {
-          if (recv_count != recv_opt_count) {
-            recv_count = 0;
-            recv_opt_count = 0;
-          } else {
-            ERROR("<recv> before <send> sequence without a mandatory message. Please remove one 'optional=true' (element %d).", scenario_file_cursor);
-          }
-        }
-
-	messages[length]->M_type = MSG_TYPE_SEND;
+	checkOptionalRecv(elem, scenario_file_cursor);
+	curmsg->M_type = MSG_TYPE_SEND;
         /* Sent messages descriptions */
         if(!(ptr = xp_get_cdata())) {
           ERROR("No CDATA in 'send' section of xml scenario file");
@@ -769,11 +800,11 @@ scenario::scenario(char * filename, int deflt)
 	    // the msg does not contain content-length field
 	    break ;
 	  case  0 :
-	    messages[length] -> content_length_flag =
+	    curmsg -> content_length_flag =
 	      message::ContentLengthValueZero;   // Initialize to No present
 	    break ;
 	  default :
-	    messages[length] -> content_length_flag =
+	    curmsg -> content_length_flag =
 	      message::ContentLengthValueNoZero;   // Initialize to No present
 	    break ;
 	}
@@ -781,15 +812,28 @@ scenario::scenario(char * filename, int deflt)
 	if((msg[strlen(msg) - 1] != '\n') && (removed_clrf)) {
 	  strcat(msg, "\n");
 	}
-	messages[length] -> send_scheme = new SendingMessage(this, msg);
+	char *tsrc = msg;
+	while(*tsrc++);
+	curmsg -> send_scheme = new SendingMessage(this, msg);
 	free(msg);
 
 	// If this is a request we are sending, then store our transaction/method matching information.
-	if (!messages[length]->send_scheme->isResponse()) {
-	  if (ptr = xp_get_value("start_txn")) {
-	    messages[length]->start_txn = get_txn(ptr, "start transaction", true);
+	if (!curmsg->send_scheme->isResponse()) {
+	  char *method = curmsg->send_scheme->getMethod();
+	  bool isInvite = !strcmp(method, "INVITE");
+	  bool isAck = !strcmp(method, "ACK");
+
+	  if ((ptr = xp_get_value("start_txn"))) {
+	    if (isAck) {
+		ERROR("An ACK message can not start a transaction!");
+	    }
+	    curmsg->start_txn = get_txn(ptr, "start transaction", true, isInvite, false);
+	  } else if ((ptr = xp_get_value("ack_txn"))) {
+	    if (!isAck) {
+		ERROR("The ack_txn attribute is valid only for ACK messages!");
+	    }
+	    curmsg->ack_txn = get_txn(ptr, "ack transaction", false, false, true);
 	  } else {
-	    char *method = messages[length]->send_scheme->getMethod();
 	    int len = method_list ? strlen(method_list) : 0;
 	    method_list = (char *)realloc(method_list, len + strlen(method) + 1);
 	    if (!method_list) {
@@ -798,75 +842,66 @@ scenario::scenario(char * filename, int deflt)
 	    strcpy(method_list + len, method);
 	  }
 	} else {
-	  if (ptr = xp_get_value("start_txn")) {
+	  if ((ptr = xp_get_value("start_txn"))) {
 	    ERROR("Responses can not start a transaction");
 	  }
+	  if ((ptr = xp_get_value("ack_txn"))) {
+	    ERROR("Responses can not ACK a transaction");
+	  }
 	}
 
-	if (ptr = xp_get_value("response_txn")) {
+	if ((ptr = xp_get_value("response_txn"))) {
 	  ERROR("response_txn can only be used for recieved messages.");
 	}
 
-	messages[length] -> retrans_delay = xp_get_long("retrans", "retransmission timer", 0);
-	messages[length] -> timeout = xp_get_long("timeout", "message send timeout", 0);
+	curmsg -> retrans_delay = xp_get_long("retrans", "retransmission timer", 0);
+	curmsg -> timeout = xp_get_long("timeout", "message send timeout", 0);
       } else if(!strcmp(elem, (char *)"recv")) {
-        recv_count++;
-        messages[length]->M_type = MSG_TYPE_RECV;
+        curmsg->M_type = MSG_TYPE_RECV;
         /* Received messages descriptions */
-        if(ptr = xp_get_value((char *)"response")) {
-          messages[length] ->recv_response = get_long(ptr, "response code");
+        if((ptr = xp_get_value((char *)"response"))) {
+          curmsg ->recv_response = get_long(ptr, "response code");
 	  if (method_list) {
-	    messages[length]->recv_response_for_cseq_method_list = strdup(method_list);
+	    curmsg->recv_response_for_cseq_method_list = strdup(method_list);
 	  }
-	  if (ptr = xp_get_value("response_txn")) {
-	    messages[length]->response_txn = get_txn(ptr, "transaction response", false);
+	  if ((ptr = xp_get_value("response_txn"))) {
+	    curmsg->response_txn = get_txn(ptr, "transaction response", false, false, false);
 	  }
         }
 
-        if(ptr = xp_get_value((char *)"request")) {
-          messages[length] -> recv_request = strdup(ptr);
-	  if (ptr = xp_get_value("response_txn")) {
+        if((ptr = xp_get_value((char *)"request"))) {
+          curmsg -> recv_request = strdup(ptr);
+	  if ((ptr = xp_get_value("response_txn"))) {
 	    ERROR("response_txn can only be used for recieved responses.");
 	  }
         }
 
-        if (0 != (ptr = xp_get_value((char *)"optional"))) {
-          if(!strcmp(ptr, "true")) {
-            messages[length] -> optional = OPTIONAL_TRUE;
-            ++recv_opt_count;
-          } else if(!strcmp(ptr, "global")) {
-            messages[length] -> optional = OPTIONAL_GLOBAL;
-            ++recv_opt_count;
-          } else if(!strcmp(ptr, "false")) {
-            messages[length] -> optional = OPTIONAL_FALSE;
-          } else {
-	    ERROR("Could not understand optional value: %s", ptr);
-	  }
-        }
-	messages[length]->advance_state = xp_get_bool("advance_state", "recv", true);
-	if (!messages[length]->advance_state && messages[length]->optional == OPTIONAL_FALSE) {
-	  ERROR("advance_state is allowed only for optional messages (index = %d)\n", length);
+	curmsg->optional = xp_get_optional("optional", "recv");
+	last_recv_optional = curmsg->optional;
+	curmsg->advance_state = xp_get_bool("advance_state", "recv", true);
+	if (!curmsg->advance_state && curmsg->optional == OPTIONAL_FALSE) {
+	  ERROR("advance_state is allowed only for optional messages (index = %d)\n", messages.size() - 1);
 	}
 
         if (0 != (ptr = xp_get_value((char *)"regexp_match"))) {
           if(!strcmp(ptr, "true")) {
-            messages[length] -> regexp_match = 1;
+            curmsg -> regexp_match = 1;
           }
         }
 
-	messages[length]->timeout = xp_get_long("timeout", "message timeout", 0);
+	curmsg->timeout = xp_get_long("timeout", "message timeout", 0);
 
         /* record the route set  */
         /* TODO disallow optional and rrs to coexist? */
-        if(ptr = xp_get_value((char *)"rrs")) {
-	  messages[length] -> bShouldRecordRoutes = get_bool(ptr, "record route set");
+        if((ptr = xp_get_value((char *)"rrs"))) {
+	  curmsg -> bShouldRecordRoutes = get_bool(ptr, "record route set");
         }
 
         /* record the authentication credentials  */
-        if(ptr = xp_get_value((char *)"auth")) {
+        if((ptr = xp_get_value((char *)"auth"))) {
 	  bool temp = get_bool(ptr, "message authentication");
 #ifdef _USE_OPENSSL
-	  messages[length] -> bShouldAuthenticate = temp;
+	  curmsg -> bShouldAuthenticate = temp;
 #else
 	  if (temp) {
 	    ERROR("Authentication requires OpenSSL support!");
@@ -874,23 +909,16 @@ scenario::scenario(char * filename, int deflt)
 #endif
         }
       } else if(!strcmp(elem, "pause") || !strcmp(elem, "timewait")) {
-        if (recv_count) {
-          if (recv_count != recv_opt_count) {
-            recv_count = 0;
-            recv_opt_count = 0;
-          } else {
-            ERROR("<recv> before <send> sequence without a mandatory message. Please remove one 'optional=true' (element %d).", scenario_file_cursor);
-          }
-        }
-        messages[length]->M_type = MSG_TYPE_PAUSE;
+	checkOptionalRecv(elem, scenario_file_cursor);
+        curmsg->M_type = MSG_TYPE_PAUSE;
 	if (!strcmp(elem, "timewait")) {
-	  messages[length]->timewait = true;
+	  curmsg->timewait = true;
 	  found_timewait = true;
 	}
 
 	int var;
 	if ((var = xp_get_var("variable", "pause", -1)) != -1) {
-	  messages[length]->pause_variable = var;
+	  curmsg->pause_variable = var;
 	} else {
 	  CSample *distribution = parse_distribution(true);
 
@@ -907,40 +935,37 @@ scenario::scenario(char * filename, int deflt)
 	    ERROR("The distribution %s has a 99th percentile of %s, which is larger than INT_MAX.  You should chose different parameters.", desc, percentile);
 	  }
 
-	  messages[length]->pause_distribution = distribution;
+	  curmsg->pause_distribution = distribution;
 	  /* Update scenario duration with max duration */
 	  duration += (int)pause_duration;
 	}
       }
       else if(!strcmp(elem, "nop")) {
+	checkOptionalRecv(elem, scenario_file_cursor);
 	/* Does nothing at SIP level.  This message type can be used to handle
 	 * actions, increment counters, or for RTDs. */
-	messages[length]->M_type = MSG_TYPE_NOP;
+	curmsg->M_type = MSG_TYPE_NOP;
       }
       else if(!strcmp(elem, "recvCmd")) {
-        recv_count++;
-        messages[length]->M_type = MSG_TYPE_RECVCMD;
+        curmsg->M_type = MSG_TYPE_RECVCMD;
+	curmsg->optional = xp_get_optional("optional", "recv");
+	last_recv_optional = curmsg->optional;
 
 	/* 3pcc extended mode */
-        if(ptr = xp_get_value((char *)"src")) {
-           messages[length] ->peer_src = strdup(ptr);
-        }
+        if((ptr = xp_get_value((char *)"src"))) {
+           curmsg ->peer_src = strdup(ptr);
+        } else if (extendedTwinSippMode) {
+	  ERROR("You must specify a 'src' for recvCmd when using extended 3pcc mode!");
+	}
       } else if(!strcmp(elem, "sendCmd")) {
-        if (recv_count) {
-          if (recv_count != recv_opt_count) {
-            recv_count = 0;
-            recv_opt_count = 0;
-          } else {
-            ERROR("<recv> before <send> sequence without a mandatory message. Please remove one 'optional=true' (element %d).", scenario_file_cursor);
-          }
-        }
-        messages[length]->M_type = MSG_TYPE_SENDCMD;
+	checkOptionalRecv(elem, scenario_file_cursor);
+        curmsg->M_type = MSG_TYPE_SENDCMD;
         /* Sent messages descriptions */
 
 	/* 3pcc extended mode  */
-	if(ptr = xp_get_value((char *)"dest")) { 
+	if((ptr = xp_get_value((char *)"dest"))) { 
 	   peer = strdup(ptr) ;
-	   messages[length] ->peer_dest = peer ;
+	   curmsg ->peer_dest = peer ;
            peer_map::iterator peer_it;
 	   peer_it = peers.find(peer_map::key_type(peer));
 	   if(peer_it == peers.end())  /* the peer (slave or master)
@@ -952,22 +977,23 @@ scenario::scenario(char * filename, int deflt)
 	     strcpy(infos.peer_host, get_peer_addr(peer));
              peers[std::string(peer)] = infos; 
 	   }
+	} else if (extendedTwinSippMode) {
+	  ERROR("You must specify a 'dest' for sendCmd with extended 3pcc mode!");
 	}
 
         if(!(ptr = xp_get_cdata())) {
-          ERROR("No CDATA in 'send' section of xml scenario file");
+          ERROR("No CDATA in 'sendCmd' section of xml scenario file");
         }
 	char *msg = clean_cdata(ptr);
 
-	messages[length] -> M_sendCmdData = new SendingMessage(this, msg, true /* skip sanity */);
+	curmsg -> M_sendCmdData = new SendingMessage(this, msg, true /* skip sanity */);
 	free(msg);
       }
       else {
         ERROR("Unknown element '%s' in xml scenario file", elem);
       }
 
-      getCommonAttributes();
-      length++;
+      getCommonAttributes(curmsg);
     } /** end * Message case */
     xp_close_element();
   } // end while
@@ -984,13 +1010,11 @@ scenario::scenario(char * filename, int deflt)
   pausedaddr = find_var("_unexp.pausedaddr", "unexpected paused until");
 
   /* Patch up the labels. */
-  apply_labels();
+  apply_labels(messages, labelMap);
+  apply_labels(initmessages, initLabelMap);
 
   /* Some post-scenario loading validation. */
-  validate_rtds();
-  if (length == 0) {
-    ERROR("Did not find any messages inside of scenario!");
-  }
+  stats->validateRtds();
 
   /* Make sure that all variables are used more than once. */
   validate_variable_usage();
@@ -998,6 +1022,17 @@ scenario::scenario(char * filename, int deflt)
   /* Make sure that all started transactions have responses, and vice versa. */
   validate_txn_usage();
 
+  if (messages.size() == 0) {
+    ERROR("Did not find any messages inside of scenario!");
+  }
+}
+
+void scenario::runInit() {
+  call *initcall;
+  if (initmessages.size() > 0) {
+    initcall = new call(main_scenario, NULL, NULL, "///main-init", 0, false, false, true);
+    initcall->run();
+  }
 }
 
 void clear_int_str(int_str_map m) {
@@ -1020,25 +1055,24 @@ void clear_int_int(int_int_map m) {
 }
 
 scenario::~scenario() {
-  for (int i = 0; i < length; i++) {
-    delete messages[i];
+  for (msgvec::iterator i = messages.begin(); i != messages.end(); i++) {
+    delete *i;
   }
-  free(messages);
+  messages.clear();
 
   free(name);
 
   allocVars->putTable();
   delete stats;
 
-  clear_int_str(txnRevMap);
-  clear_int_str(nextLabels);
-  clear_int_str(ontimeoutLabels);
+  for (unsigned int i = 0; i < transactions.size(); i++) {
+    free(transactions[i].name);
+  }
+  transactions.clear();
 
   clear_str_int(labelMap);
+  clear_str_int(initLabelMap);
   clear_str_int(txnMap);
-
-  clear_int_int(txnStarted);
-  clear_int_int(txnResponses);
 }
 
 CSample *parse_distribution(bool oldstyle = false) {
@@ -1050,23 +1084,23 @@ CSample *parse_distribution(bool oldstyle = false) {
     if (!oldstyle) {
       ERROR("statistically distributed actions or pauses requires 'distribution' parameter");
     }
-    if (ptr = xp_get_value("normal")) {
+    if ((ptr = xp_get_value("normal"))) {
 	distname = "normal";
-    } else if (ptr = xp_get_value("exponential")) {
+    } else if ((ptr = xp_get_value("exponential"))) {
 	distname = "exponential";
-    } else if (ptr = xp_get_value("lognormal")) {
+    } else if ((ptr = xp_get_value("lognormal"))) {
 	distname = "lognormal";
-    } else if (ptr = xp_get_value("weibull")) {
+    } else if ((ptr = xp_get_value("weibull"))) {
 	distname = "weibull";
-    } else if (ptr = xp_get_value("pareto")) {
+    } else if ((ptr = xp_get_value("pareto"))) {
       distname = "pareto";
-    } else if (ptr = xp_get_value("gamma")) {
+    } else if ((ptr = xp_get_value("gamma"))) {
       distname = "gamma";
-    } else if (ptr = xp_get_value("min")) {
+    } else if ((ptr = xp_get_value("min"))) {
 	distname = "uniform";
-    } else if (ptr = xp_get_value("max")) {
+    } else if ((ptr = xp_get_value("max"))) {
 	distname = "uniform";
-    } else if (ptr = xp_get_value("milliseconds")) {
+    } else if ((ptr = xp_get_value("milliseconds"))) {
 	double val = get_double(ptr, "Pause milliseconds");
 	return new CFixed(val);
     } else {
@@ -1150,9 +1184,9 @@ void parse_slave_cfg()
   if(f){
      while (fgets(line, MAX_PEER_SIZE, f) != NULL)
      {
-       if(temp_peer = strtok(line, ";")){
-         if(peer_host = (char *) malloc(MAX_PEER_SIZE)){
-           if(temp_host  = strtok(NULL, ";")){
+       if((temp_peer = strtok(line, ";"))) {
+         if((peer_host = (char *) malloc(MAX_PEER_SIZE))){
+           if((temp_host  = strtok(NULL, ";"))){
               strcpy(peer_host, temp_host);
               peer_addrs[std::string(temp_peer)] = peer_host;
              }
@@ -1173,88 +1207,101 @@ void scenario::computeSippMode()
 {
   bool isRecvCmdFound = false;
   bool isSendCmdFound = false;
-  bool isFirstMessageFound = true;
 
-  toolMode = -1;
-  for(int i=0; i<length; i++)
-    { 
+  creationMode = -1;
+  sendMode = -1;
+  thirdPartyMode = MODE_3PCC_NONE;
+
+  assert(messages.size() > 0);
+
+  for(unsigned int i=0; i<messages.size(); i++)
+    {
       switch(messages[i]->M_type)
         {
         case MSG_TYPE_PAUSE:
         case MSG_TYPE_NOP:
 	  /* Allow pauses or nops to go first. */
 	  continue;
-        case MSG_TYPE_SEND: 
-          if(isFirstMessageFound)
-            toolMode  = MODE_CLIENT;
-          isFirstMessageFound = false;
+        case MSG_TYPE_SEND:
+	  if (sendMode == -1) {
+	    sendMode = MODE_CLIENT;
+	  }
+	  if (creationMode == -1) {
+	    creationMode = MODE_CLIENT;
+	  }
           break;
 
         case MSG_TYPE_RECV:
-          if(isFirstMessageFound)
-            toolMode  = MODE_SERVER;
-          isFirstMessageFound = false;
+	  if (sendMode == -1) {
+	    sendMode = MODE_SERVER;
+	  }
+	  if (creationMode == -1) {
+	    creationMode = MODE_SERVER;
+	  }
           break;
         case MSG_TYPE_SENDCMD:
           isSendCmdFound = true;
+	  if (creationMode == -1) {
+	    creationMode = MODE_CLIENT;
+	  }
           if(!isRecvCmdFound) {
-            if (false == isFirstMessageFound && toolMode == MODE_SERVER) {
+            if (creationMode == MODE_SERVER) {
               /*
                * If it is a server already, then start it in
                * 3PCC A passive mode
                */
 	       if(twinSippMode){
-              toolMode = MODE_3PCC_A_PASSIVE;
+		 thirdPartyMode = MODE_3PCC_A_PASSIVE;
 	       }else if (extendedTwinSippMode){
-		  toolMode = MODE_MASTER_PASSIVE;
+		 thirdPartyMode = MODE_MASTER_PASSIVE;
                }
             } else {
 	        if(twinSippMode){
-              toolMode = MODE_3PCC_CONTROLLER_A;
-                 }else if (extendedTwinSippMode){
-                   toolMode = MODE_MASTER;
-                 } 
+		  thirdPartyMode = MODE_3PCC_CONTROLLER_A;
+		}else if (extendedTwinSippMode){
+		  thirdPartyMode = MODE_MASTER;
+		}
             }
-            if((toolMode == MODE_MASTER_PASSIVE || toolMode == MODE_MASTER) && !master_name){
+            if((thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_MASTER) && !master_name){
               ERROR("Inconsistency between command line and scenario: master scenario but -master option not set\n");
             }
             if(!twinSippMode && !extendedTwinSippMode)
               ERROR("sendCmd message found in scenario but no twin sipp"
                     " address has been passed! Use -3pcc option or 3pcc extended mode.\n");
-            return;
           }
-          isFirstMessageFound = false;
           break;
 
         case MSG_TYPE_RECVCMD:
+	  if (creationMode == -1) {
+	    creationMode = MODE_SERVER;
+	  }
           isRecvCmdFound = true;
           if(!isSendCmdFound)
             {
               if(twinSippMode){
-              toolMode  = MODE_3PCC_CONTROLLER_B;
+		thirdPartyMode = MODE_3PCC_CONTROLLER_B;
               } else if(extendedTwinSippMode){
-	         toolMode = MODE_SLAVE;
+		thirdPartyMode = MODE_SLAVE;
                  if(!slave_number) {
-                    ERROR("Inconsistency between command line and scenario: slave scenario but -slave option not set\n");
-                   }else{
-                    toolMode = MODE_SLAVE;
-                   } 
+		   ERROR("Inconsistency between command line and scenario: slave scenario but -slave option not set\n");
+		 }else{
+		   thirdPartyMode = MODE_SLAVE;
+		 }
               }
               if(!twinSippMode && !extendedTwinSippMode)
-                ERROR("sendCmd message found in scenario but no "
+                ERROR("recvCmd message found in scenario but no "
                       "twin sipp address has been passed! Use "
                       "-3pcc option\n");
-              return;
             }
-          isFirstMessageFound = false;
           break;
         default:
           break;
         }
     }
-    if(toolMode == -1)
-      ERROR("Unable to determine mode of the tool (server, "
-            "client, 3PCC controller A, 3PCC controller B).\n");
+    if(creationMode == -1)
+      ERROR("Unable to determine creation mode of the tool (server, client)\n");
+    if(sendMode == -1)
+      ERROR("Unable to determine send mode of the tool (server, client)\n");
 }
 
 void scenario::handle_rhs(CAction *tmpAction, char *what) {
@@ -1278,39 +1325,21 @@ void scenario::handle_arithmetic(CAction *tmpAction, char *what) {
   handle_rhs(tmpAction, what);
 }
 
-// Action list for the message indexed by message_index in 
-// the scenario
-void scenario::getActionForThisMessage()
-{
-  unsigned int recvScenarioLen = 0;
+void scenario::parseAction(CActions *actions) {
   char *        actionElem;
+  unsigned int recvScenarioLen = 0;
   char *        currentRegExp = NULL;
   char *        buffer = NULL;
   char **       currentTabVarName = NULL;
   int           currentNbVarNames;
   char * ptr;
   int           sub_currentNbVarId;
-  
-  if(!(actionElem = xp_open_element(0))) {
-    return;
-  }
-  if(strcmp(actionElem, "action")) {
-    return;
-  }
-
-  /* We actually have an action element. */
-  if(messages[length]->M_actions != NULL) {
-    ERROR("Duplicate action for scenario index %d", length);
-  }
-  messages[length]->M_actions = new CActions();
 
-  while(actionElem = xp_open_element(recvScenarioLen)) {
+  while((actionElem = xp_open_element(recvScenarioLen))) {
     CAction *tmpAction = new CAction(this);
 
     if(!strcmp(actionElem, "ereg")) {
-      if(!(ptr = xp_get_value((char *)"regexp"))) {
-	ERROR("'ereg' action without 'regexp' argument (mandatory)");
-      }
+      ptr = xp_get_string("regexp", "ereg");
 
       // keeping regexp expression in memory
       if(currentRegExp != NULL)
@@ -1325,24 +1354,22 @@ void scenario::getActionForThisMessage()
 
       // warning - although these are detected for both msg and hdr
       // they are only implemented for search_in="hdr"
-      if ((ptr = xp_get_value((char *)"case_indep"))) {
-	tmpAction->setCaseIndep(get_bool(ptr, "case_indep"));
-      } else {
-	tmpAction->setCaseIndep(false);
-      }
-
-      if ((ptr = xp_get_value((char *)"start_line"))) {
-	tmpAction->setHeadersOnly(get_bool(ptr, "start_line"));
-      } else {
-	tmpAction->setHeadersOnly(false);
-      }
+      tmpAction->setCaseIndep(xp_get_bool("case_indep", "ereg", false));
+      tmpAction->setHeadersOnly(xp_get_bool("start_line", "ereg", false));
 
+      free(ptr);
       if ( 0 != ( ptr = xp_get_value((char *)"search_in") ) ) {
 	tmpAction->setOccurence(1);
 
 	if ( 0 == strcmp(ptr, (char *)"msg") ) {
 	  tmpAction->setLookingPlace(CAction::E_LP_MSG);
 	  tmpAction->setLookingChar (NULL);
+	} else if ( 0 == strcmp(ptr, (char *)"body") ) {
+	  tmpAction->setLookingPlace(CAction::E_LP_BODY);
+	  tmpAction->setLookingChar (NULL);
+	} else if (!strcmp(ptr, (char *)"var")) {
+	  tmpAction->setVarInId(xp_get_var("variable", "ereg"));
+	  tmpAction->setLookingPlace(CAction::E_LP_VAR);
 	} else if (!strcmp(ptr, (char *)"hdr")) {
 	  ptr = xp_get_value((char *)"header");
 	  if (!ptr || !strlen(ptr)) {
@@ -1353,6 +1380,9 @@ void scenario::getActionForThisMessage()
 	  if (0 != (ptr = xp_get_value((char *)"occurence"))) {
 	    tmpAction->setOccurence (atol(ptr));
 	  }
+	  if (0 != (ptr = xp_get_value((char *)"occurrence"))) {
+	    tmpAction->setOccurence (atol(ptr));
+	  }
 	} else {
 	  ERROR("Unknown search_in value %s", ptr);
 	}
@@ -1361,11 +1391,15 @@ void scenario::getActionForThisMessage()
 	tmpAction->setLookingChar(NULL);
       } // end if-else search_in
 
-      if(ptr = xp_get_value((char *)"check_it")) {
-	tmpAction->setCheckIt(get_bool(ptr, "check_it"));
+      if (xp_get_value("check_it")) {
+      tmpAction->setCheckIt(xp_get_bool("check_it", "ereg", false));
+          if (xp_get_value("check_it_inverse")) {
+              ERROR("Can not have both check_it and check_it_inverse for ereg!");
+          }
       } else {
-	tmpAction->setCheckIt(false);
+          tmpAction->setCheckItInverse(xp_get_bool("check_it_inverse", "ereg", false));
       }
+
       if (!(ptr = xp_get_value((char *) "assign_to"))) {
 	ERROR("assign_to value is missing");
       }
@@ -1393,30 +1427,21 @@ void scenario::getActionForThisMessage()
       }
       currentRegExp = NULL;
     } /* end !strcmp(actionElem, "ereg") */ else if(!strcmp(actionElem, "log")) {
-      if(ptr = xp_get_value((char *)"message")) {
-	tmpAction->setMessage(ptr);
-      } else {
-	ERROR("Log message without a message!");
-      }
+      tmpAction->setMessage(xp_get_string("message", "log"));
       tmpAction->setActionType(CAction::E_AT_LOG_TO_FILE);
     } else if(!strcmp(actionElem, "warning")) {
-      if(ptr = xp_get_value((char *)"message")) {
-	tmpAction->setMessage(ptr);
-      } else {
-	ERROR("Warning message without a message!");
-      }
+      tmpAction->setMessage(xp_get_string("message", "warning"));
       tmpAction->setActionType(CAction::E_AT_LOG_WARNING);
+    } else if(!strcmp(actionElem, "error")) {
+      tmpAction->setMessage(xp_get_string("message", "error"));
+      tmpAction->setActionType(CAction::E_AT_LOG_ERROR);
     } else if(!strcmp(actionElem, "assign")) {
       tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_VALUE);
       handle_arithmetic(tmpAction, "assign");
     } else if(!strcmp(actionElem, "assignstr")) {
       tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_STRING);
       tmpAction->setVarId(xp_get_var("assign_to", "assignstr"));
-      if(ptr = xp_get_value((char *)"value")) {
-	tmpAction->setMessage(ptr);
-      } else {
-	ERROR("assignstr action without a value!");
-      }
+      tmpAction->setMessage(xp_get_string("value", "assignstr"));
     } else if(!strcmp(actionElem, "gettimeofday")) {
       tmpAction->setActionType(CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY);
 
@@ -1472,11 +1497,16 @@ void scenario::getActionForThisMessage()
     } else if(!strcmp(actionElem, "test")) {
       tmpAction->setVarId(xp_get_var("assign_to", "test"));
       tmpAction->setVarInId(xp_get_var("variable", "test"));
-      tmpAction->setDoubleValue(xp_get_double("value", "test"));
-      tmpAction->setActionType(CAction::E_AT_VAR_TEST);
-      if (!(ptr = xp_get_value("compare"))) {
-	ERROR("test actions require a 'compare' parameter");
+      if (xp_get_value("value")) {
+	tmpAction->setDoubleValue(xp_get_double("value", "test"));
+	if (xp_get_value("variable2")) {
+	  ERROR("Can not have both a value and a variable2 for test!");
+	}
+      } else {
+	tmpAction->setVarIn2Id(xp_get_var("variable2", "test"));
       }
+      tmpAction->setActionType(CAction::E_AT_VAR_TEST);
+      ptr = xp_get_string("compare", "test");
       if (!strcmp(ptr, "equal")) {
 	tmpAction->setComparator(CAction::E_C_EQ);
       } else if (!strcmp(ptr, "not_equal")) {
@@ -1492,6 +1522,7 @@ void scenario::getActionForThisMessage()
       } else {
 	ERROR("Invalid 'compare' parameter: %s", ptr);
       }
+      free(ptr);
     } else if(!strcmp(actionElem, "verifyauth")) {
 #ifdef _USE_OPENSSL
       tmpAction->setVarId(xp_get_var("assign_to", "verifyauth"));
@@ -1506,19 +1537,42 @@ void scenario::getActionForThisMessage()
       tmpAction->setMessage(xp_get_string("file", "lookup"), 0);
       tmpAction->setMessage(xp_get_string("key", "lookup"), 1);
       tmpAction->setActionType(CAction::E_AT_LOOKUP);
+    } else if(!strcmp(actionElem, "insert")) {
+      tmpAction->setMessage(xp_get_string("file", "insert"), 0);
+      tmpAction->setMessage(xp_get_string("value", "insert"), 1);
+      tmpAction->setActionType(CAction::E_AT_INSERT);
+    } else if(!strcmp(actionElem, "replace")) {
+      tmpAction->setMessage(xp_get_string("file", "replace"), 0);
+      tmpAction->setMessage(xp_get_string("line", "replace"), 1);
+      tmpAction->setMessage(xp_get_string("value", "replace"), 2);
+      tmpAction->setActionType(CAction::E_AT_REPLACE);
+    } else if(!strcmp(actionElem, "setdest")) {
+      tmpAction->setMessage(xp_get_string("host", actionElem), 0);
+      tmpAction->setMessage(xp_get_string("port", actionElem), 1);
+      tmpAction->setMessage(xp_get_string("protocol", actionElem), 2);
+      tmpAction->setActionType(CAction::E_AT_SET_DEST);
+    } else if(!strcmp(actionElem, "closecon")) {
+      tmpAction->setActionType(CAction::E_AT_CLOSE_CON);
     } else if(!strcmp(actionElem, "strcmp")) {
       tmpAction->setVarId(xp_get_var("assign_to", "strcmp"));
-      tmpAction->setVarInId(xp_get_var("variable", "test"));
-      tmpAction->setStringValue(xp_get_string("value", "test"));
+      tmpAction->setVarInId(xp_get_var("variable", "strcmp"));
+      if (xp_get_value("value")) {
+	tmpAction->setStringValue(xp_get_string("value", "strcmp"));
+	if (xp_get_value("variable2")) {
+	  ERROR("Can not have both a value and a variable2 for strcmp!");
+	}
+      } else {
+	tmpAction->setVarIn2Id(xp_get_var("variable2", "strcmp"));
+      }
       tmpAction->setActionType(CAction::E_AT_VAR_STRCMP);
     } else if(!strcmp(actionElem, "trim")) {
       tmpAction->setVarId(xp_get_var("assign_to", "trim"));
       tmpAction->setActionType(CAction::E_AT_VAR_TRIM);
     } else if(!strcmp(actionElem, "exec")) {
-      if(ptr = xp_get_value((char *)"command")) {
+      if((ptr = xp_get_value((char *)"command"))) {
 	tmpAction->setActionType(CAction::E_AT_EXECUTE_CMD);
-	tmpAction->setCmdLine(ptr);
-      } /* end (ptr = xp_get_value("command")  */ else if(ptr = xp_get_value((char *)"int_cmd")) {
+	tmpAction->setMessage(ptr);
+      } /* end (ptr = xp_get_value("command")  */ else if((ptr = xp_get_value((char *)"int_cmd"))) {
 	CAction::T_IntCmdType type(CAction::E_INTCMD_STOPCALL); /* assume the default */
 
 	if (!strcmp(ptr, "stop_now")) {
@@ -1534,18 +1588,18 @@ void scenario::getActionForThisMessage()
 	tmpAction->setActionType(CAction::E_AT_EXEC_INTCMD);
 	tmpAction->setIntCmd(type);
 #ifdef PCAPPLAY
-      } else if (ptr = xp_get_value((char *) "play_pcap_audio")) {
+      } else if ((ptr = xp_get_value((char *) "play_pcap_audio"))) {
 	tmpAction->setPcapArgs(ptr);
 	tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_AUDIO);
 	hasMedia = 1;
-      } else if (ptr = xp_get_value((char *) "play_pcap_video")) {
+      } else if ((ptr = xp_get_value((char *) "play_pcap_video"))) {
 	tmpAction->setPcapArgs(ptr);
 	tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_VIDEO);
 	hasMedia = 1;
 #else
-      } else if (ptr = xp_get_value((char *) "play_pcap_audio")) {
+      } else if ((ptr = xp_get_value((char *) "play_pcap_audio"))) {
 	ERROR("play_pcap_audio requires pcap support! Please recompile SIPp");
-      } else if (ptr = xp_get_value((char *) "play_pcap_video")) {
+      } else if ((ptr = xp_get_value((char *) "play_pcap_video"))) {
 	ERROR("play_pcap_video requires pcap support! Please recompile SIPp");
 #endif
       } else {
@@ -1557,78 +1611,101 @@ void scenario::getActionForThisMessage()
 
     /* If the action was not well-formed, there should have already been an
      * ERROR declaration, thus it is safe to add it here at the end of the loop. */
-    messages[length]->M_actions->setAction(tmpAction);
+    actions->setAction(tmpAction);
 
     xp_close_element();
     recvScenarioLen++;
   } // end while
+}
+
+// Action list for the message indexed by message_index in
+// the scenario
+void scenario::getActionForThisMessage(message *message)
+{
+  char *        actionElem;
+
+  if(!(actionElem = xp_open_element(0))) {
+    return;
+  }
+  if(strcmp(actionElem, "action")) {
+    return;
+  }
+
+  /* We actually have an action element. */
+  if(message->M_actions != NULL) {
+    ERROR("Duplicate action for %s index %d", message->desc, message->index);
+  }
+  message->M_actions = new CActions();
+
+  parseAction(message->M_actions);
   xp_close_element();
 }
 
-void scenario::getBookKeeping() {
+void scenario::getBookKeeping(message *message) {
   char *ptr;
 
-  if(ptr = xp_get_value((char *)"rtd")) {
-    messages[length] -> stop_rtd = get_rtd(ptr);
-    rtd_stopped[messages[length]->stop_rtd - 1] = true;
+  if((ptr = xp_get_value((char *)"rtd"))) {
+    message -> stop_rtd = get_rtd(ptr, false);
   }
-  if (ptr = xp_get_value((char *)"repeat_rtd")) {
-    if (messages[length] -> stop_rtd) {
-      messages[length] -> repeat_rtd = get_bool(ptr, "repeat_rtd");
+  if ((ptr = xp_get_value((char *)"repeat_rtd"))) {
+    if (message -> stop_rtd) {
+      message-> repeat_rtd = get_bool(ptr, "repeat_rtd");
     } else {
       ERROR("There is a repeat_rtd element without an rtd element");
     }
   }
 
-  if(ptr = xp_get_value((char *)"start_rtd")) {
-    messages[length] -> start_rtd = get_rtd(ptr);
-    rtd_started[messages[length]->start_rtd - 1] = true;
+  if((ptr = xp_get_value((char *)"start_rtd"))) {
+    message -> start_rtd = get_rtd(ptr, true);
   }
 
-  if(ptr = xp_get_value((char *)"counter")) {
-    messages[length] -> counter = get_counter(ptr, "counter");
+  if((ptr = xp_get_value((char *)"counter"))) {
+    message -> counter = get_counter(ptr, "counter");
   }
 }
 
-void scenario::getCommonAttributes() {
+void scenario::getCommonAttributes(message *message) {
   char *ptr;
 
-  getBookKeeping();
-  getActionForThisMessage();
+  getBookKeeping(message);
+  getActionForThisMessage(message);
 
-  if(ptr = xp_get_value((char *)"lost")) {
-    messages[length] -> lost = get_double(ptr, "lost percentage");
+  if((ptr = xp_get_value((char *)"lost"))) {
+    message -> lost = get_double(ptr, "lost percentage");
     lose_packets = 1;
   }
 
-  if(ptr = xp_get_value((char *)"crlf")) {
-    messages[length] -> crlf = 1;
+  if((ptr = xp_get_value((char *)"crlf"))) {
+    message -> crlf = 1;
   }
 
   if (xp_get_value("hiderest")) {
     hidedefault = xp_get_bool("hiderest", "hiderest");
   }
-  messages[length] -> hide = xp_get_bool("hide", "hide", hidedefault);
-  if(ptr = xp_get_value((char *)"display")) {
-    messages[length] -> display_str = strdup(ptr);
+  message -> hide = xp_get_bool("hide", "hide", hidedefault);
+  if((ptr = xp_get_value((char *)"display"))) {
+    message -> display_str = strdup(ptr);
   }
 
+  message -> condexec = xp_get_var("condexec", "condexec variable", -1);
+  message -> condexec_inverse = xp_get_bool("condexec_inverse", "condexec_inverse", false);
+
   if ((ptr = xp_get_value((char *)"next"))) {
     if (found_timewait) {
       ERROR("next labels are not allowed in <timewait> elements.");
     }
-    nextLabels[length] = strdup(ptr);
-    messages[length] -> test = xp_get_var("test", "test variable", -1);
+    message -> nextLabel = strdup(ptr);
+    message -> test = xp_get_var("test", "test variable", -1);
     if ( 0 != ( ptr = xp_get_value((char *)"chance") ) ) {
       float chance = get_double(ptr,"chance");
       /* probability of branch to next */
       if (( chance < 0.0 ) || (chance > 1.0 )) {
 	ERROR("Chance %s not in range [0..1]", ptr);
       }
-      messages[length] -> chance = (int)((1.0-chance)*RAND_MAX);
+      message -> chance = (int)((1.0-chance)*RAND_MAX);
     }
     else {
-      messages[length] -> chance = 0; /* always */
+      message -> chance = 0; /* always */
     }
   }
 
@@ -1636,7 +1713,7 @@ void scenario::getCommonAttributes() {
     if (found_timewait) {
       ERROR("ontimeout labels are not allowed in <timewait> elements.");
     }
-    ontimeoutLabels[length] = ptr;
+    message -> onTimeoutLabel = strdup(ptr);
   }
 }
 
diff --git a/scenario.hpp b/scenario.hpp
index ec8119f..64861ef 100644
--- a/scenario.hpp
+++ b/scenario.hpp
@@ -43,6 +43,7 @@
 #define MODE_CLIENT        0
 #define MODE_SERVER        1
 
+#define MODE_3PCC_NONE		0
 #define MODE_3PCC_CONTROLLER_A  2
 #define MODE_3PCC_CONTROLLER_B  3   
 #define MODE_3PCC_A_PASSIVE     4
@@ -108,9 +109,13 @@ public:
   bool           hide;
   char *	 display_str;
   int		 next;
+  char *         nextLabel;
   int            test;
+  int            condexec;
+  bool           condexec_inverse;
   int            chance;/* 0=always, RAND_MAX+1=never (test rand() >= chance) */
   int		 on_timeout;
+  char *         onTimeoutLabel;
 
   /* Statistics */
   unsigned long   nb_sent;
@@ -139,31 +144,48 @@ public:
 
   char           *recv_response_for_cseq_method_list;
   int            start_txn;
+  int            ack_txn;
   int            response_txn;
+  int            index;
+  const char *         desc;
 
-  message();
+  message(int index, const char *desc);
   ~message();
 };
 
+typedef std::vector<message *> msgvec;
+
+struct txnControlInfo {
+  char *name;
+  bool isInvite;
+  int acks;
+  int started;
+  int responses;
+};
+typedef std::vector<txnControlInfo> txnvec;
+
+
 class scenario {
 public:
   scenario(char * filename, int deflt);
   ~scenario();
 
-  message **messages;
-  int length;
+  void runInit();
+
+  msgvec messages;
+  msgvec initmessages;
   char *name;
   int duration;
-  int maxTxnUsed;
-  int_str_map txnRevMap;
+  txnvec transactions;
   int unexpected_jump;
   int retaddr;
   int pausedaddr;
 
   void computeSippMode();
 
-  bool rtd_stopped[MAX_RTD_INFO_LENGTH];
   int get_var(const char *varName, const char *what);
+  int get_counter(const char *varName, const char *what);
+  int get_rtd(const char *ptr, bool start);
   int find_var(const char *varName, const char *what);
 
   CStat *stats;
@@ -173,44 +195,39 @@ private:
 
   /* The mapping of labels to IDs. */
   str_int_map labelMap;
-  /* The string label representations. */
-  int_str_map nextLabels;
-  int_str_map ontimeoutLabels;
+  str_int_map initLabelMap;
 
   str_int_map txnMap;
-  int_int_map txnStarted;
-  int_int_map txnResponses;
-
 
   bool found_timewait;
-  bool rtd_started[MAX_RTD_INFO_LENGTH];
 
-  void getBookKeeping();
-  void getCommonAttributes();
-  void getActionForThisMessage();
+  void getBookKeeping(message *message);
+  void getCommonAttributes(message *message);
+  void getActionForThisMessage(message *message);
+  void parseAction(CActions *actions);
   void handle_arithmetic(CAction *tmpAction, char *what);
   void handle_rhs(CAction *tmpAction, char *what);
+  void checkOptionalRecv(char *elem, unsigned int scenario_file_cursor);
 
-  void apply_labels();
-  void init_rtds();
-  void validate_rtds();
+  void apply_labels(msgvec v, str_int_map labels);
   void validate_variable_usage();
   void validate_txn_usage();
 
-  int get_txn(const char *txnName, const char *what, bool start);
+  int get_txn(const char *txnName, const char *what, bool start, bool isInvite, bool isAck);
   int xp_get_var(const char *name, const char *what);
   int xp_get_var(const char *name, const char *what, int defval);
 
-  void expand(int length);
-
   bool hidedefault;
+  bool last_recv_optional;
 };
 
 /* There are external variable containing the current scenario */
 extern scenario      *main_scenario;
 extern scenario      *ooc_scenario;
 extern scenario      *display_scenario;
-extern int           toolMode;
+extern int           creationMode;
+extern int           sendMode;
+extern int           thirdPartyMode;
 
 extern message::ContentLengthFlag  content_length_flag;
 
diff --git a/screen.cpp b/screen.cpp
index f665442..5bac606 100644
--- a/screen.cpp
+++ b/screen.cpp
@@ -168,9 +168,9 @@ void manage_oversized_file()
           "Max file size reached - no more logs\n",
            CStat::formatTime(&currentTime));
   fflush(f);
-  stop_all_traces(); 
+  stop_all_traces();
   print_all_responses = 0;
-  screen_errorf = 0; 
+  error_lfi.fptr = NULL;
 }
 
 
@@ -216,10 +216,11 @@ void screen_init(void (*exit_handler)())
 static void _screen_error(int fatal, bool use_errno, int error, const char *fmt, va_list ap)
 {
   static unsigned long long count = 0;
-  FILE * output;
   char * c = screen_last_error;
   struct timeval currentTime;
 
+  CStat::globalStat(fatal ? CStat::E_FATAL_ERRORS : CStat::E_WARNING);
+
   GET_TIME (&currentTime);
   
   c+= sprintf(c, "%s: ", CStat::formatTime(&currentTime));
@@ -230,33 +231,33 @@ static void _screen_error(int fatal, bool use_errno, int error, const char *fmt,
   c+= sprintf(c, ".\n");
   screen_errors++;
 
-  if(screen_inited && !screen_errorf && print_all_responses) {
+  if(screen_inited && !error_lfi.fptr && print_all_responses) {
     rotate_errorf();
-    if(!screen_errorf) {
+    if(!error_lfi.fptr) {
       c += sprintf(c, "%s: Unable to create '%s': %s.\n",
                    screen_exename, screen_logfile, strerror(errno));
       screen_exit(EXIT_FATAL_ERROR);
     } else {
-      fprintf(screen_errorf, "%s: The following events occured:\n",
+      fprintf(error_lfi.fptr, "%s: The following events occured:\n",
               screen_exename);
-      fflush(screen_errorf);
+      fflush(error_lfi.fptr);
     }
   }
 
-  if(screen_errorf) {
-    count += fprintf(screen_errorf, "%s", screen_last_error);
-    fflush(screen_errorf);
+  if(error_lfi.fptr) {
+    count += fprintf(error_lfi.fptr, "%s", screen_last_error);
+    fflush(error_lfi.fptr);
     if (ringbuffer_size && count > ringbuffer_size) {
       rotate_errorf();
       count = 0;
     }
     if (max_log_size && count > max_log_size) {
       print_all_responses = 0;
-      if (screen_errorf) {
-	fflush(screen_errorf);
-	fclose(screen_errorf);
-	screen_errorf = NULL;
-	errorf_overwrite = false;
+      if (error_lfi.fptr) {
+	fflush(error_lfi.fptr);
+	fclose(error_lfi.fptr);
+	error_lfi.fptr = NULL;
+	error_lfi.overwrite = false;
       }
     }
   } else if (fatal) {
@@ -266,36 +267,45 @@ static void _screen_error(int fatal, bool use_errno, int error, const char *fmt,
 
   if(fatal) {
     if(!screen_inited) {
+      if(error == EADDRINUSE) {
+        exit(EXIT_BIND_ERROR);
+      } else {
       exit(EXIT_FATAL_ERROR);
+      }
+    } else {
+      if(error == EADDRINUSE) {
+        screen_exit(EXIT_BIND_ERROR);
     } else {
       screen_exit(EXIT_FATAL_ERROR);
     }
   }
+  }
 }
 
 extern "C" {
-int ERROR(const char *fmt, ...) {
+void ERROR(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   _screen_error(true, false, 0, fmt, ap);
   va_end(ap);
+  assert(0);
 }
 
-int ERROR_NO(const char *fmt, ...) {
+void ERROR_NO(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   _screen_error(true, true, errno, fmt, ap);
   va_end(ap);
 }
 
-int WARNING(const char *fmt, ...) {
+void WARNING(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   _screen_error(false, false, 0, fmt, ap);
   va_end(ap);
 }
 
-int WARNING_NO(const char *fmt, ...) {
+void WARNING_NO(const char *fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   _screen_error(false, true, errno, fmt, ap);
diff --git a/screen.hpp b/screen.hpp
index b606cdf..4b0da99 100644
--- a/screen.hpp
+++ b/screen.hpp
@@ -31,10 +31,10 @@
 #ifdef __cplusplus
 extern "C" {
 #endif
-  int ERROR(const char *fmt, ...);
-  int WARNING(const char *fmt, ...);
-  int ERROR_NO(const char *fmt, ...);
-  int WARNING_NO(const char *fmt, ...);
+  void ERROR(const char *fmt, ...) __attribute__ ((noreturn));
+  void WARNING(const char *fmt, ...);
+  void ERROR_NO(const char *fmt, ...);
+  void WARNING_NO(const char *fmt, ...);
 #ifdef __cplusplus
 }
 #endif
@@ -45,6 +45,7 @@ extern "C" {
 #define EXIT_TEST_RES_UNKNOWN      98
 #define EXIT_OTHER                 99
 #define EXIT_FATAL_ERROR           -1
+#define EXIT_BIND_ERROR            -2
 
 void screen_set_exename(char * exe_name);
 void screen_init(void (*exit_handler)());
diff --git a/send_packets.c b/send_packets.c
index 0c2f7e5..22357d2 100644
--- a/send_packets.c
+++ b/send_packets.c
@@ -54,6 +54,7 @@
 #include <errno.h>
 #include <string.h>
 #include <fcntl.h>
+#include <pthread.h>
 
 #include "send_packets.h"
 #include "prepare_pcap.h"
@@ -112,6 +113,14 @@ void hexdump(char *p, int s) {
 /*Safe threaded version*/
 void do_sleep (struct timeval *, struct timeval *,
                struct timeval *, struct timeval *);
+void send_packets_cleanup(void *arg)
+{
+  int sock = (int) arg;
+
+  // Close send socket
+  close(sock);
+}
+
 
 int
 send_packets (play_args_t * play_args)
@@ -171,6 +180,13 @@ send_packets (play_args_t * play_args)
     memcpy(&(from6.sin6_addr.s6_addr), &(((struct sockaddr_in6 *)(void *) from)->sin6_addr.s6_addr), sizeof(from6.sin6_addr.s6_addr));
   }
 	
+
+  /* Ensure the sender socket is closed when the thread exits - this
+   * allows the thread to be cancelled cleanly.
+   */
+  pthread_cleanup_push(send_packets_cleanup, ((void *) sock));
+
+
   while (pkt_index < pkt_max) {
     memcpy(udp, pkt_index->data, pkt_index->pktlen);
     port_diff = ntohs (udp->uh_dport) - pkts->base;
@@ -228,7 +244,8 @@ send_packets (play_args_t * play_args)
     pkt_index++;
 	}
 
-  close(sock);
+  /* Closing the socket is handled by pthread_cleanup_push()/pthread_cleanup_pop() */
+  pthread_cleanup_pop(1);
   return 0;
 }
 
diff --git a/sipp.cpp b/sipp.cpp
index b4a57e2..f65c3e6 100644
--- a/sipp.cpp
+++ b/sipp.cpp
@@ -20,6 +20,7 @@
  *           David MANSUTTI
  *           Francois-Xavier Kowalski
  *           Gerard Lyonnaz
+ *           Francois Draperi (for dynamic_id)
  *           From Hewlett Packard Company.
  *           F. Tarek Rogers
  *           Peter Higginson
@@ -29,13 +30,24 @@
  *           Clement Chen
  *           Wolfgang Beck
  *           Charles P Wright from IBM Research
+ *           Martin Van Leeuwen
+ *           Andy Aicken
+ *	     Michael Hirschbichler
  */
 
 #define GLOBALS_FULL_DEFINITION
 
+#include <dlfcn.h>
 #include "sipp.hpp"
 #include "assert.h"
 
+void sipp_usleep(unsigned long usec);
+
+void rotate_messagef();
+void rotate_calldebugf();
+void rotate_shortmessagef();
+void rotate_logfile();
+
 #ifdef _USE_OPENSSL
 SSL_CTX  *sip_trp_ssl_ctx = NULL; /* For SSL cserver context */
 SSL_CTX  *sip_trp_ssl_ctx_client = NULL; /* For SSL cserver context */
@@ -56,7 +68,6 @@ int passwd_call_back_routine(char  *buf , int size , int flag, void *passwd)
 }
 #endif
 
-unsigned long calls_since_last_rate_change = 0;
 bool do_hide = true;
 bool show_index = false;
 
@@ -114,6 +125,11 @@ struct sipp_option {
 #define SIPP_OPTION_DEFAULTS      31
 #define SIPP_OPTION_OOC_SCENARIO  32
 #define SIPP_OPTION_INDEX_FILE    33
+#define SIPP_OPTION_VAR		  34
+#define SIPP_OPTION_RTCHECK	  35
+#define SIPP_OPTION_LFNAME	  36
+#define SIPP_OPTION_LFOVERWRITE	  37
+#define SIPP_OPTION_PLUGIN	  38
 
 /* Put Each option, its help text, and type in this table. */
 struct sipp_option options_table[] = {
@@ -135,6 +151,9 @@ struct sipp_option options_table[] = {
 	{"bind_local", "Bind socket to local IP address, i.e. the local IP address is used as the source IP address.  If SIPp runs in server mode it will only listen on the local IP address instead of all IP addresses.", SIPP_OPTION_SETFLAG, &bind_local, 1},
 	{"buff_size", "Set the send and receive buffer size.", SIPP_OPTION_INT, &buff_size, 1},
 
+	{"calldebug_file", "Set the name of the call debug file.", SIPP_OPTION_LFNAME, &calldebug_lfi, 1},
+	{"calldebug_overwrite", "Overwrite the call debug file (default true).", SIPP_OPTION_LFOVERWRITE, &calldebug_lfi, 1},
+
 	{"cid_str", "Call ID string (default %u-%p@%s).  %u=call_number, %s=ip_address, %p=process_number, %%=% (in any order).", SIPP_OPTION_STRING, &call_id_string, 1},
 	{"ci", "Set the local control IP address", SIPP_OPTION_IP, control_ip, 1},
 	{"cp", "Set the local control port number. Default is 8888.", SIPP_OPTION_INT, &control_port, 1},
@@ -150,6 +169,9 @@ struct sipp_option options_table[] = {
 		"If a behavior is prefaced with a -, then it is turned off.  Example: all,-bye\n",
 		SIPP_OPTION_DEFAULTS, &default_behaviors, 1},
 
+	{"error_file", "Set the name of the error log file.", SIPP_OPTION_LFNAME, &error_lfi, 1},
+	{"error_overwrite", "Overwrite the error log file (default true).", SIPP_OPTION_LFOVERWRITE, &error_lfi, 1},
+
 	{"f", "Set the statistics report frequency on screen. Default is 1 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq, 1},
 	{"fd", "Set the statistics dump log report frequency. Default is 60 and default unit is seconds.", SIPP_OPTION_TIME_SEC, &report_freq_dumpLog, 1},
 
@@ -167,9 +189,13 @@ struct sipp_option options_table[] = {
 	{"l", "Set the maximum number of simultaneous calls. Once this limit is reached, traffic is decreased until the number of open calls goes down. Default:\n"
 	      "  (3 * call_duration (s) * rate).", SIPP_OPTION_LIMIT, NULL, 1},
 
+	{"log_file", "Set the name of the log actions log file.", SIPP_OPTION_LFNAME, &log_lfi, 1},
+	{"log_overwrite", "Overwrite the log actions log file (default true).", SIPP_OPTION_LFOVERWRITE, &log_lfi, 1},
+
 	{"lost", "Set the number of packets to lose by default (scenario specifications override this value).", SIPP_OPTION_FLOAT, &global_lost, 1},
+	{"rtcheck", "Select the retransmisison detection method: full (default) or loose.", SIPP_OPTION_RTCHECK, &rtcheck, 1},
 	{"m", "Stop the test and exit when 'calls' calls are processed", SIPP_OPTION_LONG, &stop_after, 1},
-	{"mi", "Set the local media IP address", SIPP_OPTION_IP, media_ip, 1},
+	{"mi", "Set the local media IP address (default: local primary host IP address)", SIPP_OPTION_IP, media_ip, 1},
         {"master","3pcc extended mode: indicates the master number", SIPP_OPTION_3PCC_EXTENDED, &master_name, 1},
 	{"max_recv_loops", "Set the maximum number of messages received read per cycle. Increase this value for high traffic level.  The default value is 1000.", SIPP_OPTION_INT, &max_recv_loops, 1},
 	{"max_sched_loops", "Set the maximum number of calsl run per event loop. Increase this value for high traffic level.  The default value is 1000.", SIPP_OPTION_INT, &max_sched_loops, 1},
@@ -181,6 +207,8 @@ struct sipp_option options_table[] = {
 	{"max_socket", "Set the max number of sockets to open simultaneously. This option is significant if you use one socket per call. Once this limit is reached, traffic is distributed over the sockets already opened. Default value is 50000", SIPP_OPTION_MAX_SOCKET, NULL, 1},
 
 	{"mb", "Set the RTP echo buffer size (default: 2048).", SIPP_OPTION_INT, &media_bufsize, 1},
+	{"message_file", "Set the name of the message log file.", SIPP_OPTION_LFNAME, &message_lfi, 1},
+	{"message_overwrite", "Overwrite the message log file (default true).", SIPP_OPTION_LFOVERWRITE, &message_lfi, 1},
 	{"mp", "Set the local RTP echo port number. Default is 6000.", SIPP_OPTION_INT, &user_media_port, 1},
 
 	{"nd", "No Default. Disable all default behavior of SIPp which are the following:\n"
@@ -198,6 +226,7 @@ struct sipp_option options_table[] = {
 	{"p", "Set the local port number.  Default is a random free port chosen by the system.", SIPP_OPTION_INT, &user_port, 1},
 	{"pause_msg_ign", "Ignore the messages received during a pause defined in the scenario ", SIPP_OPTION_SETFLAG, &pause_msg_ign, 1},
 	{"periodic_rtd", "Reset response time partition counters each logging interval.", SIPP_OPTION_SETFLAG, &periodic_rtd, 1},
+	{"plugin", "Load a plugin.", SIPP_OPTION_PLUGIN, NULL, 1},
 
 	{"r", "Set the call rate (in calls per seconds).  This value can be"
 	      "changed during test by pressing '+','_','*' or '/'. Default is 10.\n"
@@ -218,6 +247,7 @@ struct sipp_option options_table[] = {
 	{"no_rate_quit", "If -rate_increase is set, do not quit after the rate reaches -rate_max.", SIPP_OPTION_UNSETFLAG, &rate_quit, 1},
 	{"recv_timeout", "Global receive timeout. Default unit is milliseconds. If the expected message is not received, the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_recv_timeout, 1},
 	{"send_timeout", "Global send timeout. Default unit is milliseconds. If a message is not sent (due to congestion), the call times out and is aborted.", SIPP_OPTION_TIME_MS_LONG, &defl_send_timeout, 1},
+	{"sleep", "How long to sleep for at startup. Default unit is seconds.", SIPP_OPTION_TIME_SEC, &sleeptime, 1},
 	{"reconnect_close", "Should calls be closed on reconnect?", SIPP_OPTION_BOOL, &reset_close, 1},
 	{"reconnect_sleep", "How long (in milliseconds) to sleep between the close and reconnect?", SIPP_OPTION_TIME_MS, &reset_sleep, 1},
 	{"ringbuffer_files", "How many error/message files should be kept after rotation?", SIPP_OPTION_INT, &ringbuffer_files, 1},
@@ -231,6 +261,8 @@ struct sipp_option options_table[] = {
 	{"s", "Set the username part of the resquest URI. Default is 'service'.", SIPP_OPTION_STRING, &service, 1},
 	{"sd", "Dumps a default scenario (embeded in the sipp executable)", SIPP_OPTION_SCENARIO, NULL, 0},
 	{"sf", "Loads an alternate xml scenario file.  To learn more about XML scenario syntax, use the -sd option to dump embedded scenarios. They contain all the necessary help.", SIPP_OPTION_SCENARIO, NULL, 2},
+	{"shortmessage_file", "Set the name of the short message log file.", SIPP_OPTION_LFNAME, &shortmessage_lfi, 1},
+	{"shortmessage_overwrite", "Overwrite the short message log file (default true).", SIPP_OPTION_LFOVERWRITE, &shortmessage_lfi, 1},
 	{"oocsf", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2},
 	{"oocsn", "Load out-of-call scenario.", SIPP_OPTION_OOC_SCENARIO, NULL, 2},
 	{"skip_rlimit", "Do not perform rlimit tuning of file descriptor limits.  Default: false.", SIPP_OPTION_SETFLAG, &skip_rlimit, 1},
@@ -265,6 +297,7 @@ struct sipp_option options_table[] = {
 	      , SIPP_OPTION_TRANSPORT, NULL, 1},
 
 	{"timeout", "Global timeout. Default unit is seconds.  If this option is set, SIPp quits after nb units (-timeout 20s quits after 20 seconds).", SIPP_OPTION_TIME_SEC, &global_timeout, 1},
+	{"timeout_error", "SIPp fails if the global timeout is reached is set (-timeout option required).", SIPP_OPTION_SETFLAG, &timeout_error, 1},
 	{"timer_resol", "Set the timer resolution. Default unit is milliseconds.  This option has an impact on timers precision."
                       "Small values allow more precise scheduling but impacts CPU usage."
                       "If the compression is on, the value is set to 50ms. The default value is 10ms.", SIPP_OPTION_TIME_MS, &timer_resolution, 1},
@@ -276,6 +309,7 @@ struct sipp_option options_table[] = {
 	{"trace_screen", "Dump statistic screens in the <scenario_name>_<pid>_screens.log file when quitting SIPp. Useful to get a final status report in background mode (-bg option).", SIPP_OPTION_SETFLAG, &useScreenf, 1},
 	{"trace_err", "Trace all unexpected messages in <scenario file name>_<pid>_errors.log.", SIPP_OPTION_SETFLAG, &print_all_responses, 1},
 //	{"trace_timeout", "Displays call ids for calls with timeouts in <scenario file name>_<pid>_timeout.log", SIPP_OPTION_SETFLAG, &useTimeoutf, 1},
+	{"trace_calldebug", "Dumps debugging information about aborted calls to <scenario_name>_<pid>_calldebug.log file.", SIPP_OPTION_SETFLAG, &useCallDebugf, 1},
 	{"trace_stat", "Dumps all statistics in <scenario_name>_<pid>.csv file. Use the '-h stat' option for a detailed description of the statistics file content.", SIPP_OPTION_SETFLAG, &dumpInFile, 1},
 	{"trace_counts", "Dumps individual message counts in a CSV file.", SIPP_OPTION_SETFLAG, &useCountf, 1},
 	{"trace_rtt", "Allow tracing of all response times in <scenario file name>_<pid>_rtt.csv.", SIPP_OPTION_SETFLAG, &dumpInRtt, 1},
@@ -283,6 +317,13 @@ struct sipp_option options_table[] = {
 
 	{"users", "Instead of starting calls at a fixed rate, begin 'users' calls at startup, and keep the number of calls constant.", SIPP_OPTION_USERS, NULL, 1},
 
+	{"watchdog_interval", "Set gap between watchdog timer firings.  Default is 400.", SIPP_OPTION_TIME_MS, &watchdog_interval, 1},
+	{"watchdog_reset", "If the watchdog timer has not fired in more than this time period, then reset the max triggers counters.  Default is 10 minutes.", SIPP_OPTION_TIME_MS, &watchdog_reset, 1},
+	{"watchdog_minor_threshold", "If it has been longer than this period between watchdog executions count a minor trip.  Default is 500.", SIPP_OPTION_TIME_MS, &watchdog_minor_threshold, 1},
+	{"watchdog_major_threshold", "If it has been longer than this period between watchdog executions count a major trip.  Default is 3000.", SIPP_OPTION_TIME_MS, &watchdog_major_threshold, 1},
+	{"watchdog_major_maxtriggers", "How many times the major watchdog timer can be tripped before the test is terminated.  Default is 10.", SIPP_OPTION_INT, &watchdog_major_maxtriggers, 1},
+	{"watchdog_minor_maxtriggers", "How many times the minor watchdog timer can be tripped before the test is terminated.  Default is 120.", SIPP_OPTION_INT, &watchdog_minor_maxtriggers, 1},
+
 #ifdef _USE_OPENSSL
 	{"ap", "Set the password for authentication challenges. Default is 'password", SIPP_OPTION_STRING, &auth_password, 1},
 	{"tls_cert", "Set the name for TLS Certificate file. Default is 'cacert.pem", SIPP_OPTION_STRING, &tls_cert_name, 1},
@@ -303,6 +344,10 @@ struct sipp_option options_table[] = {
                    "A circuit must be available for the call to be placed.\n"
                    "Format: -tdmmap {0-3}{99}{5-8}{1-31}", SIPP_OPTION_TDMMAP, NULL, 1},
 	{"key", "keyword value\nSet the generic parameter named \"keyword\" to \"value\".", SIPP_OPTION_KEY, NULL, 1},
+	{"set", "variable value\nSet the global variable parameter named \"variable\" to \"value\".", SIPP_OPTION_VAR, NULL, 3},
+	{"dynamicStart", "variable value\nSet the start offset of dynamic_id varaiable",  SIPP_OPTION_INT, &startDynamicId, 1},
+	{"dynamicMax",   "variable value\nSet the maximum of dynamic_id variable     ",   SIPP_OPTION_INT, &maxDynamicId,   1},
+	{"dynamicStep",  "variable value\nSet the increment of dynamic_id variable",      SIPP_OPTION_INT, &stepDynamicId,  1}
 };
 
 struct sipp_option *find_option(const char *option) {
@@ -340,6 +385,8 @@ unsigned long long getmicroseconds()
   if (!VI_micro_base) VI_micro_base = VI_micro - 1;
   VI_micro = VI_micro - VI_micro_base;
 
+  clock_tick = VI_micro / 1000LL;
+
   return VI_micro;
 }
 
@@ -678,7 +725,6 @@ int get_decimal_from_hex(char hex) {
 /******************** Recv Poll Processing *********************/
 
 int                  pollnfds;
-unsigned int	     call_sockets;
 struct pollfd        pollfiles[SIPP_MAXFDS];
 struct sipp_socket  *sockets[SIPP_MAXFDS];
 
@@ -706,7 +752,6 @@ bool sipMsgCheck (const char *P_msg, int P_msgSize, struct sipp_socket *socket)
 
 void print_stats_in_file(FILE * f, int last)
 {
-  int index;
   static char temp_str[256];
   int divisor;
 
@@ -731,25 +776,26 @@ void print_stats_in_file(FILE * f, int last)
     sprintf(temp_str, "%3.1f(%d ms)/%5.3fs", rate, duration, (double)rate_period_ms / 1000.0);
   }
   unsigned long long total_calls = display_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + display_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated);
-  if( toolMode == MODE_SERVER) {
+  if( creationMode == MODE_SERVER) {
     fprintf
       (f,
        "  Port   Total-time  Total-calls  Transport" 
        SIPP_ENDL
-       "  %-5d %6d.%02d s     %8llu  %s"
+       "  %-5d %6lu.%02lu s     %8llu  %s"
        SIPP_ENDL SIPP_ENDL,
        local_port,
        clock_tick / 1000, (clock_tick % 1000) / 10,
        total_calls,
        TRANSPORT_TO_STRING(transport));
   } else {
+    assert(creationMode == MODE_CLIENT);
     if (users >= 0) {
       fprintf(f, "     Users (length)");
     } else {
       fprintf(f, "  Call-rate(length)");
     }
     fprintf(f, "   Port   Total-time  Total-calls  Remote-host" SIPP_ENDL
-       "%19s   %-5d %6d.%02d s     %8llu  %s:%d(%s)" SIPP_ENDL SIPP_ENDL,
+       "%19s   %-5d %6lu.%02lu s     %8llu  %s:%d(%s)" SIPP_ENDL SIPP_ENDL,
        temp_str,
        local_port,
        clock_tick / 1000, (clock_tick % 1000) / 10,
@@ -773,13 +819,13 @@ void print_stats_in_file(FILE * f, int last)
             ((clock_tick-last_report_time) % 1000));
   }
   divisor = scheduling_loops; if(!divisor) { divisor = 1; }
-  fprintf(f,"  %-38s %d ms scheduler resolution" 
+  fprintf(f,"  %-38s %lu ms scheduler resolution" 
          SIPP_ENDL,
          temp_str,
          (clock_tick-last_report_time) / divisor);
 
   /* 2nd line */
-  if( toolMode == MODE_SERVER) { 
+  if( creationMode == MODE_SERVER) {
     sprintf(temp_str, "%llu calls", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall));
   } else {
     sprintf(temp_str, "%llu calls (limit %d)", display_scenario->stats->GetStat(CStat::CPT_C_CurrentCall), open_calls_allowed);
@@ -796,7 +842,7 @@ void print_stats_in_file(FILE * f, int last)
   sprintf(temp_str,"%llu dead call msg (discarded)",
       display_scenario->stats->GetStat(CStat::CPT_G_C_DeadCallMsgs));
   fprintf(f,"  %-37s", temp_str);
-  if( toolMode != MODE_SERVER) { 
+  if( creationMode == MODE_CLIENT) {
     sprintf(temp_str,"%llu out-of-call msg (discarded)",
             display_scenario->stats->GetStat(CStat::CPT_G_C_OutOfCallMsgs));
     fprintf(f,"  %-37s", temp_str);
@@ -814,7 +860,7 @@ void print_stats_in_file(FILE * f, int last)
           pollnfds);
   fprintf(f,"  %-38s", temp_str);
   if(nb_net_recv_errors || nb_net_send_errors || nb_net_cong) {
-    fprintf(f,"  %d/%d/%d %s errors (send/recv/cong)" SIPP_ENDL,
+    fprintf(f,"  %lu/%lu/%lu %s errors (send/recv/cong)" SIPP_ENDL,
            nb_net_send_errors, 
            nb_net_recv_errors,
            nb_net_cong,
@@ -829,7 +875,7 @@ void print_stats_in_file(FILE * f, int last)
     sprintf(temp_str, "%lu Total RTP pckts sent ",
             rtp_pckts_pcap);
     if (clock_tick-last_report_time) {
-       fprintf(f,"  %-38s %d.%03d last period RTP rate (kB/s)" SIPP_ENDL,
+       fprintf(f,"  %-38s %lu.%03lu last period RTP rate (kB/s)" SIPP_ENDL,
               temp_str,
               (rtp_bytes_pcap)/(clock_tick-last_report_time),
               (rtp_bytes_pcap)%(clock_tick-last_report_time));
@@ -846,7 +892,7 @@ void print_stats_in_file(FILE * f, int last)
 
     // AComment: Fix for random coredump when using RTP echo
     if (clock_tick-last_report_time) {
-       fprintf(f,"  %-38s %d.%03d last period RTP rate (kB/s)" SIPP_ENDL,
+       fprintf(f,"  %-38s %lu.%03lu last period RTP rate (kB/s)" SIPP_ENDL,
               temp_str,
               (rtp_bytes)/(clock_tick-last_report_time),
               (rtp_bytes)%(clock_tick-last_report_time));
@@ -857,7 +903,7 @@ void print_stats_in_file(FILE * f, int last)
 
     // AComment: Fix for random coredump when using RTP echo
     if (clock_tick-last_report_time) {
-      fprintf(f,"  %-38s %d.%03d last period RTP rate (kB/s)" SIPP_ENDL,
+      fprintf(f,"  %-38s %lu.%03lu last period RTP rate (kB/s)" SIPP_ENDL,
 	      temp_str,
 	      (rtp2_bytes)/(clock_tick-last_report_time),
 	      (rtp2_bytes)%(clock_tick-last_report_time));
@@ -877,8 +923,8 @@ void print_stats_in_file(FILE * f, int last)
            "Messages  Retrans   Timeout   Unexp.    Lost" 
            SIPP_ENDL);
   }
-  for(index = 0;
-      index < display_scenario->length;
+  for(unsigned long index = 0;
+      index < display_scenario->messages.size();
       index ++) {
     message *curmsg = display_scenario->messages[index];
 
@@ -886,7 +932,7 @@ void print_stats_in_file(FILE * f, int last)
       continue;
     }
     if (show_index) {
-	fprintf(f, "%-02d:", index);
+	fprintf(f, "%-2lu:", index);
     }
     
     if(SendingMessage *src = curmsg -> send_scheme) {
@@ -896,7 +942,7 @@ void print_stats_in_file(FILE * f, int last)
 	sprintf(temp_str, "%s", src->getMethod());
       }
 
-      if(toolMode == MODE_SERVER) {
+      if(creationMode == MODE_SERVER) {
         fprintf(f,"  <---------- %-10s ", temp_str);
       } else {
         fprintf(f,"  %10s ----------> ", temp_str);
@@ -910,20 +956,20 @@ void print_stats_in_file(FILE * f, int last)
       }
 
       if(curmsg -> retrans_delay) {
-        fprintf(f,"%-9d %-9d %-9d %-9s" ,
+        fprintf(f,"%-9lu %-9lu %-9lu %-9s" ,
                curmsg -> nb_sent,
                curmsg -> nb_sent_retrans,
                curmsg -> nb_timeout,
                "" /* Unexpected */);
       } else {
-        fprintf(f,"%-9d %-9d %-9s %-9s" ,
+        fprintf(f,"%-9lu %-9lu %-9s %-9s" ,
                curmsg -> nb_sent,
                curmsg -> nb_sent_retrans,
                "", /* Timeout. */
                "" /* Unexpected. */);
       }
     } else if(curmsg -> recv_response) {
-      if(toolMode == MODE_SERVER) {
+      if(creationMode == MODE_SERVER) {
 	fprintf(f,"  ----------> %-10d ", curmsg -> recv_response);
       } else { 
 	fprintf(f,"  %10d <---------- ", curmsg -> recv_response);
@@ -966,16 +1012,16 @@ void print_stats_in_file(FILE * f, int last)
       }
       int len = strlen(desc) < 9 ? 9 : strlen(desc);
 
-      if(toolMode == MODE_SERVER) {
+      if(creationMode == MODE_SERVER) {
 	fprintf(f,"  [%9s] Pause%*s", desc, 23 - len > 0 ? 23 - len : 0, "");
       } else {
 	fprintf(f,"       Pause [%9s]%*s", desc, 18 - len > 0 ? 18 - len : 0, "");
       }
 
       fprintf(f,"%-9d", curmsg->sessions);
-      fprintf(f,"                     %-9d" , curmsg->nb_unexp);
+      fprintf(f,"                     %-9lu" , curmsg->nb_unexp);
     } else if(curmsg -> recv_request) {
-      if(toolMode == MODE_SERVER) {
+      if(creationMode == MODE_SERVER) {
 	fprintf(f,"  ----------> %-10s ", curmsg -> recv_request);
       } else {
 	fprintf(f,"  %10s <---------- ", curmsg -> recv_request);
@@ -1018,7 +1064,7 @@ void print_stats_in_file(FILE * f, int last)
       }
     } else if(curmsg -> M_type == MSG_TYPE_SENDCMD) {
       fprintf(f,"        [ Sent Command ]         ");
-      fprintf(f,"%-9d %-9s           %-9s" ,
+      fprintf(f,"%-9lu %-9s           %-9s" ,
              curmsg -> M_nbCmdSent,
              "",
              "");
@@ -1028,7 +1074,7 @@ void print_stats_in_file(FILE * f, int last)
     }
     
     if(lose_packets && (curmsg -> nb_lost)) {
-      fprintf(f," %-9d" SIPP_ENDL,
+      fprintf(f," %-9lu" SIPP_ENDL,
              curmsg -> nb_lost);
     } else {
       fprintf(f,SIPP_ENDL);
@@ -1043,18 +1089,22 @@ void print_stats_in_file(FILE * f, int last)
 void print_count_file(FILE *f, int header) {
   char temp_str[256];
 
+  if (!main_scenario || (!header && !main_scenario->stats)) {
+	return;
+  }
+
   if (header) {
     fprintf(f, "CurrentTime%sElapsedTime%s", stat_delimiter, stat_delimiter);
   } else {
     struct timeval currentTime, startTime;
     GET_TIME(&currentTime);
-    display_scenario->stats->getStartTime(&startTime);
+    main_scenario->stats->getStartTime(&startTime);
     unsigned long globalElapsedTime = CStat::computeDiffTimeInMs (&currentTime, &startTime);
     fprintf(f, "%s%s", CStat::formatTime(&currentTime), stat_delimiter);
     fprintf(f, "%s%s", CStat::msToHHMMSSmmm(globalElapsedTime), stat_delimiter);
   }
 
-  for(int index = 0; index < main_scenario->length; index ++) {
+  for(unsigned int index = 0; index < main_scenario->messages.size(); index ++) {
     message *curmsg = main_scenario->messages[index];
     if(curmsg->hide) {
       continue;
@@ -1077,18 +1127,18 @@ void print_count_file(FILE *f, int header) {
 	  fprintf(f, "%sLost%s", temp_str, stat_delimiter);
 	}
       } else {
-	fprintf(f, "%d%s", curmsg->nb_sent, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_sent_retrans, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_sent, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_sent_retrans, stat_delimiter);
 	if(curmsg -> retrans_delay) {
-	  fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter);
+	  fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter);
 	}
 	if(lose_packets) {
-	  fprintf(f, "%d%s", curmsg->nb_lost, stat_delimiter);
+	  fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter);
 	}
       }
     } else if(curmsg -> recv_response) {
       if(header) {
-	sprintf(temp_str, "%d_%d_", index, curmsg->recv_response);
+	sprintf(temp_str, "%u_%d_", index, curmsg->recv_response);
 
 	fprintf(f, "%sRecv%s", temp_str, stat_delimiter);
 	fprintf(f, "%sRetrans%s", temp_str, stat_delimiter);
@@ -1098,17 +1148,17 @@ void print_count_file(FILE *f, int header) {
 	  fprintf(f, "%sLost%s", temp_str, stat_delimiter);
 	}
       } else {
-	fprintf(f, "%d%s", curmsg->nb_recv, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_recv_retrans, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_unexp, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter);
 	if(lose_packets) {
-	  fprintf(f, "%d%s", curmsg->nb_lost, stat_delimiter);
+	  fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter);
 	}
       }
     } else if(curmsg -> recv_request) {
       if(header) {
-	sprintf(temp_str, "%d_%s_", index, curmsg->recv_request);
+	sprintf(temp_str, "%u_%s_", index, curmsg->recv_request);
 
 	fprintf(f, "%sRecv%s", temp_str, stat_delimiter);
 	fprintf(f, "%sRetrans%s", temp_str, stat_delimiter);
@@ -1118,12 +1168,12 @@ void print_count_file(FILE *f, int header) {
 	  fprintf(f, "%sLost%s", temp_str, stat_delimiter);
 	}
       } else {
-	fprintf(f, "%d%s", curmsg->nb_recv, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_recv_retrans, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_unexp, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_recv, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_recv_retrans, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter);
 	if(lose_packets) {
-	  fprintf(f, "%d%s", curmsg->nb_lost, stat_delimiter);
+	  fprintf(f, "%lu%s", curmsg->nb_lost, stat_delimiter);
 	}
       }
     } else if (curmsg -> pause_distribution ||
@@ -1135,7 +1185,7 @@ void print_count_file(FILE *f, int header) {
 	fprintf(f, "%sUnexp%s", temp_str, stat_delimiter);
       } else {
 	fprintf(f, "%d%s", curmsg->sessions, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_unexp, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_unexp, stat_delimiter);
       }
     } else if(curmsg -> M_type == MSG_TYPE_NOP) {
       /* No output. */
@@ -1145,15 +1195,15 @@ void print_count_file(FILE *f, int header) {
 	fprintf(f, "%s%s", temp_str, stat_delimiter);
 	fprintf(f, "%s_Timeout%s", temp_str, stat_delimiter);
       } else {
-	fprintf(f, "%d%s", curmsg->M_nbCmdRecv, stat_delimiter);
-	fprintf(f, "%d%s", curmsg->nb_timeout, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->M_nbCmdRecv, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->nb_timeout, stat_delimiter);
       }
     } else if(curmsg -> M_type == MSG_TYPE_SENDCMD) {
       if(header) {
 	sprintf(temp_str, "%d_SendCmd", index);
-	fprintf(f, "%s%s", temp_str);
+	fprintf(f, "%s%s", temp_str, stat_delimiter);
       } else {
-	fprintf(f, "%d%s", curmsg->M_nbCmdSent, stat_delimiter);
+	fprintf(f, "%lu%s", curmsg->M_nbCmdSent, stat_delimiter);
       }
     } else {
       ERROR("Unknown count file message type:");
@@ -1202,22 +1252,28 @@ void print_bottom_line(FILE *f, int last)
   } else if(outbound_congestion) {
     fprintf(f,"------------------------------ OUTBOUND CONGESTION -----------------------------" SIPP_ENDL);
   } else {
-    switch(toolMode)
-      {
-      case MODE_SERVER :
-        fprintf(f,"------------------------------ Sipp Server Mode -------------------------------" SIPP_ENDL);
+    if (creationMode == MODE_CLIENT) {
+      switch(thirdPartyMode) {
+      case MODE_MASTER :
+        fprintf(f,"-----------------------3PCC extended mode - Master side -------------------------" SIPP_ENDL);
         break;
-      case MODE_3PCC_CONTROLLER_B :
-        fprintf(f,"----------------------- 3PCC Mode - Controller B side -------------------------" SIPP_ENDL);
+      case MODE_3PCC_CONTROLLER_A :
+        fprintf(f,"----------------------- 3PCC Mode - Controller A side -------------------------" SIPP_ENDL);
         break;
+      case MODE_3PCC_NONE:
+	fprintf(f,"------ [+|-|*|/]: Adjust rate ---- [q]: Soft exit ---- [p]: Pause traffic -----" SIPP_ENDL);
+	break;
+      default:
+	ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode);
+      }
+    } else {
+      assert(creationMode == MODE_SERVER);
+      switch(thirdPartyMode) {
       case MODE_3PCC_A_PASSIVE :
         fprintf(f,"------------------ 3PCC Mode - Controller A side (passive) --------------------" SIPP_ENDL);
         break;
-      case MODE_3PCC_CONTROLLER_A :
-        fprintf(f,"----------------------- 3PCC Mode - Controller A side -------------------------" SIPP_ENDL);
-        break;
-      case MODE_MASTER :
-        fprintf(f,"-----------------------3PCC extended mode - Master side -------------------------" SIPP_ENDL);
+      case MODE_3PCC_CONTROLLER_B :
+        fprintf(f,"----------------------- 3PCC Mode - Controller B side -------------------------" SIPP_ENDL);
         break;
       case MODE_MASTER_PASSIVE :
         fprintf(f,"------------------ 3PCC extended mode - Master side (passive) --------------------" SIPP_ENDL);
@@ -1225,11 +1281,13 @@ void print_bottom_line(FILE *f, int last)
       case MODE_SLAVE :
         fprintf(f,"----------------------- 3PCC extended mode - Slave side -------------------------" SIPP_ENDL);
         break; 
-      case MODE_CLIENT :
+      case MODE_3PCC_NONE:
+        fprintf(f,"------------------------------ Sipp Server Mode -------------------------------" SIPP_ENDL);
+	break;
       default:
-        fprintf(f,"------ [+|-|*|/]: Adjust rate ---- [q]: Soft exit ---- [p]: Pause traffic -----" SIPP_ENDL);
-        break;
+	ERROR("Internal error: creationMode=%d, thirdPartyMode=%d", creationMode, thirdPartyMode);
       }
+    }
   }
   fprintf(f,SIPP_ENDL);
   fflush(stdout);
@@ -1256,7 +1314,7 @@ void print_tdm_map()
   printf(SIPP_ENDL);
   printf("%d/%d circuits (%d%%) in use", in_use, interval, int(100*in_use/interval));
   printf(SIPP_ENDL);
-  for(int i=0; i<(display_scenario->length + 8 - int(interval/(tdm_map_c+1))); i++) {
+  for(unsigned int i=0; i<(display_scenario->messages.size() + 8 - int(interval/(tdm_map_c+1))); i++) {
     printf(SIPP_ENDL);
   }
 }
@@ -1265,14 +1323,13 @@ void print_variable_list()
 {
   CActions  * actions;
   CAction   * action;
-  int i,j;
   int printed = 0;
   bool found;
 
   printf("Action defined Per Message :" SIPP_ENDL);
   printed++;
   found = false;
-  for(i=0; i<display_scenario->length; i++)
+  for(unsigned int i=0; i<display_scenario->messages.size(); i++)
   {
     message *curmsg = display_scenario->messages[i];
     actions = curmsg->M_actions;
@@ -1322,7 +1379,7 @@ void print_variable_list()
   }
   
   printf(SIPP_ENDL);
-  for(i=0; i<(display_scenario->length + 5 - printed); i++) {
+  for(unsigned int i=0; i<(display_scenario->messages.size() + 5 - printed); i++) {
     printf(SIPP_ENDL);
   }
 }
@@ -1349,10 +1406,9 @@ void print_screens(void)
   print_bottom_line(   screenf, 0);
 
   currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN;
-  for (int i = 1; i <= MAX_RTD_INFO_LENGTH; i++) {
-    currentRepartitionToDisplay = i;
+  for (currentRepartitionToDisplay = 2; currentRepartitionToDisplay <= display_scenario->stats->nRtds(); currentRepartitionToDisplay++) {
     print_header_line(   screenf, 0);
-    display_scenario->stats->displaySecondaryRepartition(screenf, i-1);
+    display_scenario->stats->displayRtdRepartition(screenf, currentRepartitionToDisplay);
     print_bottom_line(   screenf, 0);
   }
 
@@ -1392,7 +1448,7 @@ void print_statistics(int last)
         print_tdm_map();
         break;
       case DISPLAY_SECONDARY_REPARTITION_SCREEN :
-	display_scenario->stats->displaySecondaryRepartition(stdout, currentRepartitionToDisplay - 1);
+	display_scenario->stats->displayRtdRepartition(stdout, currentRepartitionToDisplay);
 	break;
       case DISPLAY_SCENARIO_SCREEN :
       default:
@@ -1427,71 +1483,6 @@ void print_statistics(int last)
   }
 }
 
-void set_rate(double new_rate)
-{
-  if(toolMode == MODE_SERVER) {
-    rate = 0;
-    open_calls_allowed = 0;
-  }
-
-  rate = new_rate;
-  if(rate < 0) {
-    rate = 0;
-  }
-
-  last_rate_change_time = clock_tick;
-  calls_since_last_rate_change = 0;
-
-  if(!open_calls_user_setting) {
-
-    int call_duration_min =  display_scenario->duration;
-
-    if(duration > call_duration_min) call_duration_min = duration;
-
-    if(call_duration_min < 1000) call_duration_min = 1000;
-
-    open_calls_allowed = (int)((3.0 * rate * call_duration_min) / (double)rate_period_ms);
-    if(!open_calls_allowed) {
-      open_calls_allowed = 1;
-    }
-  }
-}
-
-void set_users(int new_users)
-{
-  if (new_users < 0) {
-    new_users = 0;
-  }
-  assert(users >= 0);
-
-  if(toolMode == MODE_SERVER) {
-    rate = 0;
-    open_calls_allowed = 0;
-  }
-
-  if (users < new_users ) {
-    while (users < new_users) {
-      int userid;
-      if (!retiredUsers.empty()) {
-	userid = retiredUsers.back();
-	retiredUsers.pop_back();
-      } else {
-	userid = users + 1;
-	userVarMap[userid] = new VariableTable(userVariables);
-      }
-      freeUsers.push_front(userid);
-      users++;
-    }
-  }
-
-  users = open_calls_allowed = new_users;
-
-  last_rate_change_time = clock_tick;
-  calls_since_last_rate_change = 0;
-
-  assert(open_calls_user_setting);
-}
-
 void sipp_sigusr1(int /* not used */)
 {
   /* Smooth exit: do not place any new calls and exit */
@@ -1540,56 +1531,51 @@ bool process_key(int c) {
     case '8':
     case '9':
       currentScreenToDisplay = DISPLAY_SECONDARY_REPARTITION_SCREEN;
-      currentRepartitionToDisplay = (c - '6') + 1;
+      currentRepartitionToDisplay = (c - '6') + 2;
       print_statistics(0);
       break;
 
     case '+':
       if (users >= 0) {
-	set_users((int)(users + 1 * rate_scale));
+	opentask::set_users((int)(users + 1 * rate_scale));
       } else {
-	set_rate(rate + 1 * rate_scale);
+	opentask::set_rate(rate + 1 * rate_scale);
       }
       print_statistics(0);
       break;
 
     case '-':
       if (users >= 0) {
-	set_users((int)(users - 1 * rate_scale));
+	opentask::set_users((int)(users - 1 * rate_scale));
       } else {
-	set_rate(rate - 1 * rate_scale);
+	opentask::set_rate(rate - 1 * rate_scale);
       }
       print_statistics(0);
       break;
 
     case '*':
       if (users >= 0) {
-	set_users((int)(users + 10 * rate_scale));
+	opentask::set_users((int)(users + 10 * rate_scale));
       } else {
-	set_rate(rate + 10 * rate_scale);
+	opentask::set_rate(rate + 10 * rate_scale);
       }
       print_statistics(0);
       break;
 
     case '/':
       if (users >= 0) {
-	set_users((int)(users - 10 * rate_scale));
+	opentask::set_users((int)(users - 10 * rate_scale));
       } else {
-	set_rate(rate - 10 * rate_scale);
+	opentask::set_rate(rate - 10 * rate_scale);
       }
       print_statistics(0);
       break;
 
     case 'p':
       if(paused) { 
-	paused = 0;
-	if (users >= 0) {
-	  set_users(users);
-	} else {
-	  set_rate(rate);
-	}
+	opentask::set_paused(false);
       } else {
-	paused = 1;
+	opentask::set_paused(true);
       }
       print_statistics(0);
       break;
@@ -1645,7 +1631,7 @@ void process_set(char *what) {
     } else if (*end) {
       WARNING("Invalid rate value: \"%s\"", rest);
     } else {
-      set_rate(drest);
+      opentask::set_rate(drest);
     }
   } else if (!strcmp(what, "rate-scale")) {
     char *end;
@@ -1666,13 +1652,15 @@ void process_set(char *what) {
     } else if (urest < 0) {
       WARNING("Invalid users value: \"%s\"", rest);
     } else {
-      set_users(urest);
+      opentask::set_users(urest);
     }
   } else if (!strcmp(what, "limit")) {
     char *end;
     unsigned long lrest = strtoul(rest, &end, 0);
-    if (*end) {
-      WARNING("Invalid rate-scale value: \"%s\"", rest);
+    if (users >= 0) {
+      WARNING("Can not set call limit for a user-based benchmark.");
+    } else if (*end) {
+      WARNING("Invalid limit value: \"%s\"", rest);
     } else {
       open_calls_allowed = lrest;
       open_calls_user_setting = 1;
@@ -1706,6 +1694,15 @@ void process_set(char *what) {
   }
 }
 
+void log_off(struct logfile_info *lfi) {
+  if (lfi->fptr) {
+    fflush(lfi->fptr);
+    fclose(lfi->fptr);
+    lfi->fptr = NULL;
+    lfi->overwrite = false;
+  }
+}
+
 void process_trace(char *what) {
   bool on = false;
   char *rest = strchr(what, ' ');
@@ -1742,15 +1739,10 @@ void process_trace(char *what) {
       print_all_responses = 1;
     } else {
       print_all_responses = 0;
-      if (screen_errorf) {
-	fflush(screen_errorf);
-	fclose(screen_errorf);
-	screen_errorf = NULL;
-	errorf_overwrite = false;
-      }
+      log_off(&error_lfi);
     }
   } else if (!strcmp(what, "logs")) {
-    if (on == !!logfile) {
+    if (on == !!log_lfi.fptr) {
       return;
     }
     if (on) {
@@ -1758,13 +1750,10 @@ void process_trace(char *what) {
       rotate_logfile();
     } else {
       useLogf = 0;
-      fflush(logfile);
-      fclose(logfile);
-      logfile = NULL;
-      logfile_overwrite = false;
+      log_off(&log_lfi);
     }
   } else if (!strcmp(what, "messages")) {
-    if (on == !!messagef) {
+    if (on == !!message_lfi.fptr) {
       return;
     }
     if (on) {
@@ -1772,13 +1761,10 @@ void process_trace(char *what) {
       rotate_logfile();
     } else {
       useMessagef = 0;
-      fflush(messagef);
-      fclose(messagef);
-      messagef = NULL;
-      messagef_overwrite = false;
+      log_off(&message_lfi);
     }
   } else if (!strcmp(what, "shortmessages")) {
-    if (on == !!shortmessagef) {
+    if (on == !!shortmessage_lfi.fptr) {
       return;
     }
 
@@ -1787,10 +1773,7 @@ void process_trace(char *what) {
       rotate_shortmessagef();
     } else {
       useShortMessagef = 0;
-      fflush(shortmessagef);
-      fclose(shortmessagef);
-      shortmessagef = NULL;
-      shortmessagef_overwrite = false;
+      log_off(&shortmessage_lfi);
     }
   } else {
     WARNING("Unknown log file: %s", what);
@@ -1800,11 +1783,21 @@ void process_trace(char *what) {
 void process_dump(char *what) {
   if (!strcmp(what, "tasks")) {
     dump_tasks();
+  } else if (!strcmp(what, "variables")) {
+    display_scenario->allocVars->dump();
   } else {
     WARNING("Unknown dump type: %s", what);
   }
 }
 
+void process_reset(char *what) {
+  if (!strcmp(what, "stats")) {
+    main_scenario->stats->computeStat(CStat::E_RESET_C_COUNTERS);
+  } else {
+    WARNING("Unknown reset type: %s", what);
+  }
+}
+
 bool process_command(char *command) {
   trim(command);
 
@@ -1820,6 +1813,8 @@ bool process_command(char *command) {
 	process_trace(rest);
   } else if (!strcmp(command, "dump")) {
 	process_dump(rest);
+  } else if (!strcmp(command, "reset")) {
+	process_reset(rest);
   } else {
 	WARNING("Unrecognized command: \"%s\"", command);
   }
@@ -1849,10 +1844,10 @@ int handle_ctrl_socket() {
   } else {
     process_key(bufrcv[0]);
   }
+  return 0;
 }
 
 void setup_ctrl_socket() {
-  int ret;
   int port, firstport;
   int try_counter = 60;
   struct sockaddr_storage ctl_sa;
@@ -1907,8 +1902,8 @@ void setup_ctrl_socket() {
 
   if (try_counter == 0) {
     if (control_port) {
-      ERROR("Unable to bind remote control socket to UDP port %d: %s",
-                  control_port, strerror(errno));
+      ERROR_NO("Unable to bind remote control socket to UDP port %d",
+                  control_port);
     } else {
       WARNING("Unable to bind remote control socket (tried UDP ports %d-%d): %s",
                   firstport, port - 1, strerror(errno));
@@ -2079,7 +2074,7 @@ char * get_incoming_header_content(char* message, char * name)
       return last_header;
   }
 
-  while(src = strstr(src, name)) {
+  while((src = strstr(src, name))) {
 
       /* just want the header's content */
       src += strlen(name);
@@ -2118,7 +2113,7 @@ char * get_incoming_header_content(char* message, char * name)
   }
 
   /* remove enclosed CRs in multilines */
-  while(ptr = strchr(last_header, '\r')) {
+  while((ptr = strchr(last_header, '\r'))) {
     /* Use strlen(ptr) to include trailing zero */
     memmove(ptr, ptr+1, strlen(ptr));
   }
@@ -2130,7 +2125,7 @@ char * get_incoming_first_line(char * message)
 {
   /* non reentrant. consider accepting char buffer as param */
   static char last_header[MAX_HEADER_LEN * 10];
-  char * src, *dest, *ptr;
+  char * src, *dest;
 
   /* returns empty string in case of error */
   memset(last_header, 0, sizeof(last_header));
@@ -2261,6 +2256,7 @@ struct socketbuf *alloc_socketbuf(char *buffer, size_t size, int copy, struct so
   if (!socketbuf) {
 	ERROR("Could not allocate socket buffer!\n");
   }
+  memset(socketbuf, 0, sizeof(struct socketbuf));
   if (copy) {
     socketbuf->buf = (char *)malloc(size);
     if (!socketbuf->buf) {
@@ -2520,7 +2516,9 @@ static int read_error(struct sipp_socket *socket, int ret) {
 #endif
 
   /* We have only non-blocking reads, so this should not occur. */
-  assert(errno != EAGAIN);
+  if (ret < 0) {
+    assert(errno != EAGAIN);
+  }
 
   if (socket->ss_transport == T_TCP || socket->ss_transport == T_TLS) {
     if (ret == 0) {
@@ -2535,7 +2533,7 @@ static int read_error(struct sipp_socket *socket, int ret) {
           quitting += 20;
           }else if(twinSippMode) {
            if(twinSippSocket) sipp_close_socket(twinSippSocket);
-           if(toolMode == MODE_3PCC_CONTROLLER_B) {
+           if(thirdPartyMode == MODE_3PCC_CONTROLLER_B) {
              WARNING("3PCC controller A has ended -> exiting");
              quitting += 20;
            }else {
@@ -2602,7 +2600,6 @@ static int flush_socket(struct sipp_socket *socket) {
 
 void buffer_write(struct sipp_socket *socket, char *buffer, size_t len, struct sockaddr_storage *dest) {
   struct socketbuf *buf = socket->ss_out;
-  struct socketbuf *prev = buf;
 
   if (!buf) {
 	socket->ss_out = alloc_socketbuf(buffer, len, DO_COPY, dest);
@@ -2611,11 +2608,10 @@ void buffer_write(struct sipp_socket *socket, char *buffer, size_t len, struct s
   }
 
   while(buf->next) {
-	prev = buf;
 	buf = buf->next;
   }
 
-  prev->next = alloc_socketbuf(buffer, len, DO_COPY, dest);
+  buf->next = alloc_socketbuf(buffer, len, DO_COPY, dest);
   TRACE_MSG("Appended buffered message to socket %d\n", socket->ss_fd);
 }
 
@@ -2639,6 +2635,10 @@ void buffer_read(struct sipp_socket *socket, struct socketbuf *newbuf) {
 /* Write data to a socket. */
 int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flags, struct sockaddr_storage *dest) {
   int rc;
+  if ( socket == NULL ) {
+    //FIX coredump when trying to send data but no master yet ... ( for example after unexpected mesdsage)
+    return 0;
+  }
 
   if (socket->ss_out) {
     rc = flush_socket(socket);
@@ -2670,6 +2670,7 @@ int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flag
   } else if (rc <= 0) {
     if ((errno == EWOULDBLOCK) && (flags & WS_BUFFER)) {
       buffer_write(socket, buffer, len, dest);
+      enter_congestion(socket, errno);
       return len;
     }
     if (useMessagef == 1) {
@@ -2694,6 +2695,7 @@ int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flag
 	    rc, len, len, buffer);
     }
     buffer_write(socket, buffer + rc, len - rc, dest);
+    enter_congestion(socket, errno);
   }
 
   return rc;
@@ -2930,10 +2932,6 @@ void sipp_socket_invalidate(struct sipp_socket *socket) {
     ERROR("Pollset error: index %d is greater than number of fds %d!", pollidx, pollnfds);
   }
 
-  if (socket->ss_call_socket) {
-    call_sockets--;
-  }
-
   socket->ss_invalid = true;
   socket->ss_pollidx = -1;
 
@@ -2944,6 +2942,12 @@ void sipp_socket_invalidate(struct sipp_socket *socket) {
   pollfiles[pollidx] = pollfiles[pollnfds];
   sockets[pollidx] = sockets[pollnfds];
   sockets[pollidx]->ss_pollidx = pollidx;
+  sockets[pollnfds] = NULL;
+
+  if (socket->ss_msglen)
+  {
+     pending_messages--;
+  }
 }
 
 void sipp_close_socket (struct sipp_socket *socket) {
@@ -2963,7 +2967,8 @@ static ssize_t read_message(struct sipp_socket *socket, char *buf, size_t len, s
   if (!socket->ss_msglen)
     return 0;
   if (socket->ss_msglen > len)
-    ERROR("There is a message waiting in a socket that is bigger (%zd bytes) than the read size.", socket->ss_msglen);
+    ERROR("There is a message waiting in sockfd(%d) that is bigger (%d bytes) than the read size.",
+           socket->ss_fd, socket->ss_msglen);
 
   len = socket->ss_msglen;
 
@@ -3030,29 +3035,14 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st
   if (useShortMessagef == 1) {
               struct timeval currentTime;
               GET_TIME (&currentTime);
-              TRACE_SHORTMSG("%s\tS\t%s\tCSeq:%s\t%s\n",
+              TRACE_SHORTMSG("%s\tR\t%s\tCSeq:%s\t%s\n",
               CStat::formatTime(&currentTime),call_id, get_incoming_header_content(msg,"CSeq:"), get_incoming_first_line(msg));
           } 
 
   if(!listener_ptr)
   {
-    if(toolMode == MODE_SERVER)
-    {
-      if (quitting >= 1) {
-	CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS);
-	TRACE_MSG("Discarded message for new calls while quitting\n");
-	return;
-      }
-
-      // Adding a new INCOMING call !
-      main_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL);
-      listener_ptr = new call(call_id, socket, src);
-      if (!listener_ptr) {
-	ERROR("Out of memory allocating a call!");
-      }
-    }
-    else if(toolMode == MODE_3PCC_CONTROLLER_B || toolMode == MODE_3PCC_A_PASSIVE
-	|| toolMode == MODE_MASTER_PASSIVE || toolMode == MODE_SLAVE)
+    if(thirdPartyMode == MODE_3PCC_CONTROLLER_B || thirdPartyMode == MODE_3PCC_A_PASSIVE
+	|| thirdPartyMode == MODE_MASTER_PASSIVE || thirdPartyMode == MODE_SLAVE)
     {
       // Adding a new OUTGOING call !
       main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL);
@@ -3087,6 +3077,21 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st
       }
       listener_ptr = new_ptr;
     }
+    else if(creationMode == MODE_SERVER)
+    {
+      if (quitting >= 1) {
+	CStat::globalStat(CStat::E_OUT_OF_CALL_MSGS);
+	TRACE_MSG("Discarded message for new calls while quitting\n");
+	return;
+      }
+
+      // Adding a new INCOMING call !
+      main_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL);
+      listener_ptr = new call(call_id, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src);
+      if (!listener_ptr) {
+	ERROR("Out of memory allocating a call!");
+      }
+    }
     else // mode != from SERVER and 3PCC Controller B
     {
       // This is a message that is not relating to any known call
@@ -3097,12 +3102,12 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st
 	if(!get_reply_code(msg)){
 	  ooc_scenario->stats->computeStat(CStat::E_CREATE_INCOMING_CALL);
 	  /* This should have the real address that the message came from. */
-	  call *call_ptr = new call(ooc_scenario, socket, src, call_id, 0 /* no user. */, socket->ss_ipv6, true);
+	  call *call_ptr = new call(ooc_scenario, socket, use_remote_sending_addr ? &remote_sending_sockaddr : src, call_id, 0 /* no user. */, socket->ss_ipv6, true, false);
 	  if (!call_ptr) {
 	    ERROR("Out of memory allocating a call!");
 	  }
 	  CStat::globalStat(CStat::E_AUTO_ANSWERED);
-	  call_ptr->process_incoming(msg);
+	  call_ptr->process_incoming(msg, src);
 	} else {
 	  /* We received a response not relating to any known call */
 	  /* Do nothing, even if in auto answer mode */
@@ -3126,11 +3131,11 @@ void process_message(struct sipp_socket *socket, char *msg, ssize_t msg_size, st
   }
   else
   {
-    listener_ptr -> process_incoming(msg);
+    listener_ptr -> process_incoming(msg, src);
   }
 }
 
-void pollset_process()
+void pollset_process(int wait)
 {
   int rs; /* Number of times to execute recv().
 	     For TCP with 1 socket per call:
@@ -3144,9 +3149,13 @@ void pollset_process()
   /* What index should we try reading from? */
   static int read_index;
 
+  if (read_index >= pollnfds) {
+    read_index = 0;
+  }
+
   /* We need to process any messages that we have left over. */
-  while (pending_messages && (loops-- > 0)) {
-    clock_tick = getmilliseconds();
+  while (pending_messages && (loops > 0)) {
+    getmilliseconds();
     if (sockets[read_index]->ss_msglen) {
 	struct sockaddr_storage src;
 	char msg[SIPP_MAX_MSG_SIZE];
@@ -3156,6 +3165,7 @@ void pollset_process()
 	} else {
 	  assert(0);
 	}
+	loops--;
       }
     read_index = (read_index + 1) % pollnfds;
   }
@@ -3166,7 +3176,7 @@ void pollset_process()
   }
 
   /* Get socket events. */
-  rs = poll(pollfiles, pollnfds,  1);
+  rs = poll(pollfiles, pollnfds, wait ? 1 : 0);
   if((rs < 0) && (errno == EINTR)) {
     return;
   }
@@ -3201,7 +3211,7 @@ void pollset_process()
       } else if (sock == stdin_socket) {
 	handle_stdin_socket();
       } else if (sock == localTwinSippSocket) {
-	if (toolMode == MODE_3PCC_CONTROLLER_B) {
+	if (thirdPartyMode == MODE_3PCC_CONTROLLER_B) {
 	  twinSippSocket = sipp_accept_socket(sock);
 	  if (!twinSippMode) {
 	    ERROR_NO("Accepting new TCP connection on Twin SIPp Socket.\n");
@@ -3225,7 +3235,16 @@ void pollset_process()
 	}
       } else {
 	if ((ret = empty_socket(sock)) <= 0) {
-	  read_error(sock, ret);
+	  ret = read_error(sock, ret);
+	  if (ret == 0) {
+	    /* If read_error() then the poll_idx now belongs
+	     * to the newest/last socket added to the sockets[].
+	     * Need to re-do the same poll_idx for the "new" socket. */
+	    poll_idx--;
+	    events++;
+	    rs--;
+	    continue;
+	  }
 	}
       }
       events++;
@@ -3237,9 +3256,13 @@ void pollset_process()
     }
   }
 
+  if (read_index >= pollnfds) {
+    read_index = 0;
+  }
+
   /* We need to process any new messages that we read. */
-  while (pending_messages && (loops-- > 0)) {
-    clock_tick = getmilliseconds();
+  while (pending_messages && (loops > 0)) {
+    getmilliseconds();
 
     if (sockets[read_index]->ss_msglen) {
       char msg[SIPP_MAX_MSG_SIZE];
@@ -3252,6 +3275,7 @@ void pollset_process()
       } else {
 	assert(0);
       }
+      loops--;
     }
     read_index = (read_index + 1) % pollnfds;
   }
@@ -3260,6 +3284,9 @@ void pollset_process()
 }
 
 void timeout_alarm(int param){
+  if (timeout_error) {
+    ERROR("%s timed out after '%.3lf' seconds", scenario_file, ((double)clock_tick / 1000LL));
+  }
   quitting = 1;
   timeout_exit = true;
 }
@@ -3268,33 +3295,26 @@ void timeout_alarm(int param){
 
 void traffic_thread()
 {
-  unsigned int calls_to_open = 0;
-  unsigned int new_time;
-  unsigned int last_time;
-  bool         firstPass;
-
   /* create the file */
   char         L_file_name [MAX_PATH];
   sprintf (L_file_name, "%s_%d_screen.log", scenario_file, getpid());
 
+  getmilliseconds();
 
-  firstPass = true;
-  last_time = getmilliseconds();
- 
   /* Arm the global timer if needed */
   if (global_timeout > 0) { 
     signal(SIGALRM, timeout_alarm);
     alarm(global_timeout / 1000);
   }
-  
-  while(1) {
-    scheduling_loops ++;
 
-    /* update local time, except if resetted*/
-    new_time = getmilliseconds();
+  // Dump (to create file on disk) and showing screen at the beginning even if
+  // the report period is not reached
+  stattask::report();
+  screentask::report(false);
 
-    clock_tick = new_time;
-    last_time = new_time;
+  while(1) {
+    scheduling_loops++;
+    getmilliseconds();
 
     if (signalDump) {
        /* Screen dumping in a file */
@@ -3324,86 +3344,10 @@ void traffic_thread()
 	sockets_pending_reset.erase(sockets_pending_reset.begin());
     }
 
-    if ((!quitting) && (!paused)) {
-      long l=0;
-      unsigned long long  current_calls = main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall);
-      unsigned long long total_calls = main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated);
-
-      if (users >= 0) {
-	calls_to_open = ((l = (users - current_calls)) > 0) ? l : 0;
-      } else {
-	calls_to_open = (unsigned int)
-              ((l=(long)floor(((clock_tick - last_rate_change_time) * rate/rate_period_ms)
-              - calls_since_last_rate_change))>0?l:0);
-      }
-
-
-      if( (toolMode == MODE_CLIENT)
-          || (toolMode == MODE_3PCC_CONTROLLER_A)
-          || (toolMode == MODE_MASTER)
-          )
-        {
-	  int first_open_tick = clock_tick;
-          while((calls_to_open--) && 
-                (!open_calls_allowed || current_calls < open_calls_allowed) &&
-                (total_calls < stop_after)) 
-            {
-	      /* Associate a user with this call, if we are in users mode. */
-	      int userid = 0;
-	      if (users >= 0) {
-		userid = freeUsers.back();
-		freeUsers.pop_back();
-	      }
-
-              // adding a new OUTGOING CALL
-              main_scenario->stats->computeStat(CStat::E_CREATE_OUTGOING_CALL);
-              call * call_ptr = call::add_call(userid, is_ipv6, use_remote_sending_addr ? &remote_sending_sockaddr : &remote_sockaddr);
-              if(!call_ptr) {
-		ERROR("Out of memory allocating call!");
-	      }
-
-	      calls_since_last_rate_change++;
-
-	      outbound_congestion = false;
-
-	      if (!multisocket) {
-		switch(transport) {
-		  case T_UDP:
-		    call_ptr->associate_socket(main_socket);
-		    main_socket->ss_count++;
-		    break;
-		  case T_TCP:
-		  case T_TLS:
-		    call_ptr->associate_socket(tcp_multiplex);
-		    tcp_multiplex->ss_count++;
-		    break;
-		}
-	      }
-
-	      call_ptr -> run();
-
-	      while (sockets_pending_reset.begin() != sockets_pending_reset.end()) {
-		reset_connection(*(sockets_pending_reset.begin()));
-		sockets_pending_reset.erase(sockets_pending_reset.begin());
-	      }
-
-	      new_time = getmilliseconds();
-	      /* Never spend more than half of our time processing new call requests. */
-	      if (new_time > (first_open_tick + (timer_resolution < 2 ? 1 : (timer_resolution / 2)))) {
-		break;
-	      }
-            }
-
-	  if(open_calls_allowed && (current_calls >= open_calls_allowed)) {
-	    set_rate(rate);
-	  }
-        }
-
-        // Quit after asked number of calls is reached
-        if(total_calls >= stop_after) {
-          quitting = 1;
-        }
-    } else if (quitting) {
+    if ((main_scenario->stats->GetStat(CStat::CPT_C_IncomingCallCreated) + main_scenario->stats->GetStat(CStat::CPT_C_OutgoingCallCreated)) >= stop_after) {
+	quitting = 1;
+    }
+    if (quitting) {
       if (quitting > 11) {
         /* Force exit: abort all calls */
 	abort_all_tasks();
@@ -3412,36 +3356,22 @@ void traffic_thread()
       if(!main_scenario->stats->GetStat(CStat::CPT_C_CurrentCall)) {
 	/* We can have calls that do not count towards our open-call count (e.g., dead calls). */
 	abort_all_tasks();
-	print_statistics(0);
-
-        // Dump the latest statistics if necessary
-        if(dumpInFile) {
-          main_scenario->stats->dumpData();
-        }
-	if (useCountf) {
-	  print_count_file(countf, 0);
-	}
-
-        if(dumpInRtt) {
-          main_scenario->stats->dumpDataRtt();
-        }
-
-        /* Screen dumping in a file if asked */
-        if(screenf) {
-          print_screens();
-        }
 
 	for (int i = 0; i < pollnfds; i++) {
 	  sipp_close_socket(sockets[i]);
 	}
 
+	screentask::report(true);
+	stattask::report();
+	if (screenf) {
+	  print_screens();
+	}
+
         screen_exit(EXIT_TEST_RES_UNKNOWN);
       }
     }
 
-    new_time = getmilliseconds();
-    clock_tick = new_time;
-    last_time = new_time;
+    getmilliseconds();
 
     /* Schedule all pending calls and process their timers */
     task_list *running_tasks;
@@ -3476,9 +3406,9 @@ void traffic_thread()
     for(iter = running_tasks->begin(); iter != running_tasks->end(); iter++) {
       if(last) {
 	last -> run();
-	while (sockets_pending_reset.begin() != sockets_pending_reset.end()) {
-	  reset_connection(*(sockets_pending_reset.begin()));
-	  sockets_pending_reset.erase(sockets_pending_reset.begin());
+	if (sockets_pending_reset.begin() != sockets_pending_reset.end()) {
+	  last = NULL;
+	  break;
 	}
       }
       last = *iter;
@@ -3488,80 +3418,16 @@ void traffic_thread()
     }
     if(last) {
       last -> run();
-      while (sockets_pending_reset.begin() != sockets_pending_reset.end()) {
-	reset_connection(*(sockets_pending_reset.begin()));
-	sockets_pending_reset.erase(sockets_pending_reset.begin());
-      }
+    }
+    while (sockets_pending_reset.begin() != sockets_pending_reset.end()) {
+      reset_connection(*(sockets_pending_reset.begin()));
+      sockets_pending_reset.erase(sockets_pending_reset.begin());
     }
 
     /* Update the clock. */
-    new_time = getmilliseconds();
-    clock_tick = new_time ;
-    last_time = new_time;
-
+    getmilliseconds();
     /* Receive incoming messages */
-    pollset_process();
-    new_time = getmilliseconds();
-    clock_tick = new_time ;
-    last_time = new_time;
-
-    if(firstPass)
-      {
-        // dumping (to create file on disk) and showing 
-        // screen at the beginning even if the report
-        // period is not reach
-        firstPass = false;
-	if (report_freq > 0) {
-	  print_statistics(0);
-	}
-        /* Dumping once to create the file on disk */
-        if(dumpInFile)
-          {
-            main_scenario->stats->dumpData();
-          }
-
-	if (useCountf) {
-	    print_count_file(countf, 0);
-	}
-
-        if(dumpInRtt)
-          {
-            main_scenario->stats->dumpDataRtt();
-          }
-
-      }
-
-    if(report_freq && ((clock_tick - last_report_time) >= report_freq))
-      {
-        print_statistics(0);
-        display_scenario->stats->computeStat(CStat::E_RESET_PD_COUNTERS);
-        last_report_time  = clock_tick;
-        scheduling_loops = 0;
-      }
-
-    // FIXME - Should we recompute time ? print stat take 
-    // a lot of time, so the clock_time is no more 
-    // the current time !
-    if((clock_tick - last_dump_time) >= report_freq_dumpLog)  {
-      if(dumpInFile) {
-	main_scenario->stats->dumpData();
-      }
-      if (useCountf) {
-	print_count_file(countf, 0);
-      }
-      main_scenario->stats->computeStat(CStat::E_RESET_PL_COUNTERS);
-      last_dump_time  = clock_tick;
-      if (rate_increase) {
-	rate += rate_increase;
-	if (rate_max && (rate > rate_max)) {
-	  rate = rate_max;
-	  if (rate_quit) {
-	    quitting += 10;
-	  }
-	}
-	set_rate(rate);
-      }
-    }
+    pollset_process(running_tasks->size() == 0);
   }
 }
 
@@ -3670,14 +3536,19 @@ char *wrap(const char *in, int offset, int size) {
         }
       } else {
         int m;
+        int move_back = 0;
 
-        out[j] = '\0';
         //printf("Before wrapping (pos = %d, k = %d, j = %d):\n%-*s%s\n", pos, k, j, offset, "", out);
 
         out[k] = '\n';
         pos = j - k;
+        // move_back is used to step back in the in and out buffers when a
+        // word is longer than useoffset.
+        if (i > (k + useoffset)) {
+          move_back = i - (k + useoffset);
+          i -= move_back;
+        }
         k++;
-        out[j] = '\0';
         out = (char *)realloc(out, alloced += useoffset);
         if (!out) {
           ERROR_NO("realloc");
@@ -3688,8 +3559,7 @@ char *wrap(const char *in, int offset, int size) {
           }
           out[k + m] = ' ';
         }
-        j += useoffset;
-        out[j] = '\0';
+        j += useoffset - move_back;
         //printf("After wrapping (pos = %d, k = %d):\n%-*s%s\n", pos, k, offset, "", out);
       }
     }
@@ -3752,6 +3622,7 @@ void help()
      "   97: exit on internal command. Calls may have been processed\n"
      "   99: Normal exit without calls processed\n"
      "   -1: Fatal error\n"
+     "   -2: Fatal error binding a socket\n"
      "\n"
      "\n"
      "Example:\n"
@@ -3842,6 +3713,12 @@ void help_stats()
 "    during the call but the counter is increased only by\n"
 "    one).\n"
 "\n"
+"  - FailedRegexpShouldntMatch:\n"
+"    Number of failed calls because of regexp that shouldn't\n"
+"    match (there might be several regexp that shouldn't match\n"
+"    during the call but the counter is increased only by\n"
+"    one).\n"
+"\n"
 "  - FailedRegexpHdrNotFound:\n"
 "    Number of failed calls because of regexp with hdr    \n"
 "    option but no matching header found.\n"
@@ -3867,6 +3744,9 @@ void print_last_stats()
   // and print statistics screen
   currentScreenToDisplay = DISPLAY_STAT_SCREEN;
   print_statistics(1);
+  if (main_scenario) {
+    stattask::report();
+  }
 }
 
 void freeInFiles() {
@@ -3884,9 +3764,6 @@ void freeUserVarMap() {
 
 void releaseGlobalAllocations()
 {
-  int i,j;
-  message * L_ptMsg = NULL;
-
   delete main_scenario;
   delete ooc_scenario;
   free_default_messages();
@@ -3897,12 +3774,10 @@ void releaseGlobalAllocations()
 
 void stop_all_traces()
 {
-  if(messagef) messagef = NULL;
-  if(logfile) logfile = NULL;
- // if(timeoutf) timeoutf = NULL; TODO: finish the -trace_timeout option implementation
+  message_lfi.fptr = NULL;
+  log_lfi.fptr = NULL;
   if(dumpInRtt) dumpInRtt = 0;
   if(dumpInFile) dumpInFile = 0;
-  
 }
 
 char* remove_pattern(char* P_buffer, char* P_extensionPattern) {
@@ -3941,6 +3816,7 @@ static struct sipp_socket *sipp_allocate_socket(bool use_ipv6, int transport, in
   ret->ss_fd = fd;
   ret->ss_comp_state = NULL;
   ret->ss_count = 1;
+  ret->ss_changed_dest = false;
 
   /* Initialize all sockets with our destination address. */
   memcpy(&ret->ss_remote_sockaddr, &remote_sockaddr, sizeof(ret->ss_remote_sockaddr));
@@ -3999,7 +3875,7 @@ int socket_fd(bool use_ipv6, int transport) {
   }
 
   if((fd = socket(use_ipv6 ? AF_INET6 : AF_INET, socket_type, 0))== -1) {
-    ERROR("Unable to get a %s socket", TRANSPORT_TO_STRING(transport));
+    ERROR("Unable to get a %s socket (3)", TRANSPORT_TO_STRING(transport));
   }
 
   return fd;
@@ -4009,6 +3885,27 @@ struct sipp_socket *new_sipp_socket(bool use_ipv6, int transport) {
   struct sipp_socket *ret;
   int fd = socket_fd(use_ipv6, transport);
 
+#if defined(__SUNOS)
+  if (fd < 256)
+  {
+    int newfd = fcntl(fd, F_DUPFD, 256);
+    if (newfd <= 0)
+    {
+      // Typically, (24)(Too many open files) is the error here
+      WARNING("Unable to get a different %s socket, errno=%d(%s)",
+              TRANSPORT_TO_STRING(transport), errno, strerror(errno));
+
+      // Keep the original socket fd.
+      newfd = fd;
+    }
+    else
+    {
+      close(fd);
+    }
+    fd = newfd;
+  }
+#endif
+
   ret  = sipp_allocate_socket(use_ipv6, transport, fd);
   if (!ret) {
     close(fd);
@@ -4020,7 +3917,7 @@ struct sipp_socket *new_sipp_socket(bool use_ipv6, int transport) {
 struct sipp_socket *new_sipp_call_socket(bool use_ipv6, int transport, bool *existing) {
   struct sipp_socket *sock = NULL;
   static int next_socket;
-  if (call_sockets >= max_multi_socket - 1) {  // we must take the main socket into account
+  if (pollnfds >= max_multi_socket) {  // we must take the main socket into account
     /* Find an existing socket that matches transport and ipv6 parameters. */
     int first = next_socket;
     do
@@ -4028,8 +3925,18 @@ struct sipp_socket *new_sipp_call_socket(bool use_ipv6, int transport, bool *exi
       int test_socket = next_socket;
       next_socket = (next_socket + 1) % pollnfds;
 
-      assert(sockets[test_socket]->ss_call_socket >= 0);
       if (sockets[test_socket]->ss_call_socket) {
+	/* Here we need to check that the address is the default. */
+	if (sockets[test_socket]->ss_ipv6 != use_ipv6) {
+	  continue;
+	}
+	if (sockets[test_socket]->ss_transport != transport) {
+	  continue;
+	}
+	if (sockets[test_socket]->ss_changed_dest) {
+	  continue;
+	}
+
 	sock = sockets[test_socket];
 	sock->ss_count++;
 	*existing = true;
@@ -4043,7 +3950,6 @@ struct sipp_socket *new_sipp_call_socket(bool use_ipv6, int transport, bool *exi
   } else {
     sock = new_sipp_socket(use_ipv6, transport);
     sock->ss_call_socket = true;
-    call_sockets++;
     *existing = false;
   }
   return sock;
@@ -4059,6 +3965,28 @@ struct sipp_socket *sipp_accept_socket(struct sipp_socket *accept_socket) {
     ERROR("Unable to accept on a %s socket: %s", TRANSPORT_TO_STRING(transport), strerror(errno));
   }
 
+#if defined(__SUNOS)
+  if (fd < 256)
+  {
+    int newfd = fcntl(fd, F_DUPFD, 256);
+    if (newfd <= 0)
+    {
+      // Typically, (24)(Too many open files) is the error here
+      WARNING("Unable to get a different %s socket, errno=%d(%s)",
+               TRANSPORT_TO_STRING(transport), errno, strerror(errno));
+
+      // Keep the original socket fd.
+      newfd = fd;
+    }
+    else
+    {
+      close(fd);
+    }
+    fd = newfd;
+  }
+#endif
+
+
   ret  = sipp_allocate_socket(accept_socket->ss_ipv6, accept_socket->ss_transport, fd, 1);
   if (!ret) {
 	close(fd);
@@ -4189,7 +4117,7 @@ int main(int argc, char *argv[])
 {
   int                  argi = 0;
   struct sockaddr_storage   media_sockaddr;
-  pthread_t            pthread_id, pthread2_id,  pthread3_id;
+  pthread_t            pthread2_id,  pthread3_id;
   int                  L_maxSocketPresent = 0;
   unsigned int         generic_count = 0;
   bool                 slave_masterSet = false;
@@ -4249,7 +4177,7 @@ int main(int argc, char *argv[])
 				     "Use 'sipp -h' for details",  argv[argi - 1]); }
 #define CHECK_PASS() if (option->pass != pass) { break; }
 
-  for (int pass = 0; pass <= 2; pass++) {
+  for (int pass = 0; pass <= 3; pass++) {
     for(argi = 1; argi < argc; argi++) {
       struct sipp_option *option = find_option(argv[argi]);
       if (!option) {
@@ -4351,15 +4279,25 @@ int main(int argc, char *argv[])
 	  *((int *)option->data) = argi;
 	  break;
 	case SIPP_OPTION_INPUT_FILE:
-	  REQUIRE_ARG();
-	  CHECK_PASS();
-	  inFiles[argv[argi]] = new FileContents(argv[argi]);
-	  /* By default, the first file is used for IP address input. */
-	  if (!ip_file) {
-	    ip_file = argv[argi];
-	  }
-	  if (!default_file) {
-	    default_file = argv[argi];
+	  {
+	    REQUIRE_ARG();
+	    CHECK_PASS();
+	    FileContents *data = new FileContents(argv[argi]);
+	    char *name = argv[argi];
+	    if (strrchr(name, '/')) {
+	      name = strrchr(name, '/') + 1;
+	    } else if (strrchr(name, '\\')) {
+	      name = strrchr(name, '\\') + 1;
+	    }
+	    assert(name);
+	    inFiles[name] = data;
+	    /* By default, the first file is used for IP address input. */
+	    if (!ip_file) {
+	      ip_file = name;
+	    }
+	    if (!default_file) {
+	      default_file = name;
+	    }
 	  }
 	  break;
 	case SIPP_OPTION_INDEX_FILE:
@@ -4369,7 +4307,6 @@ int main(int argc, char *argv[])
 	  {
 	    char *fileName = argv[argi - 1];
 	    char *endptr;
-	    char tmp[SIPP_MAX_MSG_SIZE];
 	    int field;
 
 	    if (inFiles.find(fileName) == inFiles.end()) {
@@ -4381,13 +4318,7 @@ int main(int argc, char *argv[])
 	      ERROR("Invalid field specification for -infindex: %s", argv[argi]);
 	    }
 
-	    infIndex[fileName] = new str_int_map;
-
-	    for (int line = 0; line < inFiles[fileName]->numLines(); line++) {
-	      inFiles[fileName]->getField(line, field, tmp, SIPP_MAX_MSG_SIZE);
-	      str_int_map *indmap = infIndex[fileName];
-	      indmap->insert(pair<str_int_map::key_type,int>(str_int_map::key_type(tmp), line));
-	    }
+	    inFiles[fileName]->index(field);
 	  }
 	  break;
 	case SIPP_OPTION_SETFLAG:
@@ -4482,6 +4413,9 @@ int main(int argc, char *argv[])
 	case SIPP_OPTION_LIMIT:
 	  REQUIRE_ARG();
 	  CHECK_PASS();
+	  if (users >= 0) {
+	    ERROR("Can not set open call limit (-l) when -users is specified.");
+	  }
 	  open_calls_allowed = get_long(argv[argi], argv[argi - 1]);
 	  open_calls_user_setting = 1;
 	  break;
@@ -4502,6 +4436,20 @@ int main(int argc, char *argv[])
 	  generic[generic_count++] = &argv[argi - 1];
 	  generic[generic_count] = NULL;
 	  break;
+	case SIPP_OPTION_VAR:
+	  REQUIRE_ARG();
+	  REQUIRE_ARG();
+	  CHECK_PASS();
+
+	  {
+	    int varId = globalVariables->find(argv[argi  - 1], false);
+	    if (varId == -1) {
+		globalVariables->dump();
+		ERROR("Can not set the global variable %s, because it does not exist.", argv[argi - 1]);
+	    }
+	    globalVariables->getVar(varId)->setString(strdup(argv[argi]));
+	  }
+	  break;
 	case SIPP_OPTION_3PCC:
 	  if(slave_masterSet){
 	    ERROR("-3PCC option is not compatible with -master and -slave options\n");
@@ -4519,10 +4467,14 @@ int main(int argc, char *argv[])
 	  REQUIRE_ARG();
 	  CHECK_PASS();
 	  if (!strcmp(argv[argi - 1], "-sf")) {
-	    main_scenario = new scenario(argv[argi], 0);
 	    scenario_file = new char [strlen(argv[argi])+1] ;
 	    sprintf(scenario_file,"%s", argv[argi]);
-	    main_scenario->stats->setFileName(argv[argi], (char*)".csv");
+	    scenario_file = remove_pattern (scenario_file, (char*)".xml");
+	    if (useLogf == 1) {
+	      rotate_logfile();
+	    }
+	    main_scenario = new scenario(argv[argi], 0);
+	    main_scenario->stats->setFileName(scenario_file, (char*)".csv");
 	  } else if (!strcmp(argv[argi - 1], "-sn")) {
 	    int i = find_scenario(argv[argi]);
 
@@ -4562,8 +4514,8 @@ int main(int argc, char *argv[])
 	  parse_slave_cfg();
 	  break;
 	case SIPP_OPTION_3PCC_EXTENDED:
-     REQUIRE_ARG();
-     CHECK_PASS();
+	  REQUIRE_ARG();
+	  CHECK_PASS();
 	  if(slave_masterSet){
 	    ERROR("-slave and -master options are not compatible\n");
 	  }
@@ -4621,6 +4573,17 @@ int main(int argc, char *argv[])
 	  freeaddrinfo(local_addr);
 	  break;
 	}
+	case SIPP_OPTION_RTCHECK:
+	  REQUIRE_ARG();
+	  CHECK_PASS();
+	  if (!strcmp(argv[argi], "full")) {
+	    *((int *)option->data) = RTCHECK_FULL;
+	  } else if (!strcmp(argv[argi], "loose")) {
+	    *((int *)option->data) = RTCHECK_LOOSE;
+	  } else {
+	    ERROR("Unknown retransmission detection method: %s\n", argv[argi]);
+	  }
+	  break;
 	case SIPP_OPTION_TDMMAP: {
           REQUIRE_ARG();
 	  CHECK_PASS();
@@ -4691,7 +4654,43 @@ int main(int argc, char *argv[])
 		}
 		token = NULL;
 	    }
-	    fprintf(stderr, "Defaults: %lu\n", *ptr);
+	    break;
+	  }
+	case SIPP_OPTION_LFNAME:
+	  REQUIRE_ARG();
+	  CHECK_PASS();
+	  ((struct logfile_info*)option->data)->fixedname = true;
+	  strcpy(((struct logfile_info*)option->data)->file_name, argv[argi]);
+	  break;
+	case SIPP_OPTION_LFOVERWRITE:
+	  REQUIRE_ARG();
+	  CHECK_PASS();
+	  ((struct logfile_info*)option->data)->fixedname = true;
+	  ((struct logfile_info*)option->data)->overwrite = get_bool(argv[argi], argv[argi-1]);
+	  break;
+	case SIPP_OPTION_PLUGIN: {
+	    void *handle;
+	    char *error;
+	    int (*init)();
+	    int ret;
+
+	    REQUIRE_ARG();
+	    CHECK_PASS();
+
+	    handle = dlopen(argv[argi], RTLD_NOW);
+	    if (!handle) {
+	      ERROR("Could not open plugin %s: %s", argv[argi], dlerror());
+	    }
+
+	    init = (int (*)())dlsym(handle, "init");
+	    if((error = (char *) dlerror())) {
+	      ERROR("Could not locate init function in %s: %s", argv[argi], dlerror());
+	    }
+
+	    ret = init();
+	    if (ret != 0) {
+	      ERROR("Plugin %s initialization failed.", argv[argi]);
+	    }
 	  }
 	  break;
 	default:
@@ -4723,8 +4722,6 @@ int main(int argc, char *argv[])
   if (scenario_file == NULL) {
     scenario_file = new char [ 5 ] ;
     sprintf(scenario_file, "%s", "sipp");
-  } else {
-    scenario_file = remove_pattern (scenario_file, (char*)".xml");
   }
 
    screen_init(print_last_stats);
@@ -4743,6 +4740,10 @@ int main(int argc, char *argv[])
   if (useShortMessagef == 1) {
     rotate_shortmessagef();
   }
+
+  if (useCallDebugf) {
+    rotate_calldebugf();
+  }
   
   if (useScreenf == 1) {
     char L_file_name [MAX_PATH];
@@ -4774,10 +4775,6 @@ int main(int argc, char *argv[])
     print_count_file(countf, 1);
   }
 
-  if (useLogf == 1) {
-    rotate_logfile();
-  }
-
   if (dumpInRtt == 1) {
      main_scenario->stats->initRtt((char*)scenario_file, (char*)".csv",
                                 report_freq_dumpRtt);
@@ -4797,7 +4794,7 @@ int main(int argc, char *argv[])
 
     if (rlimit.rlim_max >
 #ifndef __CYGWIN
-       ((L_maxSocketPresent) ?  max_multi_socket : FD_SETSIZE)
+       ((L_maxSocketPresent) ?  (unsigned int)max_multi_socket : FD_SETSIZE)
 #else
        FD_SETSIZE
 #endif
@@ -4808,13 +4805,13 @@ int main(int argc, char *argv[])
 
       rlimit.rlim_max =
 #ifndef __CYGWIN
-          (L_maxSocketPresent) ?  max_multi_socket+min_socket : FD_SETSIZE ;
+          (L_maxSocketPresent) ?  (unsigned int)max_multi_socket+min_socket : FD_SETSIZE ;
 #else
 
 	  FD_SETSIZE;
 #endif
     }
-    
+
     rlimit.rlim_cur = rlimit.rlim_max;
     if (setrlimit (RLIMIT_NOFILE, &rlimit) < 0) {
       ERROR("Unable to increase the open file limit to FD_SETSIZE = %d",
@@ -4843,6 +4840,17 @@ int main(int argc, char *argv[])
   if(argiFileName) {
     main_scenario->stats->setFileName(argv[argiFileName]);
   }
+ 
+  // setup option form cmd line
+  call::maxDynamicId   = maxDynamicId;
+  call::startDynamicId = startDynamicId;
+  call::dynamicId      = startDynamicId;
+  call::stepDynamicId  = stepDynamicId;
+
+
+  /* Now Initialize the scenarios. */
+  main_scenario->runInit();
+  ooc_scenario->runInit();
 
   /* In which mode the tool is launched ? */
   main_scenario->computeSippMode();
@@ -4876,17 +4884,27 @@ int main(int argc, char *argv[])
           exit(EXIT_OTHER);
         }
     }
-	 
+
+    sipp_usleep(sleeptime * 1000);
+
+  /* Create the statistics reporting task. */
+  stattask::initialize();
+  /* Create the screen update task. */
+  screentask::initialize();
+  /* Create a watchdog task. */
+  if (watchdog_interval) {
+    new watchdog(watchdog_interval, watchdog_reset, watchdog_major_threshold, watchdog_major_maxtriggers, watchdog_minor_threshold, watchdog_minor_maxtriggers);
+  }
+
   /* Setting the rate and its dependant params (open_calls_allowed) */
-  set_rate(rate);
-	 
-  if (toolMode == MODE_SERVER) {
-    reset_number = 0;
+  /* If we are a client, then create the task to open new calls. */
+  if (creationMode == MODE_CLIENT) {
+    opentask::initialize();
+    opentask::set_rate(rate);
   }
-   
+
   open_connections();
-   
-   
+
   /* Defaults for media sockets */
   if (media_ip[0] == '\0') {
       strcpy(media_ip, local_ip);
@@ -4919,7 +4937,10 @@ int main(int argc, char *argv[])
       ERROR("Unknown RTP address '%s'.\n"
                "Use 'sipp -h' for details", media_ip);
     }
-
+    
+    memset(&media_sockaddr,0,sizeof(struct sockaddr_storage));
+    media_sockaddr.ss_family = local_addr->ai_addr->sa_family;
+    
     memcpy(&media_sockaddr,
            local_addr->ai_addr,
            SOCK_ADDR_SIZE(
@@ -5024,7 +5045,7 @@ int main(int argc, char *argv[])
         == -1) {
       ERROR_NO("Unable to create second RTP echo thread");
       }
-    } 
+    }
 
   traffic_thread();
 
@@ -5097,7 +5118,7 @@ int open_connections() {
   local_port = 0;
   
   if(!strlen(remote_host)) {
-    if((toolMode != MODE_SERVER)) {
+    if((sendMode != MODE_SERVER)) {
       ERROR("Missing remote host parameter. This scenario requires it");
     }
   } else {
@@ -5185,12 +5206,10 @@ int open_connections() {
 	  get_inet_address(
 	    _RCAST(struct sockaddr_storage *, local_addr->ai_addr)));
     } else {
-      if (!(local_sockaddr.ss_family == AF_INET6)) {
-	memcpy(&local_sockaddr,
-	    local_addr->ai_addr,
-	    SOCK_ADDR_SIZE(
-	      _RCAST(struct sockaddr_storage *,local_addr->ai_addr)));
-      }
+          memcpy(&local_sockaddr,
+	  local_addr->ai_addr,
+	  SOCK_ADDR_SIZE(
+	  _RCAST(struct sockaddr_storage *,local_addr->ai_addr)));
     }
     freeaddrinfo(local_addr);
 
@@ -5325,7 +5344,7 @@ int open_connections() {
 
   // Create additional server sockets when running in socket per
   // IP address mode.
-  if (peripsocket && toolMode == MODE_SERVER) {
+  if (peripsocket && sendMode == MODE_SERVER) {
     struct sockaddr_storage server_sockaddr;
     struct addrinfo * local_addr;
     struct addrinfo   hints;
@@ -5379,7 +5398,7 @@ int open_connections() {
   }
 
   if((!multisocket) && (transport == T_TCP || transport == T_TLS) &&
-   (toolMode != MODE_SERVER)) {
+   (sendMode != MODE_SERVER)) {
     if((tcp_multiplex = new_sipp_socket(local_ip_is_ipv6, transport)) == NULL) {
       ERROR_NO("Unable to get a TCP socket");
     }
@@ -5419,26 +5438,26 @@ int open_connections() {
 
   /* Trying to connect to Twin Sipp in 3PCC mode */
   if(twinSippMode) {
-    if(toolMode == MODE_3PCC_CONTROLLER_A || toolMode == MODE_3PCC_A_PASSIVE) {
+    if(thirdPartyMode == MODE_3PCC_CONTROLLER_A || thirdPartyMode == MODE_3PCC_A_PASSIVE) {
        connect_to_peer(twinSippHost, twinSippPort, &twinSipp_sockaddr, twinSippIp, &twinSippSocket);
-     }else if(toolMode == MODE_3PCC_CONTROLLER_B){
+     }else if(thirdPartyMode == MODE_3PCC_CONTROLLER_B){
        connect_local_twin_socket(twinSippHost);
       }else{
-       ERROR("TwinSipp Mode enabled but toolMode is different "
+       ERROR("TwinSipp Mode enabled but thirdPartyMode is different "
               "from 3PCC_CONTROLLER_B and 3PCC_CONTROLLER_A\n");
       }
    }else if (extendedTwinSippMode){       
-     if (toolMode == MODE_MASTER || toolMode == MODE_MASTER_PASSIVE) {
+     if (thirdPartyMode == MODE_MASTER || thirdPartyMode == MODE_MASTER_PASSIVE) {
        strcpy(twinSippHost,get_peer_addr(master_name));
        get_host_and_port(twinSippHost, twinSippHost, &twinSippPort);
        connect_local_twin_socket(twinSippHost);
        connect_to_all_peers();
-     }else if(toolMode == MODE_SLAVE) {
+     }else if(thirdPartyMode == MODE_SLAVE) {
        strcpy(twinSippHost,get_peer_addr(slave_number));
        get_host_and_port(twinSippHost, twinSippHost, &twinSippPort);
        connect_local_twin_socket(twinSippHost);
      }else{
-        ERROR("extendedTwinSipp Mode enabled but toolMode is different "
+        ERROR("extendedTwinSipp Mode enabled but thirdPartyMode is different "
               "from MASTER and SLAVE\n");
      }
     }
@@ -5662,180 +5681,154 @@ void free_peer_addr_map() {
   }
 }
 
-#ifdef __cplusplus
-extern "C" {
-#endif
-int TRACE_MSG(char *fmt, ...) {
-  int ret = 0;
-  static unsigned long long count = 0;
-  if(messagef) {
-    va_list ap;
-    va_start(ap, fmt);
-    ret = vfprintf(messagef, fmt, ap);
-    va_end(ap);
-    fflush(messagef);
+void rotatef(struct logfile_info *lfi) {
+  char L_rotate_file_name [MAX_PATH];
 
-    count += ret;
+  if (!lfi->fixedname) {
+    sprintf (lfi->file_name, "%s_%d_%s.log", scenario_file, getpid(), lfi->name);
+  }
 
-    if (max_log_size && count > max_log_size) {
-      fclose(messagef);
-      messagef = NULL;
+  if (ringbuffer_files > 0) {
+    if (!lfi->ftimes) {
+	lfi->ftimes = (struct logfile_id *)calloc(ringbuffer_files, sizeof(struct logfile_id));
     }
-
-    if (ringbuffer_size && count > ringbuffer_size) {
-      rotate_messagef();
-      count = 0;
+    /* We need to rotate away an existing file. */
+    if (lfi->nfiles == ringbuffer_files) {
+      if ((lfi->ftimes)[0].n) {
+	sprintf(L_rotate_file_name, "%s_%d_%s_%lu.%d.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[0].start, (lfi->ftimes)[0].n);
+      } else {
+	sprintf(L_rotate_file_name, "%s_%d_%s_%lu.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[0].start);
+      }
+      unlink(L_rotate_file_name);
+      lfi->nfiles--;
+      memmove(lfi->ftimes, &((lfi->ftimes)[1]), sizeof(struct logfile_id) * (lfi->nfiles));
+    }
+    if (lfi->starttime) {
+      (lfi->ftimes)[lfi->nfiles].start = lfi->starttime;
+      (lfi->ftimes)[lfi->nfiles].n = 0;
+      /* If we have the same time, then we need to append an identifier. */
+      if (lfi->nfiles && ((lfi->ftimes)[lfi->nfiles].start == (lfi->ftimes)[lfi->nfiles - 1].start)) {
+	  (lfi->ftimes)[lfi->nfiles].n = (lfi->ftimes)[lfi->nfiles - 1].n + 1;
+      }
+      if ((lfi->ftimes)[lfi->nfiles].n) {
+	sprintf(L_rotate_file_name, "%s_%d_%s_%lu.%d.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[lfi->nfiles].start, (lfi->ftimes)[lfi->nfiles].n);
+      } else {
+	sprintf(L_rotate_file_name, "%s_%d_%s_%lu.log", scenario_file, getpid(), lfi->name, (lfi->ftimes)[lfi->nfiles].start);
+      }
+      lfi->nfiles++;
+      fflush(lfi->fptr);
+      fclose(lfi->fptr);
+      lfi->fptr = NULL;
+      rename(lfi->file_name, L_rotate_file_name);
     }
   }
-  return ret;
+
+  time(&lfi->starttime);
+  if (lfi->overwrite) {
+    lfi->fptr = fopen(lfi->file_name, "w");
+  } else {
+    lfi->fptr = fopen(lfi->file_name, "a");
+    lfi->overwrite = true;
+  }
+  if(lfi->check && !lfi->fptr) {
+    /* We can not use the error functions from this function, as we may be rotating the error log itself! */
+    ERROR("Unable to create '%s'", lfi->file_name);
+  }
 }
 
-int TRACE_SHORTMSG(char *fmt, ...) {
-  int ret = 0;
-  static unsigned long long count = 0;
-  if(shortmessagef) {
-    va_list ap;
-    va_start(ap, fmt);
-    ret = vfprintf(shortmessagef, fmt, ap);
-    va_end(ap);
-    fflush(shortmessagef);
+void rotate_calldebugf() {
+  rotatef(&calldebug_lfi);
+}
 
-    count += ret;
+void rotate_messagef() {
+  rotatef(&message_lfi);
+}
 
-    if (max_log_size && count > max_log_size) {
-      fclose(shortmessagef);
-      shortmessagef = NULL;
-    }
 
-    if (ringbuffer_size && count > ringbuffer_size) {
-      rotate_shortmessagef();
-      count = 0;
-    }
-  }
-  return ret;
+void rotate_shortmessagef() {
+  rotatef(&shortmessage_lfi);
 }
 
-int LOG_MSG(char *fmt, ...) {
+
+void rotate_logfile() {
+  rotatef(&log_lfi);
+}
+
+void rotate_errorf() {
+  rotatef(&error_lfi);
+  strcpy(screen_logfile, error_lfi.file_name);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int _trace (struct logfile_info *lfi, char *fmt, va_list ap) {
   int ret = 0;
-  static unsigned long long count = 0;
-  if(logfile) {
-    va_list ap;
-    va_start(ap, fmt);
-    ret = vfprintf(logfile, fmt, ap);
-    va_end(ap);
-    fflush(logfile);
+  if(lfi->fptr) {
+    ret = vfprintf(lfi->fptr, fmt, ap);
+    fflush(lfi->fptr);
 
-    count += ret;
+    lfi->count += ret;
 
-    if (max_log_size && count > max_log_size) {
-      fclose(logfile);
-      logfile = NULL;
+    if (max_log_size && lfi->count > max_log_size) {
+      fclose(lfi->fptr);
+      lfi->fptr = NULL;
     }
 
-    if (ringbuffer_size && count > ringbuffer_size) {
-      rotate_messagef();
-      count = 0;
+    if (ringbuffer_size && lfi->count > ringbuffer_size) {
+      rotatef(lfi);
+      lfi->count = 0;
     }
   }
   return ret;
 }
 
-// TODO: finish the -trace_timeout option implementation
-/* int TRACE_TIMEOUT(char *fmt, ...) */
 
-#ifdef __cplusplus
-}
-#endif
+int TRACE_MSG(char *fmt, ...) {
+  int ret;
+  va_list ap;
 
-struct logfile_id {
-  time_t start;
-  int n;
-};
+  va_start(ap, fmt);
+  ret = _trace(&message_lfi, fmt, ap);
+  va_end(ap);
 
-/* We can not use the error functions from this file, as we may be rotating the error log itself! */
-void rotatef(char *name, FILE **fptr, time_t *starttime, int *nfiles, struct logfile_id **ftimes, bool check, bool *overwrite) {
-  char L_file_name [MAX_PATH];
-  char L_rotate_file_name [MAX_PATH];
+  return ret;
+}
 
-  sprintf (L_file_name, "%s_%d_%s.log", scenario_file, getpid(), name);
+int TRACE_SHORTMSG(char *fmt, ...) {
+  int ret;
+  va_list ap;
 
-  if (ringbuffer_files > 0) {
-    if (!*ftimes) {
-	*ftimes = (struct logfile_id *)calloc(ringbuffer_files, sizeof(struct logfile_id));
-    }
-    /* We need to rotate away an existing file. */
-    if (*nfiles == ringbuffer_files) {
-      if ((*ftimes)[0].n) {
-	sprintf(L_rotate_file_name, "%s_%d_%s_%d.%d.log", scenario_file, getpid(), name, (*ftimes)[0].start, (*ftimes)[0].n);
-      } else {
-	sprintf(L_rotate_file_name, "%s_%d_%s_%d.log", scenario_file, getpid(), name, (*ftimes)[0].start);
-      }
-      unlink(L_rotate_file_name);
-      (*nfiles)--;
-      memmove(*ftimes, &((*ftimes)[1]), sizeof(struct logfile_id) * (*nfiles));
-    }
-    if (*starttime) {
-      (*ftimes)[*nfiles].start = *starttime;
-      (*ftimes)[*nfiles].n = 0;
-      /* If we have the same time, then we need to append an identifier. */
-      if (*nfiles && ((*ftimes)[*nfiles].start == (*ftimes)[*nfiles - 1].start)) {
-	  (*ftimes)[*nfiles].n = (*ftimes)[*nfiles - 1].n + 1;
-      }
-      if ((*ftimes)[*nfiles].n) {
-	sprintf(L_rotate_file_name, "%s_%d_%s_%d.%d.log", scenario_file, getpid(), name, (*ftimes)[*nfiles].start, (*ftimes)[*nfiles].n);
-      } else {
-	sprintf(L_rotate_file_name, "%s_%d_%s_%d.log", scenario_file, getpid(), name, (*ftimes)[*nfiles].start);
-      }
-      (*nfiles)++;
-      fflush(*fptr);
-      fclose(*fptr);
-      *fptr = NULL;
-      rename(L_file_name, L_rotate_file_name);
-    }
-  }
+  va_start(ap, fmt);
+  ret = _trace(&shortmessage_lfi, fmt, ap);
+  va_end(ap);
 
-  time(starttime);
-  if (*overwrite) {
-    *fptr = fopen(L_file_name, "w");
-  } else {
-    *fptr = fopen(L_file_name, "a");
-    *overwrite = true;
-  }
-  if(check && !*fptr) {
-    ERROR("Unable to create '%s'", L_file_name);
-  }
+  return ret;
 }
 
-int messagef_nfiles = 0;
-struct logfile_id *messagef_times = NULL;
-
-void rotate_messagef() {
-  static time_t starttime = 0;
-  rotatef("messages", &messagef, &starttime, &messagef_nfiles, &messagef_times, true, &messagef_overwrite);
-}
+int LOG_MSG(char *fmt, ...) {
+  int ret;
+  va_list ap;
 
-int shortmessagef_nfiles = 0;
-struct logfile_id *shortmessagef_times = NULL;
+  va_start(ap, fmt);
+  ret = _trace(&log_lfi, fmt, ap);
+  va_end(ap);
 
-void rotate_shortmessagef() {
-  static time_t starttime = 0;
-  rotatef("shortmessages", &shortmessagef, &starttime, &shortmessagef_nfiles, &shortmessagef_times, true, &shortmessagef_overwrite);
+  return ret;
 }
 
-int logfile_nfiles = 0;
-struct logfile_id *logfile_times = NULL;
+int TRACE_CALLDEBUG(char *fmt, ...) {
+  int ret;
+  va_list ap;
 
-void rotate_logfile() {
-  static time_t starttime = 0;
-  rotatef("logs", &logfile, &starttime, &logfile_nfiles, &logfile_times, true, &logfile_overwrite);
-}
+  va_start(ap, fmt);
+  ret = _trace(&calldebug_lfi, fmt, ap);
+  va_end(ap);
 
-int errorf_nfiles  = 0;
-struct logfile_id *errorf_times = NULL;
+  return ret;
+}
 
-void rotate_errorf() {
-  static time_t starttime = 0;
-  rotatef("errors", &screen_errorf, &starttime, &errorf_nfiles, &errorf_times, false, &errorf_overwrite);
-  /* If rotatef is changed, this must be changed as well. */
-  sprintf (screen_logfile, "%s_%d_errors.log", scenario_file, getpid());
+#ifdef __cplusplus
 }
+#endif
+
diff --git a/sipp.hpp b/sipp.hpp
index 7466c85..ec1e8ef 100644
--- a/sipp.hpp
+++ b/sipp.hpp
@@ -42,6 +42,7 @@
 #include <ctype.h>
 #include <signal.h>
 #include <time.h>
+#include <limits.h>
 #include <vector>
 #include <string>
 #include <map>
@@ -67,10 +68,13 @@
 #include "socketowner.hpp"
 #include "call.hpp"
 #include "comp.h"
+#include "variables.hpp"
 #include "stat.hpp"
 #include "actions.hpp"
-#include "variables.hpp"
 #include "infile.hpp"
+#include "opentask.hpp"
+#include "reporttask.hpp"
+#include "watchdog.hpp"
 /* Open SSL stuff */
 #ifdef _USE_OPENSSL
 #include "sslcommon.h" 
@@ -184,23 +188,25 @@ extern int	          rate_max	          _DEFVAL(0);
 extern bool	          rate_quit		  _DEFVAL(true);
 extern int                users                   _DEFVAL(-1);
 extern int               rate_period_ms           _DEFVAL(DEFAULT_RATE_PERIOD_MS);
+extern int                sleeptime               _DEFVAL(0);
 extern unsigned long      defl_recv_timeout       _DEFVAL(0);
 extern unsigned long      defl_send_timeout       _DEFVAL(0);
 extern unsigned long      global_timeout          _DEFVAL(0);
 extern int                transport               _DEFVAL(DEFAULT_TRANSPORT);
 extern bool               retrans_enabled         _DEFVAL(1);
+extern int                rtcheck	          _DEFVAL(RTCHECK_FULL);
 extern int                max_udp_retrans         _DEFVAL(UDP_MAX_RETRANS);
 extern int                max_invite_retrans      _DEFVAL(UDP_MAX_RETRANS_INVITE_TRANSACTION);
 extern int                max_non_invite_retrans  _DEFVAL(UDP_MAX_RETRANS_NON_INVITE_TRANSACTION);
 extern unsigned long      default_behaviors       _DEFVAL(DEFAULT_BEHAVIOR_ALL);
 extern unsigned long	  deadcall_wait		  _DEFVAL(DEFAULT_DEADCALL_WAIT);
 extern bool               pause_msg_ign           _DEFVAL(0);
-extern int                auto_answer             _DEFVAL(0);
+extern bool               auto_answer             _DEFVAL(false);
 extern int                multisocket             _DEFVAL(0);
 extern int                compression             _DEFVAL(0);
 extern int                peripsocket             _DEFVAL(0);
 extern int                peripfield              _DEFVAL(0);
-extern int                bind_local              _DEFVAL(0);
+extern bool               bind_local              _DEFVAL(false);
 extern void             * monosocket_comp_state   _DEFVAL(0);
 extern char             * service                 _DEFVAL(DEFAULT_SERVICE);
 extern char             * auth_password           _DEFVAL(DEFAULT_AUTH_PASSWORD);
@@ -211,11 +217,12 @@ extern bool		  periodic_rtd		  _DEFVAL(false);
 extern char		* stat_delimiter          _DEFVAL(";");
 
 extern bool               timeout_exit            _DEFVAL(false);
+extern bool               timeout_error           _DEFVAL(false);
 
 extern unsigned long      report_freq_dumpRtt     _DEFVAL
                                                 (DEFAULT_FREQ_DUMP_RTT);
 
-extern unsigned int       max_multi_socket        _DEFVAL
+extern int		  max_multi_socket        _DEFVAL
                                                 (DEFAULT_MAX_MULTI_SOCKET);
 extern bool		  skip_rlimit		  _DEFVAL(false);
 
@@ -245,11 +252,11 @@ extern char               remote_ip[40];
 extern char               remote_ip_escaped[42];
 extern int                remote_port             _DEFVAL(DEFAULT_PORT);
 extern unsigned int       pid                     _DEFVAL(0);
-extern int                print_all_responses     _DEFVAL(0);
+extern bool               print_all_responses     _DEFVAL(false);
 extern unsigned long      stop_after              _DEFVAL(0xffffffff);
 extern int                quitting                _DEFVAL(0);
 extern int                interrupt               _DEFVAL(0);
-extern int                paused                  _DEFVAL(0);
+extern bool               paused                  _DEFVAL(false);
 extern int                lose_packets            _DEFVAL(0);
 extern double             global_lost             _DEFVAL(0.0);
 extern char               remote_host[255]; 
@@ -297,7 +304,6 @@ extern char                 *tls_crl_name      _DEFVAL(DEFAULT_TLS_CRL)  ;
 typedef std::map<string, FileContents *> file_map;
 extern file_map inFiles;
 typedef std::map<string, str_int_map *> file_index;
-extern file_index infIndex;
 extern char *ip_file _DEFVAL(NULL);
 extern char *default_file _DEFVAL(NULL);
 
@@ -353,7 +359,6 @@ extern int           last_running_calls           _DEFVAL(0);
 extern int           last_woken_calls             _DEFVAL(0);
 extern int           last_paused_calls            _DEFVAL(0);
 extern unsigned int  open_calls_allowed           _DEFVAL(0);
-extern unsigned long last_rate_change_time        _DEFVAL(0);
 extern unsigned long last_report_time             _DEFVAL(0);
 extern unsigned long last_dump_time               _DEFVAL(0);
 
@@ -363,6 +368,21 @@ extern unsigned long clock_tick                   _DEFVAL(0);
 extern unsigned long scheduling_loops             _DEFVAL(0);
 extern unsigned long last_timer_cycle             _DEFVAL(0);
 
+extern unsigned long watchdog_interval		  _DEFVAL(400);
+extern unsigned long watchdog_minor_threshold	  _DEFVAL(500);
+extern unsigned long watchdog_minor_maxtriggers	  _DEFVAL(120);
+extern unsigned long watchdog_major_threshold	  _DEFVAL(3000);
+extern unsigned long watchdog_major_maxtriggers	  _DEFVAL(10);
+extern unsigned long watchdog_reset		  _DEFVAL(600000);
+
+
+/********************* dynamic Id ************************* */
+extern  int maxDynamicId    _DEFVAL(12000);  // max value for dynamicId; this value is reached 
+extern  int startDynamicId  _DEFVAL(10000);  // offset for first dynamicId  FIXME:in CmdLine
+extern  int stepDynamicId   _DEFVAL(4);      // step of increment for dynamicId
+
+
+
 #define GET_TIME(clock)       \
 {                             \
   struct timezone tzp;        \
@@ -371,8 +391,9 @@ extern unsigned long last_timer_cycle             _DEFVAL(0);
 
 /*********************** Global Sockets  **********************/
 
-extern struct sipp_socket *main_socket            _DEFVAL(0);
-extern struct sipp_socket *tcp_multiplex          _DEFVAL(0);
+extern struct sipp_socket *main_socket            _DEFVAL(NULL);
+extern struct sipp_socket *main_remote_socket     _DEFVAL(NULL);
+extern struct sipp_socket *tcp_multiplex          _DEFVAL(NULL);
 extern int           media_socket                 _DEFVAL(0);
 extern int           media_socket_video           _DEFVAL(0);
 
@@ -383,7 +404,7 @@ extern char          hostname[80];
 extern bool          is_ipv6                      _DEFVAL(false);
 
 extern int           reset_number                 _DEFVAL(0);
-extern int	     reset_close                  _DEFVAL(1);
+extern bool	     reset_close                  _DEFVAL(true);
 extern int	     reset_sleep                  _DEFVAL(1000);
 extern bool	     sendbuffer_warn              _DEFVAL(false);
 /* A list of sockets pending reset. */
@@ -427,20 +448,13 @@ enum E_Alter_YesNo
 /************************** Trace Files ***********************/
 
 extern FILE * screenf                             _DEFVAL(0);
-extern FILE * logfile                             _DEFVAL(0);
-extern FILE * messagef                            _DEFVAL(0);
-extern FILE * shortmessagef                       _DEFVAL(0);
 extern FILE * countf                              _DEFVAL(0);
 // extern FILE * timeoutf                            _DEFVAL(0);
 extern bool   useMessagef                         _DEFVAL(0);
+extern bool   useCallDebugf                       _DEFVAL(0);
 extern bool   useShortMessagef                    _DEFVAL(0);
 extern bool   useScreenf                          _DEFVAL(0);
 extern bool   useLogf                             _DEFVAL(0);
-// should we overwrite the existing files?
-extern bool   messagef_overwrite		  _DEFVAL(true);
-extern bool   shortmessagef_overwrite		  _DEFVAL(true);
-extern bool   errorf_overwrite			  _DEFVAL(true);
-extern bool   logfile_overwrite			  _DEFVAL(true);
 //extern bool   useTimeoutf                         _DEFVAL(0);
 extern bool   dumpInFile                          _DEFVAL(0);
 extern bool   dumpInRtt                           _DEFVAL(0);
@@ -454,13 +468,46 @@ extern int    ringbuffer_files			  _DEFVAL(0);
 
 extern char   screen_last_error[32768];
 extern char   screen_logfile[MAX_PATH]            _DEFVAL("");
-extern FILE   * screen_errorf			  _DEFVAL(NULL);
 
-void rotate_messagef();
-void rotate_shortmessagef();
-void rotate_logfile();
+/* Log Rotation Functions. */
+struct logfile_id {
+  time_t start;
+  int n;
+};
+
+struct logfile_info {
+	char *name;
+	bool check;
+	FILE *fptr;
+	int nfiles;
+	struct logfile_id *ftimes;
+	char file_name[MAX_PATH];
+	bool overwrite;
+	bool fixedname;
+	time_t starttime;
+	unsigned int count;
+};
+
+#ifdef GLOBALS_FULL_DEFINITION
+#define LOGFILE(name, s, check) \
+	struct logfile_info name = { s, check, NULL, 0, NULL, "", true, false, 0, 0};
+#else
+#define LOGFILE(name, s, check) \
+	extern struct logfile_info name;
+#endif
+LOGFILE(calldebug_lfi, "calldebug", true);
+LOGFILE(message_lfi, "messages", true);
+LOGFILE(shortmessage_lfi, "shortmessages", true);
+LOGFILE(log_lfi, "logs", true);
+LOGFILE(error_lfi, "errors", false);
+
 void rotate_errorf();
 
+/* Screen/Statistics Printing Functions. */
+void print_statistics(int last);
+void print_count_file(FILE *f, int header);
+
+
 /********************* Mini-Parser Routines *******************/
 
 int get_method(char *msg);
@@ -501,6 +548,7 @@ struct sipp_socket {
 	bool ss_ipv6;
 	bool ss_control; /* Is this a control socket? */
 	bool ss_call_socket; /* Is this a call socket? */
+	bool ss_changed_dest; /* Has the destination changed from default. */
 
 	int ss_fd;	/* The underlying file descriptor for this socket. */
 	void *ss_comp_state; /* The compression state. */
@@ -583,6 +631,7 @@ void free_peer_addr_map();
 extern "C" {
 #endif
 int TRACE_MSG(char *fmt, ...);
+int TRACE_CALLDEBUG(char *fmt, ...);
 int TRACE_SHORTMSG(char *fmt, ...);
 int LOG_MSG(char *fmt, ...);
 #ifdef __cplusplus
diff --git a/socketowner.cpp b/socketowner.cpp
index 0976880..3b928b5 100644
--- a/socketowner.cpp
+++ b/socketowner.cpp
@@ -72,7 +72,9 @@ socketowner::socketowner() {
 }
 
 socketowner::~socketowner() {
-  sipp_close_socket(dissociate_socket());
+  if (this->call_socket) {
+    sipp_close_socket(dissociate_socket());
+  }
 }
 
 void socketowner::add_owner_to_socket(struct sipp_socket *socket) {
diff --git a/stat.cpp b/stat.cpp
index 2eef980..0540934 100644
--- a/stat.cpp
+++ b/stat.cpp
@@ -82,12 +82,33 @@
 #define DISPLAY_INFO(T1)\
   fprintf(f,"  %-77.77s \r\n", T1)
 #define DISPLAY_REPART(T1, T2, V1)\
-  fprintf(f,"    %8d ms <= n <  %8d ms : %10d  %-29.29s \r\n", T1, T2, V1, "")
+  fprintf(f,"    %8d ms <= n <  %8d ms : %10lu  %-29.29s \r\n", T1, T2, V1, "")
 #define DISPLAY_LAST_REPART(T1, V1)\
-  fprintf(f,"    %14.14s n >= %8d ms : %10d  %-29.29s \r\n", "", T1, V1, "")
+  fprintf(f,"    %14.14s n >= %8d ms : %10lu  %-29.29s \r\n", "", T1, V1, "")
 
 #define RESET_COUNTERS(PT)\
-  memset (PT, 0, CStat::E_NB_G_COUNTER * sizeof(unsigned long long))
+  memset (PT, 0, CStat::E_NB_COUNTER * sizeof(unsigned long long))
+
+#define RESET_C_COUNTERS                          \
+{                                                      \
+  int i;                                               \
+  for (i=CStat::CPT_G_C_OutOfCallMsgs;            \
+       i<=CStat::CPT_G_C_AutoAnswered;               \
+       i++)                                            \
+    M_G_counters[i - E_NB_COUNTER - 1] = (unsigned long) 0;                         \
+  for (i=CStat::CPT_C_IncomingCallCreated;            \
+       i<=CStat::CPT_C_Retransmissions;               \
+       i++)                                            \
+    M_counters[i] = (unsigned long) 0;                         \
+  for (unsigned int j=0;j<M_genericMap.size(); j++) { \
+	M_genericCounters[j * GENERIC_TYPES + GENERIC_C] = 0; \
+  } \
+  for (unsigned int j=0;j<M_rtdMap.size(); j++) { \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT] = 0; \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] = 0; \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] = 0; \
+  } \
+}
 
 #define RESET_PD_COUNTERS                          \
 {                                                      \
@@ -100,6 +121,14 @@
        i<=CStat::CPT_PD_Retransmissions;               \
        i++)                                            \
     M_counters[i] = (unsigned long) 0;                         \
+  for (unsigned int j=0;j<M_genericMap.size(); j++) { \
+	M_genericCounters[j * GENERIC_TYPES + GENERIC_PD] = 0; \
+  } \
+  for (unsigned int j=0;j<M_rtdMap.size(); j++) { \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT] = 0; \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] = 0; \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] = 0; \
+  } \
 }
 
 #define RESET_PL_COUNTERS                          \
@@ -113,6 +142,14 @@
        i<=CStat::CPT_PL_Retransmissions;               \
        i++)                                            \
     M_counters[i] = (unsigned long) 0;                         \
+  for (unsigned int j=0;j<M_genericMap.size(); j++) { \
+	M_genericCounters[j * GENERIC_TYPES + GENERIC_PL] = 0; \
+  } \
+  for (unsigned int j=0;j<M_rtdMap.size(); j++) { \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT] = 0; \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] = 0; \
+	M_rtdInfo[(j * GENERIC_TYPES * RTD_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] = 0; \
+  } \
 }
 
 /*
@@ -128,11 +165,12 @@ CStat::~CStat()
 {
   int i;
 
-  for (i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
+  for (i = 0; i < nRtds(); i++) {
     if (M_ResponseTimeRepartition[i] != NULL) {
       delete [] M_ResponseTimeRepartition[i];
     }
   }
+  free(M_ResponseTimeRepartition);
 
   if (M_CallLengthRepartition != NULL)
     delete [] M_CallLengthRepartition;
@@ -158,6 +196,10 @@ CStat::~CStat()
    if(M_dumpRespTime != NULL)
      delete [] M_dumpRespTime ;
 
+  free(M_rtdInfo);
+  for (int_str_map::iterator i = M_revRtdMap.begin(); i != M_revRtdMap.end(); ++i) {
+    free(i->second);
+  }
 
   M_SizeOfResponseTimeRepartition = 0;
   M_SizeOfCallLengthRepartition   = 0;
@@ -437,7 +479,7 @@ void CStat::setRepartitionResponseTime (char * P_listeStr)
   int sizeOfListe;
   int i;
 
-  for (i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
+  for (i = 0; i < nRtds(); i++) {
     if(createIntegerTable(P_listeStr, &listeInteger, &sizeOfListe) == 1) {
       initRepartition(listeInteger,
 	  sizeOfListe,
@@ -464,7 +506,7 @@ void CStat::setRepartitionCallLength(unsigned int* repartition,
 void CStat::setRepartitionResponseTime(unsigned int* repartition, 
                                        int nombre)
 {
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
+  for (int i = 0; i < nRtds(); i++) {
     initRepartition(repartition,
                     nombre,
                     &M_ResponseTimeRepartition[i],
@@ -628,6 +670,12 @@ int CStat::computeStat (E_Action P_action)
       M_counters [CPT_PL_FailedCallRegexpDoesntMatch]++;
       break;
 
+    case E_FAILED_REGEXP_SHOULDNT_MATCH :
+      M_counters [CPT_C_FailedCallRegexpShouldntMatch]++;
+      M_counters [CPT_PD_FailedCallRegexpShouldntMatch]++;
+      M_counters [CPT_PL_FailedCallRegexpShouldntMatch]++;
+      break;
+
     case E_FAILED_REGEXP_HDR_NOT_FOUND :
       M_counters [CPT_C_FailedCallRegexpHdrNotFound]++;
       M_counters [CPT_PD_FailedCallRegexpHdrNotFound]++;
@@ -658,6 +706,11 @@ int CStat::computeStat (E_Action P_action)
       M_counters [CPT_PL_Retransmissions]++;
       break;
 
+    case E_RESET_C_COUNTERS :
+      RESET_C_COUNTERS;
+      GET_TIME (&M_startTime);
+      break;
+
     case E_RESET_PD_COUNTERS :
       //DEBUG (C_Debug::E_LEVEL_4, "ENTER CASE", "%s", 
       //       "CStat::computeStat : RESET_PD_COUNTERS");
@@ -672,7 +725,7 @@ int CStat::computeStat (E_Action P_action)
       GET_TIME (&M_plStartTime);
       if (periodic_rtd) {
 	resetRepartition(M_CallLengthRepartition, M_SizeOfCallLengthRepartition);
-	for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
+	for (int i = 0; i < nRtds(); i++) {
 	  resetRepartition(M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition);
 	}
       }
@@ -694,12 +747,36 @@ int CStat::globalStat (E_Action P_action) {
       M_G_counters [CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1]++;
       break;
 
+    case E_WATCHDOG_MAJOR :
+      M_G_counters [CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PD_WatchdogMajor - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1]++;
+      break;
+
+    case E_WATCHDOG_MINOR :
+      M_G_counters [CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PD_WatchdogMinor - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1]++;
+      break;
+
     case E_DEAD_CALL_MSGS :
       M_G_counters [CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1]++;
       M_G_counters [CPT_G_PD_DeadCallMsgs - E_NB_COUNTER - 1]++;
       M_G_counters [CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1]++;
       break;
 
+    case E_FATAL_ERRORS :
+      M_G_counters [CPT_G_C_FatalErrors - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PD_FatalErrors - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PL_FatalErrors - E_NB_COUNTER - 1]++;
+      break;
+
+    case E_WARNING :
+      M_G_counters [CPT_G_C_Warnings - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PD_Warnings - E_NB_COUNTER - 1]++;
+      M_G_counters [CPT_G_PL_Warnings - E_NB_COUNTER - 1]++;
+      break;
+
     case E_AUTO_ANSWERED :
       // Let's count the automatic answered calls
       M_G_counters [CPT_G_C_AutoAnswered - 1]++;
@@ -747,7 +824,7 @@ void CStat::getStartTime(struct timeval *t)
 double CStat::computeStdev(E_CounterName P_SumCounter,
 			 E_CounterName P_NbOfCallUsed,
 			 E_CounterName P_Squares) {
-  if (M_counters[P_NbOfCallUsed] == 0)
+  if (M_counters[P_NbOfCallUsed] <= 0)
     return 0.0;
 
   double numerator = ((double)(M_counters[P_NbOfCallUsed]) * (double)(M_counters[P_Squares])) - ((double)(M_counters[P_SumCounter] * M_counters[P_SumCounter]));
@@ -763,6 +840,29 @@ double CStat::computeMean(E_CounterName P_SumCounter,
   return ((double)(M_counters[P_SumCounter]) / (double)(M_counters[P_NbOfCallUsed]));
 }
 
+double CStat::computeRtdMean(int which, int type) {
+  unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) +  RTD_COUNT];
+  unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) +  RTD_SUM];
+
+  if (count == 0)
+    return 0.0;
+  return ((double)(sum) / (double)(count));
+}
+
+double CStat::computeRtdStdev(int which, int type) {
+  unsigned long long count = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) +  RTD_COUNT];
+  unsigned long long sum = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) +  RTD_SUM];
+  unsigned long long sumsq = M_rtdInfo[((which - 1) * RTD_TYPES * GENERIC_TYPES) + (type * RTD_TYPES) +  RTD_SUMSQ];
+
+  if (count <= 1)
+    return 0.0;
+
+  double numerator = ((double)count * (double)sumsq) - (double)(sum * sum);
+  double denominator = (double)(count) * ((double)(count) - 1.0);
+
+  return sqrt(numerator/denominator);
+}
+
 void CStat::updateAverageCounter(E_CounterName P_SumCounter,
                                  E_CounterName P_NbOfCallUsed,
                                  E_CounterName P_Squares,
@@ -787,6 +887,109 @@ int CStat::computeStat (E_Action P_action,
   return computeStat(P_action, P_value, 0);
 }
 
+int CStat::findCounter(const char *counter, bool alloc) {
+  str_int_map::iterator it = M_genericMap.find(str_int_map::key_type(counter));
+  if (it != M_genericMap.end()) {
+    return it->second;
+  }
+  if (!alloc) {
+    return -1;
+  }
+  int ret = M_genericMap.size() + 1;
+  M_genericMap[str_int_map::key_type(counter)] = ret;
+
+  bool numeric = true;
+  const char *p = counter;
+  while (*p) {
+	if (!isdigit(*p)) {
+	  numeric = false;
+	  break;
+	}
+	p++;
+  }
+  if (numeric) {
+    char *s = new char[20];
+    snprintf(s, 20, "GenericCounter%s", counter);
+    M_revGenericMap[ret] = s;
+    M_genericDisplay[ret] = strdup(counter);
+  } else {
+    M_revGenericMap[ret] = strdup(counter);
+    M_genericDisplay[ret] = strdup(counter);
+  }
+
+
+  M_genericCounters = (unsigned long long *)realloc(M_genericCounters, sizeof(unsigned long long) * GENERIC_TYPES * M_genericMap.size());
+  if (!M_genericCounters) {
+    ERROR("Could not allocate generic counters!\n");
+  }
+  M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_C] = 0;
+  M_genericCounters[(ret - 1) * GENERIC_TYPES + GENERIC_PD] = 0;
+  M_genericCounters[(ret - 1)* GENERIC_TYPES + GENERIC_PL] = 0;
+
+  return ret;
+}
+
+int CStat::findRtd(const char *name, bool start) {
+  str_int_map::iterator it = M_rtdMap.find(str_int_map::key_type(name));
+  if (it != M_rtdMap.end()) {
+    if (start) {
+      rtd_started[it->first] = true;
+    } else {
+      rtd_stopped[it->first] = true;
+    }
+    return it->second;
+  }
+
+  int ret = M_rtdMap.size() + 1;
+  M_rtdMap[str_int_map::key_type(name)] = ret;
+
+  M_revRtdMap[ret] = strdup(name);
+
+
+  M_rtdInfo = (unsigned long long *)realloc(M_rtdInfo, sizeof(unsigned long long) * RTD_TYPES * GENERIC_TYPES * M_rtdMap.size());
+  if (!M_rtdInfo) {
+    ERROR("Could not allocate RTD info!\n");
+  }
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) +  RTD_COUNT] = 0;
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) +  RTD_SUM] = 0;
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) +  RTD_SUMSQ] = 0;
+
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) +  RTD_COUNT] = 0;
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) +  RTD_SUM] = 0;
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) +  RTD_SUMSQ] = 0;
+
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) +  RTD_COUNT] = 0;
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) +  RTD_SUM] = 0;
+  M_rtdInfo[((ret - 1) * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) +  RTD_SUMSQ] = 0;
+
+  M_ResponseTimeRepartition = (T_dynamicalRepartition **)realloc(M_ResponseTimeRepartition, sizeof(T_dynamicalRepartition *) * M_rtdMap.size());
+  if (!M_ResponseTimeRepartition) {
+    ERROR("Could not allocate RTD info!\n");
+  }
+  M_ResponseTimeRepartition[ret - 1] = NULL;
+
+  if (start) {
+    rtd_started[name] = true;
+  } else {
+    rtd_stopped[name] = true;
+  }
+  return ret;
+}
+
+int CStat::nRtds() {
+  return M_rtdMap.size();
+}
+
+/* If you start an RTD, then you should be interested in collecting statistics for it. */
+void CStat::validateRtds() {
+  for (str_int_map::iterator it = rtd_started.begin(); it != rtd_started.end(); it++) {
+    str_int_map::iterator stopit = rtd_stopped.find(it->first);
+    if (stopit == rtd_stopped.end() || !stopit->second) {
+      ERROR("You have started Response Time Duration %s, but have never stopped it!", it->first.c_str());
+    }
+  }
+}
+
 int CStat::computeStat (E_Action P_action,
                         unsigned long P_value,
 			int which)
@@ -812,26 +1015,27 @@ int CStat::computeStat (E_Action P_action,
 
 
     case E_ADD_GENERIC_COUNTER :
-      M_counters [CPT_C_Generic + which] += P_value;
-      M_counters [CPT_PD_Generic + which] += P_value;
-      M_counters [CPT_PL_Generic + which] += P_value;
+      M_genericCounters[which * GENERIC_TYPES + GENERIC_C] += P_value;
+      M_genericCounters[which * GENERIC_TYPES + GENERIC_PD] += P_value;
+      M_genericCounters[which * GENERIC_TYPES + GENERIC_PL] += P_value;
       break;
 
     case E_ADD_RESPONSE_TIME_DURATION :
       // Updating Cumulative Counter
-      updateAverageCounter((E_CounterName)(CPT_C_AverageResponseTime_Sum + which),
-                           (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + which),
-                           (E_CounterName)(CPT_C_AverageResponseTime_Squares + which), P_value);
-      updateRepartition(M_ResponseTimeRepartition[which], 
-                        M_SizeOfResponseTimeRepartition, P_value);
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_COUNT]++;
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUM] += P_value;
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_C * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value);
+      updateRepartition(M_ResponseTimeRepartition[which], M_SizeOfResponseTimeRepartition, P_value);
+
       // Updating Periodical Diplayed counter
-      updateAverageCounter((E_CounterName)(CPT_PD_AverageResponseTime_Sum + which),
-                           (E_CounterName)(CPT_PD_NbOfCallUsedForAverageResponseTime + which),
-                           (E_CounterName)(CPT_PD_AverageResponseTime_Squares + which), P_value);
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_COUNT]++;
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUM] += P_value;
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PD * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value);
+
       // Updating Periodical Logging counter
-      updateAverageCounter((E_CounterName)(CPT_PL_AverageResponseTime_Sum + which),
-                           (E_CounterName)(CPT_PL_NbOfCallUsedForAverageResponseTime + which),
-                           (E_CounterName)(CPT_PL_AverageResponseTime_Squares + which), P_value);
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_COUNT]++;
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUM] += P_value;
+      M_rtdInfo[(which * RTD_TYPES * GENERIC_TYPES) + (GENERIC_PL * RTD_TYPES) + RTD_SUMSQ] += (P_value * P_value);
       break;
 
     default :
@@ -886,19 +1090,19 @@ CStat::CStat ()
   M_fileName = new char[L_size];
   strcpy(M_fileName, DEFAULT_FILE_NAME);
   strcat(M_fileName, DEFAULT_EXTENSION);
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    M_ResponseTimeRepartition[i] = NULL;
-  }
+  M_ResponseTimeRepartition = NULL;
   M_CallLengthRepartition   = NULL;
   M_SizeOfResponseTimeRepartition = 0;
   M_SizeOfCallLengthRepartition   = 0;
   M_fileNameRtt = NULL;
+  M_genericCounters = NULL;
   M_time_ref = 0.0                   ;
   M_dumpRespTime = NULL              ;
   M_counterDumpRespTime = 0          ; 
   M_dumpRespTime = NULL;
   M_fileNameRtt  = NULL;
-
+  M_rtdInfo = NULL;
+		 
   init();
 }
 
@@ -906,23 +1110,28 @@ char* CStat::sRepartitionHeader(T_dynamicalRepartition * tabRepartition,
                                 int sizeOfTab, 
                                 char * P_repartitionName)
 {
-  static char  repartitionHeader[MAX_REPARTITION_HEADER_LENGTH];
+  static char *repartitionHeader = NULL;
   char buffer[MAX_CHAR_BUFFER_SIZE];
+  int dlen = strlen(stat_delimiter);
 
   if(tabRepartition != NULL)
     {
+      repartitionHeader = (char *)realloc(repartitionHeader, strlen(P_repartitionName) + dlen + 1);
       sprintf(repartitionHeader, "%s%s", P_repartitionName, stat_delimiter);
       for(int i=0; i<(sizeOfTab-1); i++)
-        {   
+        {
           sprintf(buffer, "<%d%s", tabRepartition[i].borderMax, stat_delimiter);
+	  repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1);
           strcat(repartitionHeader, buffer);
         }
       sprintf(buffer, ">=%d%s", tabRepartition[sizeOfTab-1].borderMax, stat_delimiter);
+      repartitionHeader = (char *)realloc(repartitionHeader, strlen(repartitionHeader) + strlen(buffer) + 1);
       strcat(repartitionHeader, buffer);
     }
   else
     {
-      repartitionHeader[0] = '\0';
+      repartitionHeader = (char *)realloc(repartitionHeader, 2);
+      strcpy(repartitionHeader, "");
     }
 
   return(repartitionHeader);
@@ -931,23 +1140,28 @@ char* CStat::sRepartitionHeader(T_dynamicalRepartition * tabRepartition,
 char* CStat::sRepartitionInfo(T_dynamicalRepartition * tabRepartition, 
                               int sizeOfTab)
 {
-  static char repartitionInfo[MAX_REPARTITION_INFO_LENGTH];
+  static char *repartitionInfo;
   char buffer[MAX_CHAR_BUFFER_SIZE];
+  int dlen = strlen(stat_delimiter);
 
   if(tabRepartition != NULL)
     {
       // if a repartition is present, this field match the repartition name
+      repartitionInfo = (char *)realloc(repartitionInfo, dlen + 1);
       sprintf(repartitionInfo, stat_delimiter);
       for(int i=0; i<(sizeOfTab-1); i++)
         {   
           sprintf(buffer, "%lu%s", tabRepartition[i].nbInThisBorder, stat_delimiter);
+	  repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1);
           strcat(repartitionInfo, buffer);
         }
       sprintf(buffer, "%lu%s", tabRepartition[sizeOfTab-1].nbInThisBorder, stat_delimiter);
+      repartitionInfo = (char *)realloc(repartitionInfo, strlen(repartitionInfo) + strlen(buffer) + 1);
       strcat(repartitionInfo, buffer);
     }
   else
     {
+      repartitionInfo = (char *)realloc(repartitionInfo, 2);
       repartitionInfo[0] = '\0';
     }
 
@@ -1033,22 +1247,15 @@ void CStat::displayData (FILE *f)
                  M_counters[CPT_C_OutgoingCallCreated]);
   DISPLAY_PERIO ("Current Call",       M_counters[CPT_C_CurrentCall]);
 
-  bool first = true;
-  for (int i = 0; i < MAX_COUNTER; i++) {
-    char s[20];
-
-    if (M_counters[CPT_C_Generic + i] == 0) {
-      continue;
-    }
-
-    if (first) {
+  if (M_genericMap.size()) {
       DISPLAY_CROSS_LINE ();
-      first = false;
-    }
-
-    sprintf(s, "Generic counter %d", i + 1);
+  }
+  for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) {
+    char *s = (char *)malloc(20 + strlen(M_genericDisplay[i]));
+    sprintf(s, "Counter %s", M_genericDisplay[i]);
 
-    DISPLAY_2VAL(s, M_counters[CPT_PD_Generic + i], M_counters[CPT_C_Generic + i]);
+    DISPLAY_2VAL(s, M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_PD], M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_C]);
+    free(s);
   }
 
   DISPLAY_CROSS_LINE ();
@@ -1064,33 +1271,26 @@ void CStat::displayData (FILE *f)
 
 
   DISPLAY_CROSS_LINE ();
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    char s[15];
+  for (int i = 1; i <= nRtds(); i++) {
+    char s[80];
 
-    if (!main_scenario->rtd_stopped[i]) {
-      continue;
-    }
+    /* Skip if we aren't stopped. */
+    assert(rtd_stopped[M_revRtdMap[i]] == true);
 
-    sprintf(s, "Response Time %d", i + 1);
+    sprintf(s, "Response Time %s", M_revRtdMap[i]);
     DISPLAY_TXT_COL (s,
-	msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_PD_AverageResponseTime_Sum + i), (E_CounterName)(CPT_PD_NbOfCallUsedForAverageResponseTime + i)) ),
-	msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_C_AverageResponseTime_Sum + i), (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i)) ) );
+	msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_PD)),
+	msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_C)));
   }
-  DISPLAY_TXT_COL ("Call Length", 
+/* I Broke this!
+  DISPLAY_TXT_COL ("Call Length",
                    msToHHMMSSmmm( (unsigned long)computeMean(CPT_PD_AverageCallLength_Sum, CPT_PD_NbOfCallUsedForAverageCallLength)),
                    msToHHMMSSmmm( (unsigned long)computeMean(CPT_C_AverageCallLength_Sum, CPT_C_NbOfCallUsedForAverageCallLength) ));
+*/
   DISPLAY_CROSS_LINE ();
 
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    char s[50];
-
-    if (!main_scenario->rtd_stopped[i]) {
-      continue;
-    }
-
-    sprintf(s, "Average Response Time Repartition, %d", i + 1);
-    DISPLAY_INFO(s);
-    displayRepartition(f, M_ResponseTimeRepartition[i], M_SizeOfResponseTimeRepartition);
+  for (int i = 1; i <= nRtds(); i++) {
+    displayRtdRepartition(f, i);
   }
   DISPLAY_INFO("Average Call Length Repartition");
   displayRepartition(f, M_CallLengthRepartition, M_SizeOfCallLengthRepartition);
@@ -1160,22 +1360,15 @@ void CStat::displayStat (FILE *f)
   DISPLAY_PERIO ("Current Call",
                  M_counters[CPT_C_CurrentCall]);
 
-  bool first = true;
-  for (int i = 0; i < MAX_COUNTER; i++) {
-    char s[20];
-
-    if (M_counters[CPT_C_Generic + i] == 0) {
-      continue;
-    }
-
-    if (first) {
+  if (M_genericMap.size()) {
       DISPLAY_CROSS_LINE ();
-      first = false;
-    }
-
-    sprintf(s, "Generic counter %d", i + 1);
+  }
+  for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) {
+    char *s = (char *)malloc(20 + strlen(M_genericDisplay[i]));
+    sprintf(s, "Counter %s", M_genericDisplay[i]);
 
-    DISPLAY_2VAL(s, M_counters[CPT_PD_Generic + i], M_counters[CPT_C_Generic + i]);
+    DISPLAY_2VAL(s, M_genericCounters[(i - 1)* GENERIC_TYPES + GENERIC_PD], M_genericCounters[(i - 1) * GENERIC_TYPES + GENERIC_C]);
+    free(s);
   }
 
   DISPLAY_CROSS_LINE ();
@@ -1190,18 +1383,13 @@ void CStat::displayStat (FILE *f)
   //               M_counters[CPT_C_UnexpectedMessage]);
 
   DISPLAY_CROSS_LINE ();
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    char s[20];
-
-    if (!main_scenario->rtd_stopped[i]) {
-      continue;
-    }
+  for (int i = 1; i <= nRtds(); i++) {
+    char s[80];
 
-
-    sprintf(s, "Response Time %d", i + 1);
+    sprintf(s, "Response Time %s", M_revRtdMap[i]);
     DISPLAY_TXT_COL (s,
-	msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_PD_AverageResponseTime_Sum + i), (E_CounterName)(CPT_PD_NbOfCallUsedForAverageResponseTime + i)) ),
-	msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_C_AverageResponseTime_Sum + i), (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i))));
+	msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_PD)),
+	msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_C)));
   }
   DISPLAY_TXT_COL ("Call Length", 
                    msToHHMMSSmmm( (unsigned long)computeMean(CPT_PD_AverageCallLength_Sum, CPT_PD_NbOfCallUsedForAverageCallLength ) ),
@@ -1211,21 +1399,25 @@ void CStat::displayStat (FILE *f)
 
 void CStat::displayRepartition (FILE *f)
 {
-  DISPLAY_INFO("Average Response Time Repartition");
-  displayRepartition(f,
-                     M_ResponseTimeRepartition[0], 
-                     M_SizeOfResponseTimeRepartition);
+  displayRtdRepartition(f, 1);
   DISPLAY_INFO("Average Call Length Repartition");
   displayRepartition(f,
                      M_CallLengthRepartition, 
                      M_SizeOfCallLengthRepartition);
 }
 
-void CStat::displaySecondaryRepartition (FILE *f, int which)
+void CStat::displayRtdRepartition (FILE *f, int which)
 {
-  DISPLAY_INFO("Average Response Time Repartition");
+  if (which > nRtds()) {
+    DISPLAY_INFO ("  <No repartion defined>");
+    return;
+  }
+
+  char s[80];
+  snprintf(s, sizeof(s), "Average Response Time Repartition %s", M_revRtdMap[which]);
+  DISPLAY_INFO(s);
   displayRepartition(f,
-                     M_ResponseTimeRepartition[which],
+                     M_ResponseTimeRepartition[which - 1],
                      M_SizeOfResponseTimeRepartition);
 }
 
@@ -1311,6 +1503,8 @@ void CStat::dumpData ()
                       << "FailedCmdNotSent(C)" << stat_delimiter
                       << "FailedRegexpDoesntMatch(P)" << stat_delimiter
                       << "FailedRegexpDoesntMatch(C)" << stat_delimiter
+                      << "FailedRegexpShouldntMatch(P)" << stat_delimiter
+                      << "FailedRegexpShouldntMatch(C)" << stat_delimiter
                       << "FailedRegexpHdrNotFound(P)" << stat_delimiter
                       << "FailedRegexpHdrNotFound(C)" << stat_delimiter
                       << "FailedOutboundCongestion(P)" << stat_delimiter
@@ -1326,23 +1520,27 @@ void CStat::dumpData ()
                       << "Retransmissions(P)" << stat_delimiter
                       << "Retransmissions(C)" << stat_delimiter
                       << "AutoAnswered(P)" << stat_delimiter
-                      << "AutoAnswered(C)" << stat_delimiter;
-
-    for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-      char s_P[30];
-      char s_C[30];
-
-      if (!main_scenario->rtd_stopped[i]) {
-	continue;
-      }
-
-      sprintf(s_P, "ResponseTime%d(P)%s", i + 1, stat_delimiter);
-      sprintf(s_C, "ResponseTime%d(C)%s", i + 1, stat_delimiter);
+                      << "AutoAnswered(C)" << stat_delimiter
+                      << "Warnings(P)" << stat_delimiter
+                      << "Warnings(C)" << stat_delimiter
+                      << "FatalErrors(P)" << stat_delimiter
+                      << "FatalErrors(C)" << stat_delimiter
+		      << "WatchdogMajor(P)" << stat_delimiter
+		      << "WatchdogMajor(C)" << stat_delimiter
+		      << "WatchdogMinor(P)" << stat_delimiter
+		      << "WatchdogMinor(C)" << stat_delimiter;
+
+    for (int i = 1; i <= nRtds(); i++) {
+      char s_P[80];
+      char s_C[80];
+
+      sprintf(s_P, "ResponseTime%s(P)%s", M_revRtdMap[i], stat_delimiter);
+      sprintf(s_C, "ResponseTime%s(C)%s", M_revRtdMap[i], stat_delimiter);
 
       (*M_outputStream) << s_P << s_C;
 
-      sprintf(s_P, "ResponseTime%dStDev(P)%s", i + 1, stat_delimiter);
-      sprintf(s_C, "ResponseTime%dStDev(C)%s", i + 1, stat_delimiter);
+      sprintf(s_P, "ResponseTime%sStDev(P)%s", M_revRtdMap[i], stat_delimiter);
+      sprintf(s_C, "ResponseTime%sStDev(C)%s", M_revRtdMap[i], stat_delimiter);
 
       (*M_outputStream) << s_P << s_C;
     }
@@ -1351,19 +1549,15 @@ void CStat::dumpData ()
                       << "CallLength(C)" << stat_delimiter;
     (*M_outputStream) << "CallLengthStDev(P)" << stat_delimiter
                       << "CallLengthStDev(C)" << stat_delimiter;
-    for (int i = 0; i < MAX_COUNTER; i++) {
-      (*M_outputStream) << "GenericCounter" << (i + 1) << "(P)" << stat_delimiter;
-      (*M_outputStream) << "GenericCounter" << (i + 1) << "(C)" << stat_delimiter;
+    for (unsigned int i = 1; i < M_genericMap.size() + 1; i++) {
+      (*M_outputStream) << M_revGenericMap[i] << "(P)" << stat_delimiter;
+      (*M_outputStream) << M_revGenericMap[i] << "(C)" << stat_delimiter;
     }
-    for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-      char s[30];
+    for (int i = 1; i <= nRtds(); i++) {
+      char s[80];
 
-      if (!main_scenario->rtd_stopped[i]) {
-	continue;
-      }
-
-      sprintf(s, "ResponseTimeRepartition%d", i + 1);
-      (*M_outputStream) << sRepartitionHeader(M_ResponseTimeRepartition[i],
+      sprintf(s, "ResponseTimeRepartition%s", M_revRtdMap[i]);
+      (*M_outputStream) << sRepartitionHeader(M_ResponseTimeRepartition[i - 1],
 					      M_SizeOfResponseTimeRepartition,
 					      s);
     }
@@ -1414,6 +1608,8 @@ void CStat::dumpData ()
                     << M_counters[CPT_C_FailedCallCmdNotSent]           << stat_delimiter
                     << M_counters[CPT_PL_FailedCallRegexpDoesntMatch]   << stat_delimiter
                     << M_counters[CPT_C_FailedCallRegexpDoesntMatch]    << stat_delimiter
+                    << M_counters[CPT_PL_FailedCallRegexpShouldntMatch] << stat_delimiter
+                    << M_counters[CPT_C_FailedCallRegexpShouldntMatch]  << stat_delimiter
                     << M_counters[CPT_PL_FailedCallRegexpHdrNotFound]   << stat_delimiter
                     << M_counters[CPT_C_FailedCallRegexpHdrNotFound]    << stat_delimiter
                     << M_counters[CPT_PL_FailedOutboundCongestion]      << stat_delimiter
@@ -1422,36 +1618,29 @@ void CStat::dumpData ()
                     << M_counters[CPT_C_FailedTimeoutOnRecv]            << stat_delimiter
                     << M_counters[CPT_PL_FailedTimeoutOnSend]           << stat_delimiter
                     << M_counters[CPT_C_FailedTimeoutOnSend]            << stat_delimiter
-                    << M_G_counters[CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1]                 << stat_delimiter
-                    << M_G_counters[CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1]                  << stat_delimiter
+                    << M_G_counters[CPT_G_PL_OutOfCallMsgs - E_NB_COUNTER - 1]                << stat_delimiter
+                    << M_G_counters[CPT_G_C_OutOfCallMsgs - E_NB_COUNTER - 1]                 << stat_delimiter
                     << M_G_counters[CPT_G_PL_DeadCallMsgs - E_NB_COUNTER - 1]                 << stat_delimiter
                     << M_G_counters[CPT_G_C_DeadCallMsgs - E_NB_COUNTER - 1]                  << stat_delimiter
                     << M_counters[CPT_PL_Retransmissions]               << stat_delimiter
                     << M_counters[CPT_C_Retransmissions]                << stat_delimiter
                     << M_G_counters[CPT_G_PL_AutoAnswered - E_NB_COUNTER - 1]                  << stat_delimiter
-                    << M_G_counters[CPT_G_C_AutoAnswered - E_NB_COUNTER - 1]                   << stat_delimiter;
+                    << M_G_counters[CPT_G_C_AutoAnswered - E_NB_COUNTER - 1]                   << stat_delimiter
+                    << M_G_counters[CPT_G_PL_Warnings - E_NB_COUNTER - 1]                  << stat_delimiter
+                    << M_G_counters[CPT_G_C_Warnings - E_NB_COUNTER - 1]                   << stat_delimiter
+                    << M_G_counters[CPT_G_PL_FatalErrors - E_NB_COUNTER - 1]                  << stat_delimiter
+		    << M_G_counters[CPT_G_C_FatalErrors - E_NB_COUNTER - 1]                   << stat_delimiter
+		    << M_G_counters[CPT_G_PL_WatchdogMajor - E_NB_COUNTER - 1]                  << stat_delimiter
+		    << M_G_counters[CPT_G_C_WatchdogMajor - E_NB_COUNTER - 1]                   << stat_delimiter
+		    << M_G_counters[CPT_G_PL_WatchdogMinor - E_NB_COUNTER - 1]                  << stat_delimiter
+		    << M_G_counters[CPT_G_C_WatchdogMinor - E_NB_COUNTER - 1]                   << stat_delimiter;
 
   // SF917289 << M_counters[CPT_C_UnexpectedMessage]    << stat_delimiter;
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    if (!main_scenario->rtd_stopped[i]) {
-      continue;
-    }
-
-    (*M_outputStream) 
-      << msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_PL_AverageResponseTime_Sum + i),
-				    (E_CounterName)(CPT_PL_NbOfCallUsedForAverageResponseTime + i))) << stat_delimiter;
-    (*M_outputStream) 
-      << msToHHMMSSmmm( (unsigned long)computeMean((E_CounterName)(CPT_C_AverageResponseTime_Sum + i),
-				    (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i))) << stat_delimiter;
-
-    (*M_outputStream)
-      << msToHHMMSSmmm( (unsigned long)computeStdev((E_CounterName)(CPT_PL_AverageResponseTime_Sum + i),
-				     (E_CounterName)(CPT_PL_NbOfCallUsedForAverageResponseTime + i),
-				     (E_CounterName)(CPT_PL_AverageResponseTime_Squares + i)) ) << stat_delimiter;
-    (*M_outputStream)
-      << msToHHMMSSmmm( (unsigned long)computeStdev((E_CounterName)(CPT_C_AverageResponseTime_Sum + i),
-				     (E_CounterName)(CPT_C_NbOfCallUsedForAverageResponseTime + i),
-				     (E_CounterName)(CPT_C_AverageResponseTime_Squares + i)) ) << stat_delimiter;
+  for (int i = 1; i <= nRtds(); i++) {
+    (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_PL)) << stat_delimiter;
+    (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdMean(i, GENERIC_C)) << stat_delimiter;
+    (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdStdev(i, GENERIC_PL)) << stat_delimiter;
+    (*M_outputStream) << msToHHMMSSmmm( (unsigned long)computeRtdStdev(i, GENERIC_C)) << stat_delimiter;
   }
   (*M_outputStream) 
     << msToHHMMSSmmm( (unsigned long)computeMean(CPT_PL_AverageCallLength_Sum, CPT_PL_NbOfCallUsedForAverageCallLength) ) << stat_delimiter;
@@ -1466,15 +1655,12 @@ void CStat::dumpData ()
 				   CPT_C_NbOfCallUsedForAverageCallLength,
 				   CPT_C_AverageCallLength_Squares )) << stat_delimiter;
 
-  for (int i = 0; i < MAX_COUNTER; i++) {
-    (*M_outputStream) << M_counters[CPT_PL_Generic + i] << stat_delimiter;
-    (*M_outputStream) << M_counters[CPT_C_Generic + i] << stat_delimiter;
+  for (unsigned int i = 0; i < M_genericMap.size(); i++) {
+    (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_PL] << stat_delimiter;
+    (*M_outputStream) << M_genericCounters[GENERIC_TYPES * i + GENERIC_C] << stat_delimiter;
   }
 
-  for (int i = 0; i < MAX_RTD_INFO_LENGTH; i++) {
-    if (!main_scenario->rtd_stopped[i]) {
-      continue;
-    }
+  for (int i = 0; i < nRtds(); i++) {
     (*M_outputStream) 
       << sRepartitionInfo(M_ResponseTimeRepartition[i], 
                           M_SizeOfResponseTimeRepartition);
@@ -1509,7 +1695,7 @@ void CStat::dumpDataRtt ()
       }
 #endif
   }
-  
+
   if(M_headerAlreadyDisplayedRtt == false) {
     (*M_outputStreamRtt) << "Date_ms" << stat_delimiter
       << "response_time_ms" << stat_delimiter
@@ -1520,16 +1706,16 @@ void CStat::dumpDataRtt ()
   for (unsigned int L_i = 0; L_i < M_counterDumpRespTime ; L_i ++) {
     (*M_outputStreamRtt) <<  M_dumpRespTime[L_i].date   << stat_delimiter ;
     (*M_outputStreamRtt) <<  M_dumpRespTime[L_i].rtt    << stat_delimiter ;
-    (*M_outputStreamRtt) <<  M_dumpRespTime[L_i].rtd_no << endl;
+    (*M_outputStreamRtt) <<  M_revRtdMap[M_dumpRespTime[L_i].rtd_no] << endl;
     (*M_outputStreamRtt).flush();
     M_dumpRespTime[L_i].date = 0.0;
     M_dumpRespTime[L_i].rtt = 0.0;
     M_dumpRespTime[L_i].rtd_no = 0;
   }
-      
+
   // flushing the output file
   (*M_outputStreamRtt).flush();
-        
+
   M_counterDumpRespTime = 0;
 }
 
@@ -1587,7 +1773,7 @@ char* CStat::formatTime (struct timeval* P_tv, bool microseconds)
 	      L_currentDate->tm_sec,
 	      (double)P_tv->tv_usec/(double)1000.0);
 	} else {
-          sprintf(L_time, "%4.4d-%2.2d-%2.2d\t%2.2d:%2.2d:%2.2d:%3.3d\t%10.10d.%6.6d",
+          sprintf(L_time, "%4.4d-%2.2d-%2.2d\t%2.2d:%2.2d:%2.2d:%3.3d\t%10.10ld.%6.6ld",
 	      L_currentDate->tm_year + 1900,
 	      L_currentDate->tm_mon + 1,
 	      L_currentDate->tm_mday,
@@ -1596,7 +1782,7 @@ char* CStat::formatTime (struct timeval* P_tv, bool microseconds)
 	      L_currentDate->tm_sec,
               (int) (P_tv->tv_usec)/1000,
               (long) (P_tv->tv_sec),
-              (long) (P_tv->tv_usec));       
+              (long) (P_tv->tv_usec));
 	}
     }
   return (L_time);
@@ -1612,6 +1798,9 @@ long CStat::computeDiffTimeInMs (struct timeval* tf, struct timeval* ti)
   return (v1*1000 + v2/1000);
 }
 
+CSample::~CSample() {
+}
+
 
 /* Implementation of a fixed distribution. */
 CFixed::CFixed(double value) {
diff --git a/stat.hpp b/stat.hpp
index af90e70..1521c84 100644
--- a/stat.hpp
+++ b/stat.hpp
@@ -29,10 +29,7 @@
 #define DEFAULT_FILE_NAME (char*)"dumpFile"
 #define DEFAULT_EXTENSION (char*)".csv"
 
-#define MAX_REPARTITION_HEADER_LENGTH 1024
-#define MAX_REPARTITION_INFO_LENGTH   1024
 #define MAX_CHAR_BUFFER_SIZE          1024
-#define MAX_COUNTER		      5
 
 #include <ctime> 
 #include <sys/time.h> 
@@ -47,11 +44,7 @@
 #include <gsl/gsl_cdf.h>
 #endif
 
-/* MAX_RTD_INFO_LENGTH defines the number of RTD begin and end points a single
- * call can have.  If you need more than five, you can increase this number,
- * but you also need to insert entries into the E_CounterName enum in stat.hpp.
- */
-#define MAX_RTD_INFO_LENGTH 5
+#include "variables.hpp"
 
 using namespace std;
 
@@ -98,6 +91,7 @@ public:
     E_CREATE_INCOMING_CALL,
     E_CALL_FAILED, 
     E_CALL_SUCCESSFULLY_ENDED,
+    E_RESET_C_COUNTERS,
     E_RESET_PD_COUNTERS,
     E_RESET_PL_COUNTERS,
     E_ADD_CALL_DURATION,
@@ -110,12 +104,17 @@ public:
     E_FAILED_CALL_REJECTED,
     E_FAILED_CMD_NOT_SENT,
     E_FAILED_REGEXP_DOESNT_MATCH,
+    E_FAILED_REGEXP_SHOULDNT_MATCH,
     E_FAILED_REGEXP_HDR_NOT_FOUND,
     E_FAILED_OUTBOUND_CONGESTION,
     E_FAILED_TIMEOUT_ON_RECV,
     E_FAILED_TIMEOUT_ON_SEND,
     E_OUT_OF_CALL_MSGS,
+    E_WATCHDOG_MAJOR,
+    E_WATCHDOG_MINOR,
     E_DEAD_CALL_MSGS,
+    E_FATAL_ERRORS,
+    E_WARNING,
     E_RETRANSMISSION,
     E_AUTO_ANSWERED,
     E_ADD_GENERIC_COUNTER
@@ -137,21 +136,6 @@ public:
   CPT_C_NbOfCallUsedForAverageCallLength,
   CPT_C_AverageCallLength_Sum,
   CPT_C_AverageCallLength_Squares,
-  CPT_C_NbOfCallUsedForAverageResponseTime,
-  CPT_C_NbOfCallUsedForAverageResponseTime_2,
-  CPT_C_NbOfCallUsedForAverageResponseTime_3,
-  CPT_C_NbOfCallUsedForAverageResponseTime_4,
-  CPT_C_NbOfCallUsedForAverageResponseTime_5, // This must match or exceed MAX_RTD_INFO
-  CPT_C_AverageResponseTime_Sum,
-  CPT_C_AverageResponseTime_Sum_2,
-  CPT_C_AverageResponseTime_Sum_3,
-  CPT_C_AverageResponseTime_Sum_4,
-  CPT_C_AverageResponseTime_Sum_5, // This must match or exceed MAX_RTD_INFO
-  CPT_C_AverageResponseTime_Squares,
-  CPT_C_AverageResponseTime_Squares_2,
-  CPT_C_AverageResponseTime_Squares_3,
-  CPT_C_AverageResponseTime_Squares_4,
-  CPT_C_AverageResponseTime_Squares_5,
   CPT_C_FailedCallCannotSendMessage,
   CPT_C_FailedCallMaxUdpRetrans,
   CPT_C_FailedCallTcpConnect,
@@ -160,15 +144,11 @@ public:
   CPT_C_FailedCallCallRejected,
   CPT_C_FailedCallCmdNotSent,
   CPT_C_FailedCallRegexpDoesntMatch,
+  CPT_C_FailedCallRegexpShouldntMatch,
   CPT_C_FailedCallRegexpHdrNotFound,
   CPT_C_FailedOutboundCongestion,
   CPT_C_FailedTimeoutOnRecv,
   CPT_C_FailedTimeoutOnSend,
-  CPT_C_Generic,
-  CPT_C_Generic_2,
-  CPT_C_Generic_3,
-  CPT_C_Generic_4,
-  CPT_C_Generic_5, // This must match or exceed MAX_COUNTER
   CPT_C_Retransmissions,
 
   // Periodic Display counter
@@ -204,15 +184,11 @@ public:
   CPT_PD_FailedCallCallRejected,
   CPT_PD_FailedCallCmdNotSent,
   CPT_PD_FailedCallRegexpDoesntMatch,
+  CPT_PD_FailedCallRegexpShouldntMatch,
   CPT_PD_FailedCallRegexpHdrNotFound,
   CPT_PD_FailedOutboundCongestion,
   CPT_PD_FailedTimeoutOnRecv,
   CPT_PD_FailedTimeoutOnSend,
-  CPT_PD_Generic,
-  CPT_PD_Generic_2,
-  CPT_PD_Generic_3,
-  CPT_PD_Generic_4,
-  CPT_PD_Generic_5, // This must match or exceed MAX_COUNTER
   CPT_PD_Retransmissions,
 
   // Periodic logging counter
@@ -249,15 +225,11 @@ public:
   CPT_PL_FailedCallCallRejected,
   CPT_PL_FailedCallCmdNotSent,
   CPT_PL_FailedCallRegexpDoesntMatch,
+  CPT_PL_FailedCallRegexpShouldntMatch,
   CPT_PL_FailedCallRegexpHdrNotFound,
   CPT_PL_FailedOutboundCongestion,
   CPT_PL_FailedTimeoutOnRecv,
   CPT_PL_FailedTimeoutOnSend,
-  CPT_PL_Generic,
-  CPT_PL_Generic_2,
-  CPT_PL_Generic_3,
-  CPT_PL_Generic_4,
-  CPT_PL_Generic_5,
   CPT_PL_Retransmissions,
 
   E_NB_COUNTER,
@@ -266,15 +238,27 @@ public:
   // Cumulative counters
   CPT_G_C_OutOfCallMsgs,
   CPT_G_C_DeadCallMsgs,
+  CPT_G_C_FatalErrors,
+  CPT_G_C_Warnings,
+  CPT_G_C_WatchdogMajor,
+  CPT_G_C_WatchdogMinor,
   CPT_G_C_AutoAnswered,
   // Periodic Display counter
   CPT_G_PD_OutOfCallMsgs,
   CPT_G_PD_DeadCallMsgs,
+  CPT_G_PD_FatalErrors,
+  CPT_G_PD_Warnings,
+  CPT_G_PD_WatchdogMajor,
+  CPT_G_PD_WatchdogMinor,
   CPT_G_PD_AutoAnswered, // must be last (RESET_PD_COUNTER)
 
   // Periodic logging counter
   CPT_G_PL_OutOfCallMsgs,
   CPT_G_PL_DeadCallMsgs,
+  CPT_G_PL_FatalErrors,
+  CPT_G_PL_Warnings,
+  CPT_G_PL_WatchdogMajor,
+  CPT_G_PL_WatchdogMinor,
   CPT_G_PL_AutoAnswered, // must be last (RESET_PL_COUNTER)
 
   E_NB_G_COUNTER,
@@ -377,8 +361,7 @@ public:
   void displayData (FILE *f);
   void displayStat(FILE *f);
   void displayRepartition(FILE *f);
-  void displaySecondaryRepartition (FILE *f, int which);
-
+  void displayRtdRepartition (FILE *f, int which);
 
   /**
    * Dump data periodically in the file M_FileName
@@ -428,12 +411,38 @@ public:
    */
   static char* msToHHMMSSmmm (unsigned long P_ms);
 
+  /* Get a counter ID by name. */
+  int findCounter(const char *counter, bool alloc);
+  int findRtd(const char *name, bool start);
+  void validateRtds();
+  int nRtds();
 
 private:
   unsigned long long       M_counters[E_NB_COUNTER];
   static unsigned long long M_G_counters[E_NB_G_COUNTER - E_NB_COUNTER];
 
-  T_dynamicalRepartition*  M_ResponseTimeRepartition[MAX_RTD_INFO_LENGTH];
+#define GENERIC_C 0
+#define GENERIC_PD 1
+#define GENERIC_PL 2
+#define GENERIC_TYPES 3
+  unsigned long long	   *M_genericCounters;
+
+  str_int_map		   M_genericMap;
+  int_str_map		   M_revGenericMap;
+  int_str_map		   M_genericDisplay;
+
+  str_int_map		   rtd_started;
+  str_int_map		   rtd_stopped;
+
+#define RTD_COUNT 0
+#define RTD_SUM 1
+#define RTD_SUMSQ 2
+#define RTD_TYPES 3
+  unsigned long long	   *M_rtdInfo;
+  str_int_map		   M_rtdMap;
+  int_str_map		   M_revRtdMap;
+
+  T_dynamicalRepartition** M_ResponseTimeRepartition;
   T_dynamicalRepartition*  M_CallLengthRepartition;
   int                      M_SizeOfResponseTimeRepartition;
   int                      M_SizeOfCallLengthRepartition;
@@ -553,6 +562,10 @@ private:
    */
   double computeMean(E_CounterName P_SumCounter,
                              E_CounterName P_NbOfCallUsed);
+
+  double computeRtdMean(int which, int type);
+  double computeRtdStdev(int which, int type);
+
   /**
    * Effective C++
    *
@@ -577,6 +590,7 @@ public:
 	virtual int textDescr(char *s, int len) = 0;
 	virtual int timeDescr(char *s, int len) = 0;
 	virtual double cdfInv(double percentile) = 0;
+	virtual ~CSample();
 private:
 };
 
diff --git a/task.cpp b/task.cpp
index 5856921..bcaac76 100644
--- a/task.cpp
+++ b/task.cpp
@@ -110,6 +110,8 @@ task_list *timewheel::task2list(task *task) {
   unsigned int wake_sigbits = wake;
   unsigned int base_sigbits = wheel_base;
 
+  assert(wheel_base <= clock_tick);
+
   if (wake == 0) {
     return &forever_list;
   }
@@ -188,6 +190,10 @@ int timewheel::expire_paused_tasks() {
 
 void timewheel::add_paused_task(task *task, bool increment) {
   task_list::iterator task_it;
+  if (task->wake() && task->wake() < wheel_base) {
+    task->add_to_runqueue();
+    return;
+  }
   task_list *list = task2list(task);
   task_it = list->insert(list->end(), task);
   task->pauselist = list;
@@ -220,8 +226,18 @@ void task::setRunning() {
 }
 
 void task::setPaused() {
+  if (running) {
     if (!remove_from_runqueue()) {
-      ERROR("Tried to remove a running call that wasn't running!\n");
+      WARNING("Tried to remove a running call that wasn't running!\n");
+      assert(0);
     }
-    add_to_paused_tasks(true);
+  } else {
+    paused_tasks.remove_paused_task(this);
+  }
+  assert(running == false);
+  add_to_paused_tasks(true);
+}
+
+void task::abort() {
+  delete this;
 }
diff --git a/task.hpp b/task.hpp
index 1512d41..da42a2b 100644
--- a/task.hpp
+++ b/task.hpp
@@ -80,7 +80,7 @@ public:
   virtual bool run() = 0;
 
   /* Our abort action. */
-  virtual void abort() = 0;
+  virtual void abort();
 
   /* Dump task info to error log. */
   virtual void dump() = 0;
diff --git a/variables.cpp b/variables.cpp
index d354386..96b6489 100644
--- a/variables.cpp
+++ b/variables.cpp
@@ -39,6 +39,8 @@ bool CCallVariable::isSet()
       return(false);
   } else if (M_type == E_VT_BOOL) {
     return M_bool;
+  } else if (M_type == E_VT_DOUBLE) {
+    return M_double;
   }
   return (M_type != E_VT_UNDEFINED);
 }
@@ -333,7 +335,7 @@ int AllocVariableTable::find(const char *varName, bool allocate) {
 }
 
 char *AllocVariableTable::getName(int i) {
-  int thisLevel  = i & (1 << LEVEL_BITS);
+  int thisLevel  = i & ((1 << LEVEL_BITS) - 1);
   assert(thisLevel <= level);
   if (thisLevel == level) {
 	return variableRevMap[i];
@@ -342,6 +344,16 @@ char *AllocVariableTable::getName(int i) {
   return av_parent->getName(i);
 }
 
+void AllocVariableTable::dump() {
+  if (av_parent) {
+    av_parent->dump();
+  }
+  WARNING("%d level %d variables:", variableMap.size(), level);
+  for (str_int_map::iterator i = variableMap.begin(); i != variableMap.end(); i++) {
+	WARNING("%s", i->first.c_str());
+  }
+}
+
 void AllocVariableTable::validate() {
   for (str_int_map::iterator var_it = variableMap.begin(); var_it != variableMap.end(); var_it++) {
     if (variableReferences[var_it->second] < 2) {
diff --git a/variables.hpp b/variables.hpp
index 67d6c8d..89a5527 100644
--- a/variables.hpp
+++ b/variables.hpp
@@ -24,6 +24,8 @@
 #ifndef _CVARIABLE
 #define _CVARIABLE
 
+#include <string>
+#include <map>
 #include <sys/types.h>
 #include <regex.h>
 
@@ -117,6 +119,7 @@ public:
   int find(const char *name, bool allocate);
   char *getName(int i);
   void validate();
+  void dump();
 private:
   AllocVariableTable *av_parent;
   str_int_map  variableMap;
diff --git a/watchdog.cpp b/watchdog.cpp
new file mode 100644
index 0000000..185c71c
--- /dev/null
+++ b/watchdog.cpp
@@ -0,0 +1,81 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author : Richard GAYRAUD - 04 Nov 2003
+ *           Marc LAMBERTON
+ *           Olivier JACQUES
+ *           Herve PELLAN
+ *           David MANSUTTI
+ *           Francois-Xavier Kowalski
+ *           Gerard Lyonnaz
+ *           From Hewlett Packard Company.
+ *           F. Tarek Rogers
+ *           Peter Higginson
+ *           Vincent Luba
+ *           Shriram Natarajan
+ *           Guillaume Teissier from FTR&D
+ *           Clement Chen
+ *           Wolfgang Beck
+ *           Charles P Wright from IBM Research
+ */
+#include "sipp.hpp"
+
+void watchdog::dump() {
+  WARNING("Watchdog Task: interval = %d, major_threshold = %d (%d triggers left), minor_threshold = %d (%d triggers left)", interval, major_threshold, major_maxtriggers, minor_threshold, minor_maxtriggers);
+}
+
+watchdog::watchdog(int interval, int reset_interval, int major_threshold, int major_maxtriggers, int minor_threshold, int minor_maxtriggers) {
+  this->interval = interval;
+  this->reset_interval = reset_interval;
+  this->major_threshold = major_threshold;
+  this->major_maxtriggers = major_maxtriggers;
+  this->minor_threshold = minor_threshold;
+  this->minor_maxtriggers = minor_maxtriggers;
+  major_triggers = 0;
+  minor_triggers = 0;
+  last_trigger = last_fire = getmilliseconds();
+}
+
+bool watchdog::run() {
+  getmilliseconds();
+  if (last_fire + this->major_threshold < clock_tick) {
+	CStat::globalStat(CStat::E_WATCHDOG_MAJOR);
+	last_trigger = clock_tick;
+	WARNING("The major watchdog timer %dms has been tripped (%d), %d trips remaining.", major_threshold, clock_tick - last_fire, major_maxtriggers - major_triggers);
+	if ((this->major_maxtriggers != -1) && (++major_triggers > this->major_maxtriggers)) {
+	  ERROR("The watchdog timer has tripped the major threshold of %dms too many times (%d out of %d allowed) (%d out of %d minor %dms timeouts tripped)\n", major_threshold, major_triggers, major_maxtriggers, minor_threshold, minor_triggers, minor_maxtriggers);
+	}
+  } else if (last_fire + this->minor_threshold < clock_tick) {
+	last_trigger = clock_tick;
+	CStat::globalStat(CStat::E_WATCHDOG_MINOR);
+	WARNING("The minor watchdog timer %dms has been tripped (%d), %d trips remaining.", minor_threshold, clock_tick - last_fire, minor_maxtriggers - minor_triggers);
+	if ((this->minor_maxtriggers != -1) && (++minor_triggers > this->minor_maxtriggers)) {
+	  ERROR("The watchdog timer has tripped the minor threshold of %dms too many times (%d out of %d allowed) (%d out of %d major %dms timeouts tripped)\n", minor_threshold, minor_triggers, minor_maxtriggers, major_threshold, major_triggers, major_maxtriggers);
+	}
+  }
+
+  if (reset_interval && (major_triggers || minor_triggers) && (last_trigger + reset_interval < clock_tick)) {
+    WARNING("Resetting watchdog timer trigger counts, as it has not been triggered in over %dms.", clock_tick - last_trigger);
+    major_triggers = minor_triggers = 0;
+  }
+
+  last_fire = clock_tick;
+  setPaused();
+  return true;
+}
+
+unsigned int watchdog::wake() {
+  return last_fire + interval;
+}
diff --git a/watchdog.hpp b/watchdog.hpp
new file mode 100644
index 0000000..4e2aca3
--- /dev/null
+++ b/watchdog.hpp
@@ -0,0 +1,58 @@
+/*
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Author : Richard GAYRAUD - 04 Nov 2003
+ *           Marc LAMBERTON
+ *           Olivier JACQUES
+ *           Herve PELLAN
+ *           David MANSUTTI
+ *           Francois-Xavier Kowalski
+ *           Gerard Lyonnaz
+ *           From Hewlett Packard Company.
+ *           F. Tarek Rogers
+ *           Peter Higginson
+ *           Vincent Luba
+ *           Shriram Natarajan
+ *           Guillaume Teissier from FTR&D
+ *           Clement Chen
+ *           Wolfgang Beck
+ *           Charles P Wright from IBM Research
+ */
+
+#ifndef WATCHDOG_HPP
+#define WATCHDOG_HPP
+
+#include "task.hpp"
+
+class watchdog : public task {
+public:
+  unsigned int wake();
+  watchdog(int interval, int reset, int major_threshold, int major_maxtriggers, int minor_threshold, int minor_maxtriggers);
+  bool run();
+  void dump();
+private:
+  int interval;
+  int reset_interval;
+  int minor_threshold;
+  int major_threshold;
+  int minor_maxtriggers;
+  int major_maxtriggers;
+  unsigned long last_fire;
+  unsigned long last_trigger;
+  int major_triggers;
+  int minor_triggers;
+};
+
+#endif
diff --git a/xp_parser.c b/xp_parser.c
index 1a2a3a5..ce0ca43 100644
--- a/xp_parser.c
+++ b/xp_parser.c
@@ -77,7 +77,6 @@ int xp_replace(char *source, char *dest, char *search, char *replace)
  * into other elements. */
 char * xp_find_start_tag_end(char *ptr)
 {
-  char *optr = ptr;
   while(*ptr) {
     if (*ptr == '<') {
       if ((strstr(ptr,"<!--") == ptr)) {
@@ -234,33 +233,37 @@ char * xp_open_element(int index)
         level--;
         if(level < 0) return NULL;
       } else {
-        if(level==0) {
-          if(index) {
-            index--;
-          } else {
-            char * end = xp_find_start_tag_end(ptr + 1);
-            char * p;
-            if(!end) return NULL;
-
-            p = strchr(ptr, ' ');
-            if(p && (p < end))  { end = p; }
-            p = strchr(ptr, '\t');
-            if(p && (p < end))  { end = p; }
-            p = strchr(ptr, '\r');
-            if(p && (p < end))  { end = p; }
-            p = strchr(ptr, '\n');
-            if(p && (p < end))  { end = p; }
-            p = strchr(ptr, '/');
-            if(p && (p < end))  { end = p; }
-
-            memcpy(name, ptr + 1, end-ptr-1);
-            name[end-ptr-1] = 0;
-            
-            xp_position[++xp_stack] = end;
-            return name;
-          }
-        }
-        level ++;
+	if(level==0) {
+	  if (index) {
+	    index --;
+	  } else {
+	    char * end = xp_find_start_tag_end(ptr + 1);
+	    char * p;
+	    if(!end) return NULL;
+
+	    p = strchr(ptr, ' ');
+	    if(p && (p < end))  { end = p; }
+	    p = strchr(ptr, '\t');
+	    if(p && (p < end))  { end = p; }
+	    p = strchr(ptr, '\r');
+	    if(p && (p < end))  { end = p; }
+	    p = strchr(ptr, '\n');
+	    if(p && (p < end))  { end = p; }
+	    p = strchr(ptr, '/');
+	    if(p && (p < end))  { end = p; }
+
+	    memcpy(name, ptr + 1, end-ptr-1);
+	    name[end-ptr-1] = 0;
+
+	    xp_position[++xp_stack] = end;
+	    return name;
+	  }
+	}
+
+	/* We want to skip over this particular element .*/
+	ptr = xp_find_start_tag_end(ptr + 1);
+	if (ptr) ptr--;
+	level ++;
       }
     } else if((*ptr == '/') && (*(ptr+1) == '>')) {
       level --;