[Pkg-voip-commits] r6622 - in /sip-tester/branches/upstream/current: Makefile call.cpp sipp.hpp

msp at alioth.debian.org msp at alioth.debian.org
Sun Jan 4 02:47:02 UTC 2009


Author: msp
Date: Sun Jan  4 02:47:02 2009
New Revision: 6622

URL: http://svn.debian.org/wsvn/pkg-voip/?sc=1&rev=6622
Log:
[svn-upgrade] Integrating new upstream version, sip-tester (3.1)

Modified:
    sip-tester/branches/upstream/current/Makefile
    sip-tester/branches/upstream/current/call.cpp
    sip-tester/branches/upstream/current/sipp.hpp

Modified: sip-tester/branches/upstream/current/Makefile
URL: http://svn.debian.org/wsvn/pkg-voip/sip-tester/branches/upstream/current/Makefile?rev=6622&op=diff
==============================================================================
--- sip-tester/branches/upstream/current/Makefile (original)
+++ sip-tester/branches/upstream/current/Makefile Sun Jan  4 02:47:02 2009
@@ -19,13 +19,15 @@
 #
 
 -include local.mk
+SVN_VERSION=$(shell if test -d .svn ; then svnversion . | sed -e 's/^/svn/;' ;  else echo unknown ; fi)
+VERINFO=-DSVN_VERSION="\"$(SVN_VERSION)\""
 
 # Output binary to be built
 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
+     actions.o variables.o infile.o deadcall.o task.o socketowner.o listener.o
 
 # Libraries directories
 LIBDIR_linux=
@@ -99,20 +101,20 @@
 CFLAGS_linux=-D__LINUX -pthread 
 CFLAGS_freebsd=-D__LINUX -pthread
 CFLAGS_tru64=-D__OSF1 -pthread
-CFLAGS_SunOS=-g -D__SUNOS
+CFLAGS_SunOS=${DEBUG_FLAGS} -D__SUNOS
 CFLAGS_Cygwin=-D__CYGWIN -Dsocklen_t=int
 CFLAGS_Darwin=-D__DARWIN
-CFLAGS=$(CFLAGS_$(SYSTEM)) -D__3PCC__ $(TLS) $(PCAPPLAY) $(EXTRACFLAGS)
+CFLAGS=$(CFLAGS_$(SYSTEM)) $(VERINFO) $(TLS) $(PCAPPLAY) $(EXTRACFLAGS)
 
 #C++ Compiler Flags
-CPPFLAGS_hpux=-AA -mt -D__HPUX +W829 
+CPPFLAGS_hpux=-AA -mt -D__HPUX -D_INCLUDE_LONGLONG -DNOMACROS +W829  
 CPPFLAGS_linux=-D__LINUX -pthread 
 CPPFLAGS_freebsd=-D__LINUX -pthread
 CPPFLAGS_tru64=-D__OSF1 -pthread
-CPPFLAGS_SunOS=-g -D__SUNOS
+CPPFLAGS_SunOS=${DEBUG_FLAGS} -D__SUNOS
 CPPFLAGS_Cygwin=-D__CYGWIN -Dsocklen_t=int
 CPPFLAGS_Darwin=-D__DARWIN
-CPPFLAGS=$(CPPFLAGS_$(SYSTEM)) -D__3PCC__ $(TLS) $(PCAPPLAY) $(EXTRACPPFLAGS)
+CPPFLAGS=$(CPPFLAGS_$(SYSTEM)) $(VERINFO) $(TLS) $(PCAPPLAY) $(EXTRACPPFLAGS)
 
 #Linker mapping
 CCLINK_hpux=aCC
@@ -129,7 +131,7 @@
 LFLAGS_linux=
 LFLAGS_freebsd=
 LFLAGS_tru64=
-LFLAGS_SunOS=
+LFLAGS_SunOS=-mt ${DEBUG_FLAGS}
 LFLAGS_Cygwin=
 LFLAGS_Darwin=
 LFLAGS=$(LFLAGS_$(SYSTEM)) $(EXTRALFLAGS)
@@ -139,7 +141,7 @@
 LIBS_hpux= -lcurses -lpthread -L /opt/openssl/lib -L /usr/local/lib
 LIBS_tru64= -lcurses -lpthread
 LIBS_freebsd= -lcurses -pthread -L /usr/local/lib
-LIBS_SunOS= -lcurses -lpthread -lnsl -lsocket -lstdc++ -lm -ldl -L /usr/local/ssl/lib/
+LIBS_SunOS= -lcurses -lpthread -lnsl -lsocket -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic -lm -ldl -L /usr/local/ssl/lib/
 LIBS_Cygwin= -lcurses -lpthread -lstdc++ -L /usr/lib/WpdPack/Lib
 LIBS_Darwin= -lcurses
 LIBS=$(LIBS_$(SYSTEM)) $(EXTRALIBS)
@@ -222,4 +224,3 @@
 
 .c.o:
 	$(CC) $(CFLAGS) $(MFLAGS) $(DEBUG_FLAGS) $(_HPUX_LI_FLAG) $(INCDIR) -c -o $*.o $<
-

Modified: sip-tester/branches/upstream/current/call.cpp
URL: http://svn.debian.org/wsvn/pkg-voip/sip-tester/branches/upstream/current/call.cpp?rev=6622&op=diff
==============================================================================
--- sip-tester/branches/upstream/current/call.cpp (original)
+++ sip-tester/branches/upstream/current/call.cpp Sun Jan  4 02:47:02 2009
@@ -46,6 +46,7 @@
 #include "send_packets.h"
 #endif
 #include "sipp.hpp"
+#include "deadcall.hpp"
 #include "assert.h"
 
 #ifdef _USE_OPENSSL
@@ -56,26 +57,15 @@
 
 extern  map<string, struct sipp_socket *>     map_perip_fd;
 
-call_map calls;
-call_list running_calls;
-timewheel paused_calls;
-
- socket_call_map_map socket_to_calls;
-
 #ifdef PCAPPLAY
 /* send_packets pthread wrapper */
 void *send_wrapper(void *);
 #endif
 
 /************** Call map and management routines **************/
-call_map * get_calls()
-{
-  return & calls;
-}
-
 static unsigned int next_number = 1;
 
-unsigned int get_tdm_map_number(unsigned int number) {
+unsigned int get_tdm_map_number() {
   unsigned int nb = 0;
   unsigned int i=0;
   unsigned int interval=0;
@@ -100,95 +90,197 @@
   } 
 }
 
-struct sipp_socket *call::associate_socket(struct sipp_socket *socket) {
-  if (socket) {
-    this->call_socket = socket;
-    add_call_to_socket(socket, this);
-  }
-  return socket;
-}
-
-struct sipp_socket *call::dissociate_socket() {
-  struct sipp_socket *ret = this->call_socket;
-
-  remove_call_from_socket(this->call_socket, this);
-  this->call_socket = NULL;
-
-  return ret;
-}
-
-call * add_call(char * call_id , bool use_ipv6, int userId)
-{
-  return add_call(call_id, use_ipv6, userId, false /* Is not automatic. */);
-}
-
-call * add_call(char * call_id , bool use_ipv6, int userId, bool isAutomatic)
-{
-  call * new_call;
-  unsigned int nb;
-
-  if(!next_number) { next_number ++; }
-
-  if (use_tdmmap) {
-    nb = get_tdm_map_number(next_number);
-    if (nb != 0) {
-      /* Mark the entry in the list as busy */
-      tdm_map[nb - 1] = true;
+/* When should this call wake up? */
+unsigned int call::wake() {
+  unsigned int wake = 0;
+
+  if (zombie) {
+    return wake;
+  }
+
+  if (paused_until) {
+    wake = paused_until;
+  }
+
+  if (next_retrans && (!wake || (next_retrans < wake))) {
+    wake = next_retrans;
+  }
+
+  if (recv_timeout && (!wake || (recv_timeout < wake))) {
+    wake = recv_timeout;
+  }
+
+  return wake;
+}
+
+#ifdef PCAPPLAY
+/******* Media information management *************************/
+/*
+ * Look for "c=IN IP4 " pattern in the message and extract the following value
+ * which should be IP address
+ */
+uint32_t get_remote_ip_media(char *msg)
+{
+    char pattern[] = "c=IN IP4 ";
+    char *begin, *end;
+    char ip[32];
+    begin = strstr(msg, pattern);
+    if (!begin) {
+      /* Can't find what we're looking at -> return no address */
+      return INADDR_NONE;
+    }
+    begin += sizeof("c=IN IP4 ") - 1;
+    end = strstr(begin, "\r\n");
+    if (!end)
+      return INADDR_NONE;
+    memset(ip, 0, 32);
+    strncpy(ip, begin, end - begin);
+    return inet_addr(ip);
+}
+
+/*
+ * 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)
+{
+    char pattern[] = "c=IN IP6 ";
+    char *begin, *end;
+    char ip[128];
+
+    memset(&addr, 0, sizeof(addr));
+    memset(ip, 0, 128);
+
+    begin = strstr(msg, pattern);
+    if (!begin) {
+      /* Can't find what we're looking at -> return no address */
+      return 0;
+    }
+    begin += sizeof("c=IN IP6 ") - 1;
+    end = strstr(begin, "\r\n");
+    if (!end)
+      return 0;
+    strncpy(ip, begin, end - begin);
+    if (!inet_pton(AF_INET6, ip, &addr)) {
+      return 0;
+    }
+    return 1;
+}
+
+/*
+ * Look for "m=audio " or "m=video " pattern in the message and extract the
+ * following value which should be port number
+ */
+#define PAT_AUDIO 1
+#define PAT_VIDEO 2
+uint16_t get_remote_port_media(char *msg, int pattype)
+{
+    char *pattern;
+    char *begin, *end;
+    char number[6];
+
+    if (pattype == PAT_AUDIO) {
+      pattern = "m=audio ";
+    } else if (pattype == PAT_VIDEO) {
+      pattern = "m=video ";
     } else {
-      /* Can't create the new call */
-      WARNING("Can't create new outgoing call: all tdm_map circuits busy");
-      return NULL;
-    }
-  }
-
-  new_call = new call(call_id, userId, use_ipv6, isAutomatic);
-
-  if(!new_call) {
-    ERROR("Memory Overflow");
-  }
-
-  /* All calls must exist in the map. */
-  calls[std::string(call_id)] = new_call;
-  /* All calls start off in the running state. */
-  add_running_call(new_call);
-
-  new_call -> number = next_number;
-  new_call -> tdm_map_number = nb - 1;
-
-  /* Vital counters update */
-  if (!isAutomatic) {
-    next_number++;
-  } else {
-    /* We do not update the call_id counter, for we create here a call */
-    /* to answer to an out of call message */
-  }
-  open_calls++;
-
-  /* Statistics update */
-  calls_since_last_rate_change++;
-  total_calls ++;
-
-  if(open_calls > open_calls_peak) { 
-    open_calls_peak = open_calls;
-    open_calls_peak_time = clock_tick / 1000;
-  }
-
-  return new_call;
-}
-
-call * add_call(char * call_id , struct sipp_socket *socket) {
-  call *new_call = add_call(call_id, socket->ss_ipv6, 0 /* No User. */, false /* Not Auto. */);
-  new_call->associate_socket(socket);
-  return new_call;
-}
-
-call * add_call(char * call_id , struct sipp_socket *socket, bool isAutomatic) {
-  call *new_call = add_call(call_id, socket->ss_ipv6, 0 /* No User. */, isAutomatic);
-  new_call->associate_socket(socket);
-  return new_call;
-}
-
-call * add_call(int userId, bool ipv6)
+	ERROR("Internal error: Undefined media pattern %d\n", 3);
+    }
+
+    begin = strstr(msg, pattern);
+    if (!begin) {
+      /* m=audio not found */
+      return 0;
+    }
+    begin += strlen(pattern) - 1;
+    end = strstr(begin, "\r\n");
+    if (!end)
+      ERROR("get_remote_port_media: no CRLF found");
+    memset(number, 0, sizeof(number));
+    strncpy(number, begin, sizeof(number) - 1);
+    return atoi(number);
+}
+
+/*
+ * IPv{4,6} compliant
+ */
+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)) {
+      audio_port = get_remote_port_media(msg, PAT_AUDIO);
+      if (audio_port) {
+        /* We have audio in the SDP: set the to_audio addr */
+        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_flowinfo = 0;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_scope_id = 0;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_family = AF_INET6;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_port = audio_port;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_addr = ip_media;
+      }
+      video_port = get_remote_port_media(msg, PAT_VIDEO);
+      if (video_port) {
+        /* We have video in the SDP: set the to_video addr */
+        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_flowinfo = 0;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_scope_id = 0;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_family = AF_INET6;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_port = video_port;
+        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_addr = ip_media;
+      }
+      hasMediaInformation = 1;
+    }
+  }
+  else {
+    uint32_t ip_media;
+    ip_media = get_remote_ip_media(msg);
+    if (ip_media != INADDR_NONE) {
+      audio_port = get_remote_port_media(msg, PAT_AUDIO);
+      if (audio_port) {
+        /* We have audio in the SDP: set the to_audio addr */
+        (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_family = AF_INET;
+        (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_port = audio_port;
+        (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_addr.s_addr = ip_media;
+      }
+      video_port = get_remote_port_media(msg, PAT_VIDEO);
+      if (video_port) {
+        /* We have video in the SDP: set the to_video addr */
+        (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_family = AF_INET;
+        (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_port = video_port;
+        (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_addr.s_addr = ip_media;
+      }
+      hasMediaInformation = 1;
+    }
+  }
+}
+
+#endif
+
+/******* Very simple hash for retransmission detection  *******/
+
+unsigned long hash(char * msg) {
+  unsigned long hash = 0;
+  int c;
+
+  while (c = *msg++)
+    hash = c + (hash << 6) + (hash << 16) - hash;
+
+  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);
+}
+
+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. */);
+}
+
+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::add_call(int userId, bool ipv6, struct sockaddr_storage *dest)
 {
   static char call_id[MAX_HEADER_LEN];
 
@@ -220,472 +312,112 @@
   }
   call_id[count] = 0;
 
-  return add_call(call_id, ipv6, userId);
-}
-
-call * get_call(char * call_id)
-{
-
-  call * call_ptr;
-
-  call_map::iterator call_it ;
-  call_it = calls.find(call_map::key_type(call_id));
-  call_ptr = (call_it != calls.end()) ? call_it->second : NULL ;
-
-  return call_ptr;
-}
-
-void delete_call(char * call_id)
-{
-  call * call_ptr;
-  call_map::iterator call_it ;
-  call_it = calls.find(call_map::key_type(call_id));
-  call_ptr = (call_it != calls.end()) ? call_it->second : NULL ;
-
-  if(call_ptr) {
-    if (use_tdmmap)
-      tdm_map[call_ptr->tdm_map_number] = false;
-    calls.erase(call_it);
-
-    if (call_ptr->running) {
-      remove_running_call(call_ptr);
-    } else {
-      paused_calls.remove_paused_call(call_ptr);
-    }
-
-    delete call_ptr;
-    open_calls--;
-  } else {
-    if (start_calls == 0) {
-      ERROR("Call not found");
-    }
-  }
-}
-
-void delete_calls(void)
-{
-  call * call_ptr;
-  
-  call_map::iterator call_it ;
-  call_it = calls.begin();
-  while (call_it != calls.end()) {
-    call_ptr = (call_it != calls.end()) ? call_it->second : NULL ;
-    WARNING_P1("Aborting call with Call-Id '%s'", call_ptr->id);
-    call_ptr->abortCall();
-    call_it = calls.begin();
-  }
-
-}
-
-/* Routines for running calls. */
-
-/* Get the overall list of running calls. */
-call_list * get_running_calls()
-{
-  return & running_calls;
-}
-
-/* Put this call in the run queue. */
-void add_running_call(call *call) {
-  call->runit = running_calls.insert(running_calls.end(), call);
-  call->running = true;
-}
-
-/* Remove this call from the run queue. */
-bool remove_running_call(call *call) {
-  if (!call->running) {
-    return false;
-    }
-  running_calls.erase(call->runit);
-  call->running = false;
-  return true;
-}
-
-/* When should this call wake up? */
-unsigned int call_wake(call *call) {
-  unsigned int wake = 0;
-
-  if (call->paused_until) {
-    wake = call->paused_until;
-  }
-
-  if (call->next_retrans && (!wake || (call->next_retrans < wake))) {
-    wake = call->next_retrans;
-  }
-
-  if (call->recv_timeout && (!wake || (call->recv_timeout < wake))) {
-    wake = call->recv_timeout;
-  }
-
-  return wake;
-}
-
-call_list *timewheel::call2list(call *call) {
-  unsigned int wake = call_wake(call);
-  unsigned int wake_sigbits = wake;
-  unsigned int base_sigbits = wheel_base;
-
-  if (wake == 0) {
-    return &forever_list;
-  }
-
-  wake_sigbits /= LEVEL_ONE_SLOTS;
-  base_sigbits /= LEVEL_ONE_SLOTS;
-  if (wake_sigbits == base_sigbits) {
-    return &wheel_one[wake % LEVEL_ONE_SLOTS];
-  }
-  wake_sigbits /= LEVEL_TWO_SLOTS;
-  base_sigbits /= LEVEL_TWO_SLOTS;
-  if (wake_sigbits == base_sigbits) {
-    return &wheel_two[(wake / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS];
-  }
-  assert(wake_sigbits < LEVEL_THREE_SLOTS);
-  return &wheel_three[wake_sigbits];
-}
-
-int expire_paused_calls() {
-  return paused_calls.expire_paused_calls();
-}
-int paused_calls_count() {
-  return paused_calls.size();
-}
-void remove_paused_call(call *call) {
-  assert(!call->running);
-  paused_calls.remove_paused_call(call);
-}
-
-/* Iterate through our sorted set of paused calls, removing those that
- * should no longer be paused, and adding them to the run queue. */
-int timewheel::expire_paused_calls() {
-  int found = 0;
-
-  while (wheel_base < clock_tick) {
-    int slot1 = wheel_base % LEVEL_ONE_SLOTS;
-
-    /* Migrate calls from slot2 when we hit 0. */
-    if (slot1 == 0) {
-      int slot2 = (wheel_base / LEVEL_ONE_SLOTS) % LEVEL_TWO_SLOTS;
-
-      /* If slot2 is also zero, we must migrate calls from slot3 into slot2. */
-      if (slot2 == 0) {
-	int slot3 = ((wheel_base / LEVEL_ONE_SLOTS) / LEVEL_TWO_SLOTS);
-	assert(slot3 < LEVEL_THREE_SLOTS);
-
-	for (call_list::iterator l3it = wheel_three[slot3].begin();
-	     l3it != wheel_three[slot3].end();
-	     l3it++) {
-	  /* Migrate this call to wheel two. */
-	  add_paused_call(*l3it, false);
-        }
-
-	wheel_three[slot3].clear();
-      }
-
-      for (call_list::iterator l2it = wheel_two[slot2].begin();
-	  l2it != wheel_two[slot2].end();
-	  l2it++) {
-	/* Migrate this call to wheel one. */
-	add_paused_call(*l2it, false);
-      }
-
-      wheel_two[slot2].clear();
-    }
-
-    found += wheel_one[slot1].size();
-    for(call_list::iterator it = wheel_one[slot1].begin();
-	it != wheel_one[slot1].end(); it++) {
-      add_running_call(*it);
-      count--;
-    }
-    wheel_one[slot1].clear();
-
-    wheel_base++;
-  }
-
-  return found;
-}
-
-void timewheel::add_paused_call(call *call, bool increment) {
-  call_list *list = call2list(call);
-  call->pauseit = list->insert(list->end(), call);
-  if (increment) {
-    count++;
-  }
-}
-
-void timewheel::remove_paused_call(call *call) {
-  call_list *list = call2list(call);
-  list->erase(call->pauseit);
-  count--;
-}
-
-timewheel::timewheel() {
-  count = 0;
-  wheel_base = clock_tick;
-}
-
-int timewheel::size() {
-  return count;
-}
-
-/* The caller must delete this list. */
-call_list *get_calls_for_socket(struct sipp_socket *socket) {
-  call_list *l = new call_list;
-
-  socket_call_map_map::iterator map_it = socket_to_calls.find(socket);
-
-  /* No map defined for this socket. */
-  if (map_it == socket_to_calls.end()) {
-    return l;
-  }
-
-  call_map *socket_call_map = (call_map *) map_it->second;
-  call_map::iterator call_it;
-
-  for (call_it = socket_call_map->begin();
-       call_it != socket_call_map->end();
-       call_it++) {
-	l->insert(l->end(), call_it->second);
-  }
-
-  return l;
-}
-
-void add_call_to_socket(struct sipp_socket *socket, call *call) {
-  socket_call_map_map::iterator map_it = socket_to_calls.find(socket);
-  /* No map defined for this socket. */
-  if (map_it == socket_to_calls.end()) {
-    socket_to_calls.insert(socket_map_pair(socket, new call_map));
-    map_it = socket_to_calls.find(socket);
-    assert(map_it != socket_to_calls.end());
-  }
-
- call_map *socket_call_map = (call_map *) map_it->second;
- socket_call_map->insert(string_call_pair(call->id, call));
-}
-
-void remove_call_from_socket(struct sipp_socket *socket, call *call) {
-  socket_call_map_map::iterator map_it = socket_to_calls.find(socket);
-  /* We must have  a map for this socket. */
-  assert(map_it != socket_to_calls.end());
-
-  call_map *socket_call_map = (call_map *) map_it->second;
-  call_map::iterator call_it = socket_call_map->find(call->id);
-  /* And our call must exist in the map. */
-  assert(call_it != socket_call_map->end());
-  socket_call_map->erase(call_it);
-
-  /* If we have no more calls, we can delete this entry. */
-  if (socket_call_map->begin() == socket_call_map->end()) {
-    delete socket_call_map;
-    socket_to_calls.erase(map_it);
-  }
-}
-
-#ifdef PCAPPLAY
-/******* Media information management *************************/
-/*
- * Look for "c=IN IP4 " pattern in the message and extract the following value
- * which should be IP address
- */
-uint32_t get_remote_ip_media(char *msg)
-{
-    char pattern[] = "c=IN IP4 ";
-    char *begin, *end;
-    char ip[32];
-    begin = strstr(msg, pattern);
-    if (!begin) {
-      /* Can't find what we're looking at -> return no address */
-      return INADDR_NONE;
-    }
-    begin += sizeof("c=IN IP4 ") - 1;
-    end = strstr(begin, "\r\n");
-    if (!end)
-      return INADDR_NONE;
-    memset(ip, 0, 32);
-    strncpy(ip, begin, end - begin);
-    return inet_addr(ip);
-}
-
-/*
- * 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)
-{
-    char pattern[] = "c=IN IP6 ";
-    char *begin, *end;
-    char ip[128];
-
-    memset(&addr, 0, sizeof(addr));
-    memset(ip, 0, 128);
-
-    begin = strstr(msg, pattern);
-    if (!begin) {
-      /* Can't find what we're looking at -> return no address */
-      return 0;
-    }
-    begin += sizeof("c=IN IP6 ") - 1;
-    end = strstr(begin, "\r\n");
-    if (!end)
-      return 0;
-    strncpy(ip, begin, end - begin);
-    if (!inet_pton(AF_INET6, ip, &addr)) {
-      return 0;
-    }
-    return 1;
-}
-
-/*
- * Look for "m=audio " pattern in the message and extract the following value
- * which should be port number
- */
-uint16_t get_remote_audio_port_media(char *msg)
-{
-    char pattern[] = "m=audio ";
-    char *begin, *end;
-    char number[6];
-    begin = strstr(msg, pattern);
-    if (!begin) {
-      /* m=audio not found */
-      return 0;
-    }
-    begin += sizeof("m=audio ") - 1;
-    end = strstr(begin, "\r\n");
-    if (!end)
-      ERROR("get_remote_audio_port_media: no CRLF found");
-    memset(number, 0, sizeof(number));
-    strncpy(number, begin, sizeof(number) - 1);
-    return atoi(number);
-}
-
-/*
- * Look for "m=video " pattern in the message and extract the following value
- * which should be port number
- */
-uint16_t get_remote_video_port_media(char *msg)
-{
-    char pattern[] = "m=video ";
-    char *begin, *end;
-    char number[5];
-    begin = strstr(msg, pattern);
-    if (!begin) {
-      /* m=video not found */
-      return 0;
-    }
-    begin += sizeof("m=video ") - 1;
-    end = strstr(begin, "\r\n");
-    if (!end)
-      ERROR("get_remote_video_port_media: no CRLF found");
-    memset(number, 0, 5);
-    strncpy(number, begin, end - begin);
-    return atoi(number);
-}
-
-/*
- * IPv{4,6} compliant
- */
-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)) {
-      audio_port = get_remote_audio_port_media(msg);
-      if (audio_port) {
-        /* We have audio in the SDP: set the to_audio addr */
-        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_flowinfo = 0;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_scope_id = 0;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_family = AF_INET6;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_port = audio_port;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_addr = ip_media;
-      }
-      video_port = get_remote_video_port_media(msg);
-      if (video_port) {
-        /* We have video in the SDP: set the to_video addr */
-        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_flowinfo = 0;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_scope_id = 0;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_family = AF_INET6;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_port = video_port;
-        (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_addr = ip_media;
-      }
-      hasMediaInformation = 1;
-    }
-  }
-  else {
-    uint32_t ip_media;
-    ip_media = get_remote_ip_media(msg);
-    if (ip_media != INADDR_NONE) {
-      audio_port = get_remote_audio_port_media(msg);
-      if (audio_port) {
-        /* We have audio in the SDP: set the to_audio addr */
-        (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_family = AF_INET;
-        (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_port = audio_port;
-        (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_addr.s_addr = ip_media;
-      }
-      video_port = get_remote_video_port_media(msg);
-      if (video_port) {
-        /* We have video in the SDP: set the to_video addr */
-        (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_family = AF_INET;
-        (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_port = video_port;
-        (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_addr.s_addr = ip_media;
-      }
-      hasMediaInformation = 1;
-    }
-  }
-}
-
-#endif
-
-/******* Very simple hash for retransmission detection  *******/
-
-unsigned long hash(char * msg) {
-  unsigned long hash = 0;
-  int c;
-
-  while (c = *msg++)
-    hash = c + (hash << 6) + (hash << 16) - hash;
-
-  return hash;
-}
-
-/******************* Call class implementation ****************/
-
-call::call(char * p_id, int userId, bool ipv6, bool isAutomatic) : use_ipv6(ipv6)
-{
-  memset(this, 0, sizeof(call));
-  id = strdup(p_id);
+  return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */);
+}
+
+
+void call::init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic)
+{
+  this->call_scenario = call_scenario;
+  zombie = false;
+  msg_index = 0;
+  last_send_index = 0;
+  last_send_msg = NULL;
+
+  last_recv_hash = 0;
+  last_recv_index = -1;
+  last_recv_msg = NULL;
+
+  recv_retrans_hash = 0;
+  recv_retrans_recv_index = -1;
+  recv_retrans_send_index = -1;
+
+  dialog_route_set = NULL;
+  next_req_url = NULL;
+
+  cseq = 0;
+
+  next_retrans = 0;
+  nb_retrans = 0;
+  nb_last_delay = 0;
+
+  paused_until = 0;
+
+  call_port = 0;
+  comp_state = NULL;
+
   start_time = clock_tick;
   call_established=false ;
-  count_in_stats=true ;
   ack_is_pending=false ;
   last_recv_msg = NULL;
   cseq = base_cseq;
   nb_last_delay = 0;
-  tdm_map_number = 0;
+  use_ipv6 = ipv6;
+  queued_msg = NULL;
   
 #ifdef _USE_OPENSSL
+  dialog_authentication = NULL;
+  dialog_challenge_type = 0;
+
   m_ctx_ssl = NULL ;
   m_bio = NULL ;
 #endif
 
-  call_remote_socket = 0;
+#ifdef PCAPPLAY
+  hasMediaInformation = 0;
+#endif
+
+  call_remote_socket = NULL;
+  if (socket) {
+    associate_socket(socket);
+    socket->ss_count++;
+  } else {
+    call_socket = NULL;
+  }
+  if (dest) {
+    memcpy(&call_peer, dest, SOCK_ADDR_SIZE(dest));
+  } else {
+    memset(&call_peer, 0, sizeof(call_peer));
+  }
   
   // initialising the CallVariable with the Scenario variable
   int i;
-  if (maxVariableUsed >= 0) {
-	M_callVariableTable = new CCallVariable *[maxVariableUsed + 1];
-  }
-  for(i=0; i<=maxVariableUsed; i++)
-    {
-      if (variableUsed[i]) {
-        M_callVariableTable[i] = new CCallVariable();
-        if (M_callVariableTable[i] == NULL) {
-          ERROR ("call variable allocation failed");
-        }
-      } else {
-        M_callVariableTable[i] = NULL;
-      }
-    }
+  VariableTable *userVars = NULL;
+  bool putUserVars = false;
+  if (userId) {
+    int_vt_map::iterator it = userVarMap.find(userId);
+    if (it != userVarMap.end()) {
+      userVars  = it->second;
+    }
+  } else {
+    userVars = new VariableTable(userVariables);
+    /* Creating this table creates a reference to it, but if it is really used,
+     * then the refcount will be increased. */
+    putUserVars = true;
+  }
+  if (call_scenario->allocVars->size > 0) {
+	if (userVars) {
+	  M_callVariableTable = new VariableTable(userVars, call_scenario->allocVars->size);
+	} else {
+	  M_callVariableTable = new VariableTable(userVars, call_scenario->allocVars->size);
+	}
+  } else if (userVars->size > 0) {
+	M_callVariableTable = userVars->getTable();
+  } else if (globalVariables->size > 0) {
+	M_callVariableTable = globalVariables->getTable();
+  } else {
+	M_callVariableTable = NULL;
+  }
+  if (putUserVars) {
+    userVars->putTable();
+  }
+
+  if (call_scenario->maxTxnUsed > 0) {
+    txnID = (char **)malloc(sizeof(char *) * call_scenario->maxTxnUsed);
+    memset(txnID, 0, sizeof(char *) * call_scenario->maxTxnUsed);
+  } else {
+    txnID = NULL;
+  }
 
   // If not updated by a message we use the start time 
   // information to compute rtd information
@@ -710,6 +442,8 @@
 	file_it++) {
       (*m_lineNumber)[file_it->first] = file_it->second->nextLine(userId);
     }
+  } else {
+    m_lineNumber = NULL;
   }
 
 #ifdef PCAPPLAY
@@ -724,38 +458,67 @@
   peer_tag = NULL;
   recv_timeout = 0;
   send_timeout = 0;
+  timewait = false;
+
+  if (!isAutomatic) {
+    /* Not advancing the number is safe, because for automatic calls we do not
+     * assign the identifier,  the only other place it is used is for the auto
+     * media port. */
+    number = next_number++;
+
+    if (use_tdmmap) {
+      tdm_map_number = get_tdm_map_number();
+      if (tdm_map_number == 0) {
+	/* Can't create the new call */
+	WARNING("Can't create new outgoing call: all tdm_map circuits busy");
+	computeStat(CStat::E_CALL_FAILED);
+	computeStat(CStat::E_FAILED_OUTBOUND_CONGESTION);
+	this->zombie = true;
+	return;
+      }
+      /* Mark the entry in the list as busy */
+      tdm_map[tdm_map_number - 1] = true;
+    } else {
+      tdm_map_number = 0;
+    }
+  }
+
+  setRunning();
 }
 
 call::~call()
 {
-  deleted += 1;
+  computeStat(CStat::E_ADD_CALL_DURATION, clock_tick - start_time);
 
   if(comp_state) { comp_free(&comp_state); }
 
-  if(count_in_stats) {
-    CStat::instance()->computeStat(CStat::E_ADD_CALL_DURATION, 
-                                   clock_tick - start_time);
-  }
-
-  sipp_close_socket(dissociate_socket());
+
   if (call_remote_socket) {
     sipp_close_socket(call_remote_socket);
   }
 
   /* Deletion of the call variable */
-  for(int i=0; i<=maxVariableUsed; i++) {
-    if(M_callVariableTable[i] != NULL) {
-      delete M_callVariableTable[i] ;
-      M_callVariableTable[i] = NULL;
-    }
-  }
-  if(M_callVariableTable) { delete M_callVariableTable; }
-  delete m_lineNumber;
+  if(M_callVariableTable) {
+    M_callVariableTable->putTable();
+  }
+  if (m_lineNumber) {
+    delete m_lineNumber;
+  }
   if (userId) {
-    freeUsers.push_front(userId);
-  }
-
-  if(id) { free(id); }
+    if (call_scenario->stats->GetStat(CStat::CPT_C_CurrentCall) >= open_calls_allowed) {
+      retiredUsers.push_front(userId);
+    } else {
+      freeUsers.push_front(userId);
+    }
+  }
+
+  if (txnID) {
+    for (int i = 0; i < call_scenario->maxTxnUsed; i++) {
+      free(txnID[i]);
+    }
+    free(txnID);
+  }
+
   if(last_recv_msg) { free(last_recv_msg); }
   if(last_send_msg) { free(last_send_msg); }
   if(peer_tag) { free(peer_tag); }
@@ -774,21 +537,55 @@
        free(dialog_authentication);
   }
 #endif
-  call_established= false ;
-}
-
-void call::connect_socket_if_needed()
+
+  if (use_tdmmap) {
+    tdm_map[tdm_map_number] = false;
+  }
+}
+
+void call::computeStat (CStat::E_Action P_action) {
+  call_scenario->stats->computeStat(P_action);
+}
+
+void call::computeStat (CStat::E_Action P_action, unsigned long P_value) {
+  call_scenario->stats->computeStat(P_action, P_value);
+}
+
+void call::computeStat (CStat::E_Action P_action, unsigned long P_value, int which) {
+  call_scenario->stats->computeStat(P_action, P_value, which);
+}
+
+/* Dump call info to error log. */
+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);
+  }
+  if (paused_until) {
+    sprintf(s, "%s (paused until %ld)", s, paused_until);
+  }
+  if (recv_timeout) {
+    sprintf(s, "%s (recv timeout %ld)", s, recv_timeout);
+  }
+  if (send_timeout) {
+    sprintf(s, "%s (send timeout %ld)", s, send_timeout);
+  }
+  WARNING("%s", s);
+}
+
+bool call::connect_socket_if_needed()
 {
   bool existing;
 
-  if(call_socket) return;
-  if(!multisocket) return;
+  if(call_socket) return true;
+  if(!multisocket) return true;
 
   if(transport == T_UDP) {
     struct sockaddr_storage saddr;
 
     if(toolMode != MODE_CLIENT)
-      return;
+      return true;
 
     char peripaddr[256];
     if (!peripsocket) {
@@ -797,7 +594,7 @@
       }
     } else {
       char *tmp = peripaddr;
-      getFieldFromInputFile(ip_file, peripfield, tmp);
+      getFieldFromInputFile(ip_file, peripfield, NULL, tmp);
       map<string, struct sipp_socket *>::iterator i;
       i = map_perip_fd.find(peripaddr);
       if (i == map_perip_fd.end()) {
@@ -817,7 +614,7 @@
       }
     }
     if (existing) {
-	return;
+	return true;
     }
 
     memset(&saddr, 0, sizeof(struct sockaddr_storage));
@@ -866,7 +663,7 @@
     }
 
     if (existing) {
-      return;
+      return true;
     }
     
     sipp_customize_socket(call_socket);
@@ -876,14 +673,26 @@
     }
 
     if (sipp_connect_socket(call_socket, L_dest)) {
-      if (reset_number > 0) {
+      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");
         }
-        start_calls = 1;
+	/* 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--;
+	}
+
+	computeStat(CStat::E_CALL_FAILED);
+	computeStat(CStat::E_FAILED_TCP_CONNECT);
+	delete this;
+
+	return false;
       } else {
 	if(errno == EINVAL){
 	  /* This occurs sometime on HPUX but is not a true INVAL */
@@ -894,17 +703,18 @@
       }
     }
   }
-}
-
-bool lost(int index)
+  return true;
+}
+
+bool call::lost(int index)
 {
   static int inited = 0;
   double percent = global_lost;
 
   if(!lose_packets) return false;
 
-  if (scenario[index]->lost >= 0) {
-    percent = scenario[index]->lost;
+  if (call_scenario->messages[index]->lost >= 0) {
+    percent = call_scenario->messages[index]->lost;
   }
 
   if (percent == 0) {
@@ -928,15 +738,15 @@
       struct timeval currentTime;
       GET_TIME (&currentTime);
       char* cs=get_header_content(msg,"CSeq:");
-      TRACE_SHORTMSG((s, "%s\tS\t%s\tCSeq:%s\t%s\n",
-             CStat::instance()->formatTime(&currentTime),id, cs, get_first_line(msg)));
+      TRACE_SHORTMSG("%s\tS\t%s\tCSeq:%s\t%s\n",
+             CStat::formatTime(&currentTime),id, cs, get_first_line(msg));
   }  
  
   if((index!=-1) && (lost(index))) {
-    TRACE_MSG((s, "%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport)));
+    TRACE_MSG("%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport));
     
     if(comp_state) { comp_free(&comp_state); }
-    scenario[index] -> nb_lost++;
+    call_scenario->messages[index] -> nb_lost++;
     return 0;
   }
   
@@ -956,7 +766,7 @@
 	if (sipp_connect_socket(call_remote_socket, L_dest)) {
 	  if(errno == EINVAL){
 	    /* This occurs sometime on HPUX but is not a true INVAL */
-	    ERROR_P1("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport));
+	    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");
 	  }
@@ -966,15 +776,15 @@
     sock=call_remote_socket ;
   }
 
-  rc = write_socket(sock, msg, strlen(msg), WS_BUFFER);
+  rc = write_socket(sock, msg, strlen(msg), WS_BUFFER, &call_peer);
   if(rc == -1 && errno == EWOULDBLOCK) {
     return -1;
   }
 
   if(rc < 0) {
-    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-    CStat::instance()->computeStat(CStat::E_FAILED_CANNOT_SEND_MSG);
-    delete_call(id);
+    computeStat(CStat::E_CALL_FAILED);
+    computeStat(CStat::E_FAILED_CANNOT_SEND_MSG);
+    delete this;
   }
 
   return rc; /* OK */
@@ -986,7 +796,11 @@
 {
   /* call send_raw but with a special scenario index */
   if (send_raw(msg, -1) < 0) {
-    ERROR_NO("Error sending raw message");
+    if (sendbuffer_warn) {
+      ERROR_NO("Error sending raw message");
+    } else {
+      WARNING_NO("Error sending raw message");
+    }
   }
 }
 
@@ -1044,7 +858,7 @@
   /* Ideally this check should be moved to the XML parser so that it is not
    * along a critical path.  We could also handle lowercasing there. */
   if (len > MAX_HEADER_LEN) {
-    ERROR_P2("call::get_last_header: Header to parse bigger than %d (%zu)", MAX_HEADER_LEN, strlen(name));
+    ERROR("call::get_last_header: Header to parse bigger than %d (%zu)", MAX_HEADER_LEN, strlen(name));
   }
 
   if (name[len - 1] == ':') {
@@ -1081,7 +895,7 @@
 
   /* for safety's sake */
   if (NULL == name || NULL == strrchr(name, ':')) {
-    WARNING_P1("Can not searching for header (no colon): %s", name ? name : "(null)");
+    WARNING("Can not searching for header (no colon): %s", name ? name : "(null)");
     return last_header;
   }
 
@@ -1274,7 +1088,10 @@
   char *L_ptr2 ;
 
   /* Socket port must be known before string substitution */
-  connect_socket_if_needed();
+  if (!connect_socket_if_needed()) {
+    *send_status = -2;
+    return NULL;
+  }
 
   assert(call_socket);
 
@@ -1283,34 +1100,32 @@
     return NULL;
   }
 
-  if(scenario[index] -> send_scheme) {
-    char * dest;
-    dest = createSendingMessage(scenario[index] -> send_scheme, index);
-    strcpy(msg_buffer, dest);
-
-    if (dest) {
-      L_ptr1=msg_name ;
-      L_ptr2=msg_buffer ;
-      while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t'))  {
-        *L_ptr1 = *L_ptr2;
-        L_ptr1 ++;
-        L_ptr2 ++;
-      }
-      *L_ptr1 = '\0' ;
-    }
-
-    if (strcmp(msg_name,"ACK") == 0) {
-      call_established = true ;
-      ack_is_pending = false ;
-    }
-
-    if(send_status) {
-      *send_status = send_raw(msg_buffer, index);
-    } else {
-      send_raw(msg_buffer, index);
-    }
+  assert(call_scenario->messages[index]->send_scheme);
+
+  char * dest;
+  dest = createSendingMessage(call_scenario->messages[index] -> send_scheme, index);
+  strcpy(msg_buffer, dest);
+
+  if (dest) {
+    L_ptr1=msg_name ;
+    L_ptr2=msg_buffer ;
+    while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t'))  {
+      *L_ptr1 = *L_ptr2;
+      L_ptr1 ++;
+      L_ptr2 ++;
+    }
+    *L_ptr1 = '\0' ;
+  }
+
+  if (strcmp(msg_name,"ACK") == 0) {
+    call_established = true ;
+    ack_is_pending = false ;
+  }
+
+  if(send_status) {
+    *send_status = send_raw(msg_buffer, index);
   } else {
-    ERROR("Unsupported 'send' message in scenario");
+    send_raw(msg_buffer, index);
   }
 
   return msg_buffer;
@@ -1318,75 +1133,108 @@
 
 void call::do_bookkeeping(int index) {
   /* If this message increments a counter, do it now. */
-  if(int counter = scenario[index] -> counter) {
-    CStat::instance()->computeStat(CStat::E_ADD_GENERIC_COUNTER, 1, counter - 1);
+  if(int counter = call_scenario->messages[index] -> 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 = scenario[index] -> start_rtd) {
+  if(int rtd = call_scenario->messages[index] -> start_rtd) {
     start_time_rtd[rtd - 1] = getmicroseconds();
   }
 
-  if(int rtd = scenario[index] -> stop_rtd) {
+  if(int rtd = call_scenario->messages[index] -> stop_rtd) {
     if (!rtd_done[rtd - 1]) {
       unsigned long long start = start_time_rtd[rtd - 1];
       unsigned long long end = getmicroseconds();
 
       if(dumpInRtt) {
-	CStat::instance()->computeRtt(start, end, rtd);
-      }
-
-      CStat::instance()->computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION,
+	call_scenario->stats->computeRtt(start, end, rtd);
+      }
+
+      computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION,
 	  (end - start) / 1000, rtd - 1);
 
-      if (!scenario[index] -> repeat_rtd) {
+      if (!call_scenario->messages[index] -> repeat_rtd) {
 	rtd_done[rtd - 1] = true;
       }
     }
   }
 }
 
+void call::tcpClose() {
+  terminate(CStat::E_FAILED_TCP_CLOSED);
+}
+
+bool call::terminate(CStat::E_Action reason) {
+  char reason_str[100];
+
+  stopListening();
+
+  // Call end -> was it successful?
+  if(call::last_action_result != call::E_AR_NO_ERROR) {
+    switch(call::last_action_result) {
+      case call::E_AR_REGEXP_DOESNT_MATCH:
+	computeStat(CStat::E_CALL_FAILED);
+	computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH);
+	if (deadcall_wait) {
+	  sprintf(reason_str, "regexp match failure 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) {
+	  sprintf(reason_str, "regexp header not found at index %d", msg_index);
+	  new deadcall(id, reason_str);
+	}
+	break;
+      case call::E_AR_NO_ERROR:
+      case call::E_AR_STOP_CALL:
+	/* Do nothing. */
+	break;
+    }
+  } else {
+    if (reason == CStat::E_CALL_SUCCESSFULLY_ENDED || timewait) {
+      computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED);
+      if (deadcall_wait) {
+	new deadcall(id, "successful");
+      }
+    } else {
+      computeStat(CStat::E_CALL_FAILED);
+      if (reason != CStat::E_NO_ACTION) {
+	computeStat(reason);
+      }
+      if (deadcall_wait) {
+	sprintf(reason_str, "failed at index %d", msg_index);
+	new deadcall(id, reason_str);
+      }
+    }
+  }
+  delete this;
+}
+
 bool call::next()
 {
-  int test = scenario[msg_index]->test;
+  int test = call_scenario->messages[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 ( scenario[msg_index]->next && 
-       ((test == -1) ||
-        (test <= maxVariableUsed && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet()))
+  if ( (call_scenario->messages[msg_index]->next >= 0) &&
+       ((test == -1) || M_callVariableTable->getVar(test)->isSet())
      ) {
     /* Branching possible, check the probability */
-    int chance = scenario[msg_index]->chance;
+    int chance = call_scenario->messages[msg_index]->chance;
     if ((chance <= 0) || (rand() > chance )) {
       /* Branch == overwrite with the 'next' attribute value */
-      new_msg_index = labelArray[scenario[msg_index]->next];
+      new_msg_index = call_scenario->messages[msg_index]->next;
     }
   }
   msg_index=new_msg_index;
   recv_timeout = 0;
-  if(msg_index >= scenario_len) {
-    // Call end -> was it successful?
-    if(call::last_action_result != call::E_AR_NO_ERROR) {
-      switch(call::last_action_result) {
-        case call::E_AR_REGEXP_DOESNT_MATCH:
-          CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-          CStat::instance()->computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH);
-          break;
-        case call::E_AR_HDR_NOT_FOUND:
-          CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-          CStat::instance()->computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND);
-          break;
-	case call::E_AR_NO_ERROR:
-	case call::E_AR_STOP_CALL:
-	  /* Do nothing. */
-	  break;
-      }
-    } else {
-      CStat::instance()->computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED);
-    }
-    delete_call(id);
+  if(msg_index >= call_scenario->length) {
+    terminate(CStat::E_CALL_SUCCESSFULLY_ENDED);
     return false;
   }
 
@@ -1400,12 +1248,19 @@
 
   assert(running);
 
+  if (zombie) {
+    delete this;
+    return false;
+  }
+
   clock_tick = getmilliseconds();
 
-  if(msg_index >= scenario_len) {
-    ERROR_P3("Scenario overrun for call %s (%p) (index = %d)\n",
+  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)) {
@@ -1418,38 +1273,38 @@
 
     if((nb_retrans > (bInviteTransaction ? max_invite_retrans : max_non_invite_retrans)) ||
        (nb_retrans > max_udp_retrans)) {
-      scenario[last_send_index] -> nb_timeout ++;
-      if (scenario[last_send_index]->on_timeout) {  // action on timeout
-          WARNING_P3("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ", 
-                      id, msg_index, scenario[last_send_index]->on_timeout);
-          msg_index = labelArray[scenario[last_send_index]->on_timeout];
+      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 < scenario_len) {
+          if (msg_index < call_scenario->length) {
 		return true;
 	  }
 
           // here if asked to go to the last label  delete the call
-          CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-          CStat::instance()->computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
-          if (default_behavior) {
+          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_call(id);
+            delete this;
             return false;
           }
       }
-      CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-      CStat::instance()->computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
-      if (default_behavior) {
+      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_P1("Aborting call on UDP retransmission timeout for Call-ID '%s'", id);
+        WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id);
         return(abortCall());
       } else {
         // Just delete existing call
-        delete_call(id);
+        delete this;
         return false;
       }
     } else {
@@ -1464,8 +1319,8 @@
       if(send_raw(last_send_msg, last_send_index) < -1) {
         return false;
       }
-      scenario[last_send_index] -> nb_sent_retrans++;
-      CStat::instance()->computeStat(CStat::E_RETRANSMISSION);
+      call_scenario->messages[last_send_index] -> nb_sent_retrans++;
+      computeStat(CStat::E_RETRANSMISSION);
       next_retrans = clock_tick + nb_last_delay;
     }
   }
@@ -1473,26 +1328,19 @@
   if(paused_until) {
     /* Process a pending pause instruction until delay expiration */
     if(paused_until > clock_tick) {
-      if (!remove_running_call(this)) {
-	ERROR("Tried to remove a running call that wasn't running!\n");
-      }
-      paused_calls.add_paused_call(this, true);
+      setPaused();
       return true;
     }
     /* Our pause is over. */
     paused_until = 0;
     return next();
-  } else if(scenario[msg_index] -> pause_distribution || scenario[msg_index]->pause_variable) {
+  } else if(curmsg -> pause_distribution || curmsg->pause_variable != -1) {
     unsigned int pause;
-    if (scenario[msg_index]->pause_distribution) {
-      pause  = (int)(scenario[msg_index] -> pause_distribution -> sample());
+    if (curmsg->pause_distribution) {
+      pause  = (int)(curmsg -> pause_distribution -> sample());
     } else {
-      int varId = scenario[msg_index]->pause_variable;
-      if(varId <= maxVariableUsed && M_callVariableTable[varId]) {
-	pause = (int) M_callVariableTable[varId]->getDouble();
-      } else {
-	pause = 0;
-      }
+      int varId = curmsg->pause_variable;
+      pause = (int) M_callVariableTable->getVar(varId)->getDouble();
     }
     if (pause < 0) {
       pause = 0;
@@ -1502,12 +1350,18 @@
     }
     paused_until = clock_tick + pause;
 
+    /* This state is used as the last message of a scenario, just for handling
+     * final retransmissions. If the connection closes, we do not mark it is
+     * failed. */
+    this->timewait = curmsg->timewait;
+
     /* Increment the number of sessions in pause state */
-    ++scenario[msg_index]->sessions;
+    curmsg->sessions++;
+    do_bookkeeping(msg_index);
+    actionResult = executeAction(NULL, msg_index);
     return run(); /* In case delay is 0 */
   }
-#ifdef __3PCC__
-  else if(scenario[msg_index] -> M_type == MSG_TYPE_SENDCMD) {
+  else if(curmsg -> M_type == MSG_TYPE_SENDCMD) {
     int send_status;
 
     if(next_retrans) {
@@ -1519,18 +1373,20 @@
     if(send_status != 0) { /* Send error */
       return false; /* call deleted */
     }
-    scenario[msg_index] -> M_nbCmdSent++;
+    curmsg -> M_nbCmdSent++;
     next_retrans = 0;
-    return(next());
-  }
-#endif
-  else if(scenario[msg_index] -> M_type == MSG_TYPE_NOP) {
+
     do_bookkeeping(msg_index);
     actionResult = executeAction(NULL, msg_index);
     return(next());
   }
-
-  else if(scenario[msg_index] -> send_scheme) {
+  else if(curmsg -> M_type == MSG_TYPE_NOP) {
+    do_bookkeeping(msg_index);
+    actionResult = executeAction(NULL, msg_index);
+    return(next());
+  }
+
+  else if(curmsg -> send_scheme) {
     char * msg_snd;
     int send_status;
 
@@ -1538,10 +1394,7 @@
      * retransmission enabled is acknowledged */
 
     if(next_retrans) {
-      if (!remove_running_call(this)) {
-	ERROR("Tried to remove a running call that wasn't running!\n");
-      }
-      paused_calls.add_paused_call(this, true);
+      setPaused();
       return true;
     }
 
@@ -1555,9 +1408,9 @@
      */
 
     int incr_cseq = 0;
-    if (!scenario[msg_index]->send_scheme->isAck() &&
-        !scenario[msg_index]->send_scheme->isCancel() &&
-        !scenario[msg_index]->send_scheme->isResponse()) {
+    if (!curmsg->send_scheme->isAck() &&
+        !curmsg->send_scheme->isCancel() &&
+        !curmsg->send_scheme->isResponse()) {
           ++cseq;
           incr_cseq = 1;
     }
@@ -1569,20 +1422,20 @@
       if (send_timeout) {
 	/* If we have actually timed out. */
 	if (clock_tick > send_timeout) {
-	  WARNING_P2("Call-Id: %s, send timeout on message %d: aborting call",
+	  WARNING("Call-Id: %s, send timeout on message %d: aborting call",
 	      id, msg_index);
-	  CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-	  CStat::instance()->computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND);
-	  if (default_behavior) {
+	  computeStat(CStat::E_CALL_FAILED);
+	  computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND);
+	  if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
 	    return (abortCall());
 	  } else {
-	    delete_call(id);
+	    delete this;
 	    return false;
 	  }
 	}
-      } else if (scenario[msg_index]->timeout) {
+      } else if (curmsg->timeout) {
 	/* Initialize the send timeout to the per message timeout. */
-	send_timeout = clock_tick + scenario[msg_index]->timeout;
+	send_timeout = clock_tick + curmsg->timeout;
       } else if (defl_send_timeout) {
 	/* Initialize the send timeout to the global timeout. */
 	send_timeout = clock_tick + defl_send_timeout;
@@ -1591,7 +1444,8 @@
     } else if(send_status < 0) { /* Send error */
       /* The timeout will not be sent, so the timeout is no longer needed. */
       send_timeout = 0;
-      return false; /* call deleted */
+      /* The call was already deleted by connect_socket_if_needed or send_raw. */
+      return false;
     }
     /* We have sent the message, so the timeout is no longer needed. */
     send_timeout = 0;
@@ -1600,96 +1454,203 @@
     last_send_msg = (char *) realloc(last_send_msg, strlen(msg_snd) + 1);
     strcpy(last_send_msg, msg_snd);
 
-    if(last_recv_hash) {
+    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);
+    }
+
+    if(last_recv_index >= 0) {
       /* We are sending just after msg reception. There is a great
        * 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;
-    
+
       /* Prevent from detecting the cause relation between send and recv 
        * in the next valid send */
       last_recv_hash = 0;
     }
 
     /* Update retransmission information */
-    if(scenario[msg_index] -> retrans_delay) {
+    if(curmsg -> retrans_delay) {
       if((transport == T_UDP) && (retrans_enabled)) {
-        next_retrans = clock_tick + scenario[msg_index] -> retrans_delay;
+        next_retrans = clock_tick + curmsg -> retrans_delay;
         nb_retrans = 0;
-        nb_last_delay = scenario[msg_index]->retrans_delay;
+        nb_last_delay = curmsg->retrans_delay;
       }
     } else {
       next_retrans = 0;
     }
-    
-#ifdef PCAPPLAY
+
     actionResult = executeAction(msg_snd, msg_index);
-#endif
-    
+
     /* Update scenario statistics */
-    scenario[msg_index] -> nb_sent++;
+    curmsg -> nb_sent++;
 
     return next();
-  } else if (scenario[msg_index]->M_type == MSG_TYPE_RECV
-#ifdef __3PCC__
-         || scenario[msg_index]->M_type == MSG_TYPE_RECVCMD
-#endif
+  } else if (curmsg->M_type == MSG_TYPE_RECV
+         || curmsg->M_type == MSG_TYPE_RECVCMD
                                                  ) {
-    if (recv_timeout) {
+    if (queued_msg) {
+      char *msg = queued_msg;
+      queued_msg = NULL;
+      bool ret = process_incoming(msg);
+      free(msg);
+      return ret;
+    } else if (recv_timeout) {
       if(recv_timeout > clock_tick || recv_timeout > getmilliseconds()) {
-	if (!remove_running_call(this)) {
-	  ERROR("Tried to remove a running call that wasn't running!\n");
-	}
-	paused_calls.add_paused_call(this, true);
+	setPaused();
 	return true;
       }
       recv_timeout = 0;
-      ++scenario[msg_index]->nb_timeout;
-      if (scenario[msg_index]->on_timeout == 0) {
+      curmsg->nb_timeout++;
+      if (curmsg->on_timeout < 0) {
         // if you set a timeout but not a label, the call is aborted 
-        WARNING_P2("Call-Id: %s, receive timeout on message %d without label to jump to (ontimeout attribute): aborting call", 
+        WARNING("Call-Id: %s, receive timeout on message %d without label to jump to (ontimeout attribute): aborting call",
                    id, msg_index);
-        CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-        CStat::instance()->computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
-        if (default_behavior) {
+        computeStat(CStat::E_CALL_FAILED);
+        computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
+        if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
           return (abortCall());
         } else {
-          delete_call(id);
+          delete this;
           return false;
         }
       }
-      WARNING_P3("Call-Id: %s, receive timeout on message %d, jumping to label %d", 
-                  id, msg_index, scenario[msg_index]->on_timeout);
-      msg_index = labelArray[scenario[msg_index]->on_timeout];
+      WARNING("Call-Id: %s, receive timeout on message %d, jumping to label %d",
+                  id, msg_index, curmsg->on_timeout);
+      msg_index = curmsg->on_timeout;
       recv_timeout = 0;
-      if (msg_index < scenario_len) return true;
+      if (msg_index < call_scenario->length) return true;
       // special case - the label points to the end - finish the call
-      CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-      CStat::instance()->computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
-      if (default_behavior) {
+      computeStat(CStat::E_CALL_FAILED);
+      computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
+      if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
         return (abortCall());
       } else {
-        delete_call(id);
+        delete this;
         return false;
       }
-    } else if ((scenario[msg_index]->timeout) || (defl_recv_timeout)) {
-      if (scenario[msg_index]->timeout)
+    } else if (curmsg->timeout || defl_recv_timeout) {
+      if (curmsg->timeout)
         // If timeout is specified on message receive, use it
-        recv_timeout = getmilliseconds() + scenario[msg_index]->timeout;
+        recv_timeout = getmilliseconds() + curmsg->timeout;
       else
         // Else use the default timeout if specified
         recv_timeout = getmilliseconds() + defl_recv_timeout;
 	return true;
     } else {
 	/* We are going to wait forever. */
-	if (!remove_running_call(this)) {
-	  ERROR("Tried to remove a running call that wasn't running!\n");
-	}
-	paused_calls.add_paused_call(this, true);
+	setPaused();
     }
   }
   return true;
+}
+
+char *default_message_names[] = {
+	"3pcc_abort",
+	"ack",
+	"ack2",
+	"bye",
+	"cancel",
+	"200",
+};
+char *default_message_strings[] = {
+	/* 3pcc_abort */
+	"call-id: [call_id]\ninternal-cmd: abort_call\n\n",
+	/* ack */
+        "ACK [last_Request_URI] SIP/2.0\n"
+        "[last_Via]\n"
+        "[last_From]\n"
+        "[last_To]\n"
+        "Call-ID: [call_id]\n"
+        "CSeq: [last_cseq_number] ACK\n"
+        "Contact: <sip:sipp@[local_ip]:[local_port];transport=[transport]>\n"
+        "Max-Forwards: 70\n"
+        "Subject: Performance Test\n"
+        "Content-Length: 0\n\n",
+	/* ack2, the only difference is Via, I don't quite know why. */
+        "ACK [last_Request_URI] SIP/2.0\n"
+        "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n"
+        "[last_From]\n"
+        "[last_To]\n"
+        "Call-ID: [call_id]\n"
+        "CSeq: [last_cseq_number] ACK\n"
+        "Contact: <sip:sipp@[local_ip]:[local_port];transport=[transport]>\n"
+        "Max-Forwards: 70\n"
+        "Subject: Performance Test\n"
+        "Content-Length: 0\n\n",
+	/* bye */
+        "BYE [last_Request_URI] SIP/2.0\n"
+        "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n"
+        "[last_From]\n"
+        "[last_To]\n"
+        "Call-ID: [call_id]\n"
+        "CSeq: [last_cseq_number+1] BYE\n"
+        "Max-Forwards: 70\n"
+        "Contact: <sip:sipp@[local_ip]:[local_port];transport=[transport]>\n"
+        "Content-Length: 0\n\n",
+	/* cancel */
+        "CANCEL [last_Request_URI] SIP/2.0\n"
+        "[last_Via]\n"
+        "[last_From]\n"
+        "[last_To]\n"
+        "Call-ID: [call_id]\n"
+	"CSeq: [last_cseq_number] CANCEL\n"
+        "Max-Forwards: 70\n"
+        "Contact: <sip:sipp@[local_ip]:[local_port];transport=[transport]>\n"
+        "Content-Length: 0\n\n",
+	/* 200 */
+	"SIP/2.0 200 OK\n"
+	"[last_Via:]\n"
+	"[last_From:]\n"
+	"[last_To:]\n"
+	"[last_Call-ID:]\n"
+	"[last_CSeq:]\n"
+	"Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n"
+	"Content-Length: 0\n\n"
+};
+
+SendingMessage **default_messages;
+
+void init_default_messages() {
+  int messages = sizeof(default_message_strings)/sizeof(default_message_strings[0]);
+  default_messages = new SendingMessage* [messages];
+  for (int i = 0; i < messages; i++) {
+    default_messages[i] = new SendingMessage(main_scenario, default_message_strings[i]);
+  }
+}
+
+void free_default_messages() {
+  int messages = sizeof(default_message_strings)/sizeof(default_message_strings[0]);
+  if (!default_messages) {
+    return;
+  }
+  for (int i = 0; i < messages; i++) {
+    delete default_messages[i];
+  }
+  delete [] default_messages;
+}
+
+SendingMessage *get_default_message(const char *which) {
+  int messages = sizeof(default_message_names)/sizeof(default_message_names[0]);
+  for (int i = 0; i < messages; i++) {
+    if (!strcmp(which, default_message_names[i])) {
+      return default_messages[i];
+    }
+  }
+  ERROR("Internal Error: Unknown default message: %s!", which);
+}
+
+void set_default_message(const char *which, char *msg) {
+  int messages = sizeof(default_message_names)/sizeof(default_message_names[0]);
+  for (int i = 0; i < messages; i++) {
+    if (!strcmp(which, default_message_names[i])) {
+      default_message_strings[i] = msg;
+      return;
+    }
+  }
+  ERROR("Internal Error: Unknown default message: %s!", which);
 }
 
 bool call::process_unexpected(char * msg)
@@ -1697,60 +1658,70 @@
   char buffer[MAX_HEADER_LEN];
   char *desc = buffer;
 
-  scenario[msg_index] -> nb_unexp++;
-
-  if (default_behavior) {
+  message *curmsg = call_scenario->messages[msg_index];
+
+  curmsg->nb_unexp++;
+
+  if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) {
 	desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Aborting ");
   } else {
 	desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "Continuing ");
   }
   desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "call on unexpected message for Call-Id '%s': ", id);
 
-  if (scenario[msg_index] -> M_type == MSG_TYPE_RECV) {
-    if (scenario[msg_index] -> recv_request) {
-      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%s' ", scenario[msg_index] -> recv_request);
+  if (curmsg -> M_type == MSG_TYPE_RECV) {
+    if (curmsg -> recv_request) {
+      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%s' ", curmsg -> recv_request);
     } else {
-      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%d' ", scenario[msg_index] -> recv_response);
-    }
-  } else if (scenario[msg_index] -> M_type == MSG_TYPE_SEND) {
+      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting '%d' ", curmsg -> recv_response);
+    }
+  } else if (curmsg -> M_type == MSG_TYPE_SEND) {
       desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending ");
-  } else if (scenario[msg_index] -> M_type == MSG_TYPE_PAUSE) {
+  } else if (curmsg -> M_type == MSG_TYPE_PAUSE) {
       desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while pausing ");
-  } else if (scenario[msg_index] -> M_type == MSG_TYPE_SENDCMD) {
+  } else if (curmsg -> M_type == MSG_TYPE_SENDCMD) {
       desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending command ");
-  } else if (scenario[msg_index] -> M_type == MSG_TYPE_RECVCMD) {
+  } else if (curmsg -> M_type == MSG_TYPE_RECVCMD) {
       desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while expecting command ");
   } else {
-      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while in message type %d ", scenario[msg_index]->M_type);
+      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while in message type %d ", curmsg->M_type);
   }
   desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "(index %d)", msg_index);
 
-  WARNING_P2("%s, received '%s'", buffer, msg);
-
-  TRACE_MSG((s, "-----------------------------------------------\n"
+  WARNING("%s, received '%s'", buffer, msg);
+
+  TRACE_MSG("-----------------------------------------------\n"
              "Unexpected %s message received:\n\n%s\n",
              TRANSPORT_TO_STRING(transport),
-             msg));
-
-  if (default_behavior) {
+             msg);
+
+  if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) {
     // if twin socket call => reset the other part here 
     if (twinSippSocket && (msg_index > 0)) {
-      //WARNING_P2("call-ID '%s', internal-cmd: abort_call %s",id, "");
-      sendCmdBuffer
-	(createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n", -1));
+      sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1));
     }
 
     // usage of last_ keywords => for call aborting
     last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1);
     strcpy(last_recv_msg, msg);
 
-    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-    CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
-    return (abortCall());
+    computeStat(CStat::E_CALL_FAILED);
+    computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
+    if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
+      return (abortCall());
+    } else {
+      delete this;
+      return false;
+    }
   } else {
     // Do not abort call nor send anything in reply if default behavior is disabled
     return false;
   }
+}
+
+void call::abort() {
+  WARNING("Aborted call with Call-ID '%s'", id);
+  abortCall();
 }
 
 bool call::abortCall()
@@ -1775,23 +1746,7 @@
       // Answer unexpected errors (4XX, 5XX and beyond) with an ACK 
       // Contributed by F. Tarek Rogers
       if((src_recv) && (get_reply_code(src_recv) >= 400)) {
-        strcpy(L_param,  "ACK [last_Request_URI] SIP/2.0\n");
-        sprintf(L_param, "%s%s", L_param, "[last_Via]\n");
-        sprintf(L_param, "%s%s", L_param, "[last_From]\n");
-        sprintf(L_param, "%s%s", L_param, "[last_To]\n");
-        sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n");
-        char * cseq;
-        cseq = get_header_field_code(src_recv,(char *) "CSeq:");
-        if (cseq != NULL) {
-          sprintf(L_param, "%s%s ACK\n", L_param, cseq);
-        }
-        sprintf(L_param, "%s%s", L_param, "Contact: <sip:sipp@[local_ip]:[local_port];transport=[transport]>\n");
-        sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n");
-        sprintf(L_param, "%s%s", L_param, "Subject: Performance Test\n");
-        sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n");
-
-        sendBuffer(createSendingMessage((char*)(L_param), -2));
-
+        sendBuffer(createSendingMessage(get_default_message("ack"), -2));
       } else if (src_recv) {
         /* Call is not established and the reply is not a 4XX, 5XX */
         /* And we already received a message. */
@@ -1802,48 +1757,13 @@
            * and send a BYE afterwards                           */
           ack_is_pending = false;
           /* Send an ACK */
-          strcpy(L_param,  "ACK [last_Request_URI] SIP/2.0\n");
-          sprintf(L_param, "%s%s", L_param, "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n");
-          sprintf(L_param, "%s%s", L_param, "[last_From]\n");
-          sprintf(L_param, "%s%s", L_param, "[last_To]\n");
-          sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n");
-          src_send = last_send_msg ;
-          cseq = get_header_field_code(src_recv,"CSeq:");
-          if (cseq != NULL) {
-            sprintf(L_param, "%s%s ACK\n", L_param, cseq);
-          }
-		    sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n");
-          sprintf(L_param, "%s%s", L_param, "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
-          sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n");
-          sendBuffer(createSendingMessage((char*)(L_param),-1));
-          
+	  sendBuffer(createSendingMessage(get_default_message("ack"), -1));
+
           /* Send the BYE */
-          cseq = NULL;
-          strcpy(L_param,  "BYE [last_Request_URI] SIP/2.0\n");
-          sprintf(L_param, "%s%s", L_param, "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n");
-          sprintf(L_param, "%s%s", L_param, "[last_From]\n");
-          sprintf(L_param, "%s%s", L_param, "[last_To]\n");
-          sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n");
-          cseq = compute_cseq(src_recv);
-          if (cseq != NULL) {
-            sprintf(L_param, "%s%s BYE\n", L_param, compute_cseq(src_recv));
-          }
-		  sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n");
-          sprintf(L_param, "%s%s", L_param,  "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
-          sprintf(L_param, "%s%s", L_param,  "Content-Length: 0\n\n");
-          sendBuffer(createSendingMessage((char*)(L_param),-1));
+	  sendBuffer(createSendingMessage(get_default_message("bye"), -1));
         } else {
           /* Send a CANCEL */
-          strcpy(L_param,  "CANCEL [last_Request_URI] SIP/2.0\n");
-          sprintf(L_param, "%s%s", L_param, "[last_Via]\n");
-          sprintf(L_param, "%s%s", L_param, "[last_From]\n");
-          sprintf(L_param, "%s%s", L_param, "[last_To]\n");
-          sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n");
-          sprintf(L_param, "%sCSeq: 1 CANCEL\n", L_param);
-		    sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n");
-          sprintf(L_param, "%s%s", L_param, "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
-          sprintf(L_param, "%s%s", L_param, "Content-Length: 0\n\n");
-          sendBuffer(createSendingMessage((char*)(L_param),-2));
+	  sendBuffer(createSendingMessage(get_default_message("cancel"), -1));
         }
       } else {
         /* Call is not established and the reply is not a 4XX, 5XX */
@@ -1861,32 +1781,27 @@
       char   L_msg_buffer[SIPP_MAX_MSG_SIZE];
       L_msg_buffer[0] = '\0';
       char * L_param = L_msg_buffer;
-      strcpy(L_param,  "BYE [last_Request_URI] SIP/2.0\n");
-      sprintf(L_param, "%s%s", L_param, "Via: SIP/2.0/[transport] [local_ip]:[local_port];branch=[branch]\n");
-      sprintf(L_param, "%s%s", L_param, "[last_From:]\n");
-      sprintf(L_param, "%s%s", L_param, "[last_To:]\n");
-      sprintf(L_param, "%s%s", L_param, "Call-ID: [call_id]\n");
-      char * cseq;
-      cseq = compute_cseq(src_recv);
-      if (cseq != NULL) {
-        sprintf(L_param, "%s%s BYE\n", L_param, compute_cseq(src_recv));
-      }
-	   sprintf(L_param, "%s%s", L_param, "Max-Forwards: 70\n");
-      sprintf(L_param, "%s%s", L_param,  "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
-      sprintf(L_param, "%s%s", L_param,  "Content-Length: 0\n\n");
-      sendBuffer(createSendingMessage((char*)(L_param),-1));
-    }
-  }
-
-  delete_call(id);
+      sendBuffer(createSendingMessage(get_default_message("bye"), -1));
+    }
+  }
+
+  stopListening();
+  deadcall *deadcall_ptr = NULL;
+  if (deadcall_wait) {
+    char reason[100];
+    sprintf(reason, "aborted at index %d", msg_index);
+    deadcall_ptr = new deadcall(id, reason);
+  }
+  delete this;
+
   return false;
 }
 
 bool call::rejectCall()
 {
-  CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-  CStat::instance()->computeStat(CStat::E_FAILED_CALL_REJECTED);
-  delete_call(id);
+  computeStat(CStat::E_CALL_FAILED);
+  computeStat(CStat::E_FAILED_CALL_REJECTED);
+  delete this;
   return false;
 }
 
@@ -1902,26 +1817,28 @@
   char * peer_dest;
   struct sipp_socket **peer_socket;
 
-  if(scenario[index] -> M_sendCmdData) {
-    // WARNING_P1("---PREPARING_TWIN_CMD---%s---", scenario[index] -> M_sendCmdData); 
-    dest = createSendingMessage(scenario[index] -> M_sendCmdData, -1);
+  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);
     strcat(dest, delimitor);
-    //WARNING_P1("---SEND_TWIN_CMD---%s---", dest); 
+    //WARNING("---SEND_TWIN_CMD---%s---", dest);
 
     int rc;
 
     /* 3pcc extended mode */
-    peer_dest = scenario[index]->peer_dest;
-    if(peer_dest){ 
+    peer_dest = curmsg->peer_dest;
+    if(peer_dest){
       peer_socket = get_peer_socket(peer_dest);
-      rc = write_socket(*peer_socket, dest, strlen(dest), WS_BUFFER);
+      rc = write_socket(*peer_socket, dest, strlen(dest), WS_BUFFER, &call_peer);
     }else {
-      rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER);
+      rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER, &call_peer);
     }
     if(rc <  0) {
-      CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-      CStat::instance()->computeStat(CStat::E_FAILED_CMD_NOT_SENT);
-      delete_call(id);
+      computeStat(CStat::E_CALL_FAILED);
+      computeStat(CStat::E_FAILED_CMD_NOT_SENT);
+      delete this;
       return(-1);
     }
 
@@ -1945,25 +1862,30 @@
 
   strcat(dest, delimitor);
 
-  rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER);
+  rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER, &twinSippSocket->ss_remote_sockaddr);
   if(rc <  0) {
-    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-    CStat::instance()->computeStat(CStat::E_FAILED_CMD_NOT_SENT);
-    delete_call(id);
+    computeStat(CStat::E_CALL_FAILED);
+    computeStat(CStat::E_FAILED_CMD_NOT_SENT);
+    delete this;
     return(-1);
   }
 
   return(0);
 }
 
-char* call::createSendingMessage(SendingMessage *src, int P_index)
+
+char* call::createSendingMessage(SendingMessage *src, int P_index) {
+  static char msg_buffer[SIPP_MAX_MSG_SIZE+2];
+  return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer));
+}
+
+char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len)
 {
   char * length_marker = NULL;
   char * auth_marker = NULL;
   MessageComponent *auth_comp = NULL;
   bool auth_comp_allocated = false;
   int    len_offset = 0;
-  static char msg_buffer[SIPP_MAX_MSG_SIZE+2];
   char *dest = msg_buffer;
   bool supresscrlf = false;
 
@@ -1971,7 +1893,7 @@
 
   for (int i = 0; i < src->numComponents(); i++) {
     MessageComponent *comp = src->getComponent(i);
-    int left = sizeof(msg_buffer) - (dest - msg_buffer);
+    int left = buf_len - (dest - msg_buffer);
     switch(comp->type) {
       case E_Message_Literal:
 	if (supresscrlf) {
@@ -1985,6 +1907,9 @@
 	break;
       case E_Message_Remote_IP:
 	dest += snprintf(dest, left, "%s", remote_ip_escaped);
+	break;
+      case E_Message_Remote_Host:
+	dest += snprintf(dest, left, "%s", remote_host);
 	break;
       case E_Message_Remote_Port:
 	dest += snprintf(dest, left, "%d", remote_port + comp->offset);
@@ -2064,7 +1989,7 @@
 	    (_RCAST(struct sockaddr_in *, &(play_args_v.from)))->sin_port = port;
 	  }
 	} else {
-	  ERROR_P1("media_port keyword with no audio or video on the current line (%s)", begin);
+	  ERROR("media_port keyword with no audio or video on the current line (%s)", begin);
 	}
 #endif
 	dest += sprintf(dest, "%u", port);
@@ -2132,35 +2057,40 @@
       case E_Message_ClockTick:
 	dest += snprintf(dest, left, "%lu", clock_tick);
 	break;
+      case E_Message_Timestamp:
+	struct timeval currentTime;
+	gettimeofday(&currentTime, NULL);
+	dest += snprintf(dest, left, "%s", CStat::formatTime(&currentTime));
+	break;
+      case E_Message_Users:
+	dest += snprintf(dest, left, "%d", users);
+	break;
+      case E_Message_UserID:
+	dest += snprintf(dest, left, "%d", userId);
+	break;
       case E_Message_Variable: {
 	 int varId = comp->varId;
-	 if(varId <= maxVariableUsed) {
-	   if(M_callVariableTable[varId] != NULL) {
-	     if(M_callVariableTable[varId]->isSet()) {
-	       if (M_callVariableTable[varId]->isRegExp()) {
-		 dest += sprintf(dest, "%s", M_callVariableTable[varId]->getMatchingValue());
-	       } else if (M_callVariableTable[varId]->isDouble()) {
-		 dest += sprintf(dest, "%lf", M_callVariableTable[varId]->getDouble());
-	       } else if (M_callVariableTable[varId]->isString()) {
-		 dest += sprintf(dest, "%s", M_callVariableTable[varId]->getString());
-	       } else if (M_callVariableTable[varId]->isBool()) {
-		 dest += sprintf(dest, "true");
-	       }
-	     } else if (M_callVariableTable[varId]->isBool()) {
-	       dest += sprintf(dest, "false");
-	     }
+	 CCallVariable *var = M_callVariableTable->getVar(varId);
+	 if(var->isSet()) {
+	   if (var->isRegExp()) {
+	     dest += sprintf(dest, "%s", var->getMatchingValue());
+	   } else if (var->isDouble()) {
+	     dest += sprintf(dest, "%lf", var->getDouble());
+	   } else if (var->isString()) {
+	     dest += sprintf(dest, "%s", var->getString());
+	   } else if (var->isBool()) {
+	     dest += sprintf(dest, "true");
 	   }
+	 } else if (var->isBool()) {
+	   dest += sprintf(dest, "false");
 	 }
 	 break;
       }
       case E_Message_Fill: {
         int varId = comp->varId;
-	int length = 0;
-	if(varId <= maxVariableUsed && M_callVariableTable[varId]) {
-	  length = (int) M_callVariableTable[varId]->getDouble();
-	  if (length < 0) {
-	    length = 0;
-	  }
+	int length = (int) M_callVariableTable->getVar(varId)->getDouble();
+	if (length < 0) {
+	  length = 0;
 	}
 	char *filltext = comp->literal;
 	int filllen = strlen(filltext);
@@ -2175,7 +2105,7 @@
       }
       case E_Message_Injection: {
 	char *orig_dest = dest;
-	getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, dest);
+	getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, comp->comp_param.field_param.line, dest);
 	/* We are injecting an authentication line. */
 	if (char *tmp = strstr(orig_dest, "[authentication")) {
 	  if (auth_marker) {
@@ -2189,7 +2119,7 @@
 	  tmp = strchr(auth_marker, ']');
 	  char c = *tmp;
 	  *tmp = '\0';
-	  SendingMessage::parseAuthenticationKeyword(auth_comp, auth_marker);
+	  SendingMessage::parseAuthenticationKeyword(call_scenario, auth_comp, auth_marker);
 	  *tmp = c;
 	}
 	if (*(dest - 1) == '\n') {
@@ -2207,11 +2137,29 @@
 	}
 	break;
       }
+      case E_Message_Last_Message:
+        if(last_recv_msg && strlen(last_recv_msg)) {
+	  dest += sprintf(dest, "%s", last_recv_msg);
+	}
+	break;
       case E_Message_Last_Request_URI: {
        char * last_request_uri = get_last_request_uri();
        dest += sprintf(dest, "%s", last_request_uri);
        free(last_request_uri);
-   break;
+       break;
+      }
+      case E_Message_Last_CSeq_Number: {
+       int last_cseq = 0;
+
+       char *last_header = get_last_header("CSeq:");
+       if(last_header) {
+	 last_header += 5;
+	 /* Extract the integer value of the field */
+	 while(isspace(*last_header)) last_header++;
+	 sscanf(last_header,"%d", &last_cseq);
+       }
+       dest += sprintf(dest, "%d", last_cseq + comp->offset);
+       break;
       }
       case E_Message_TDM_Map:
 	if (!use_tdmmap)
@@ -2268,10 +2216,11 @@
 
     /* Need the Method name from the CSeq of the Challenge */
     char method[MAX_HEADER_LEN];
-    tmp = get_last_header("CSeq:") + 5;
+    tmp = get_last_header("CSeq:");
     if(!tmp) {
       ERROR("Could not extract method from cseq of challenge");
     }
+    tmp += 5;
     while(isspace(*tmp) || isdigit(*tmp)) tmp++;
     sscanf(tmp,"%s", method);
 
@@ -2292,11 +2241,22 @@
     /* Build the auth credenticals */
     char uri[MAX_HEADER_LEN];
     sprintf (uri, "%s:%d", remote_ip, remote_port);
-    if (createAuthHeader(auth_comp->comp_param.auth_param.auth_user, auth_comp->comp_param.auth_param.auth_pass,
-	  method, uri, body, dialog_authentication,
-	  auth_comp->comp_param.auth_param.aka_OP, auth_comp->comp_param.auth_param.aka_AMF, auth_comp->comp_param.auth_param.aka_K,
-	  result + authlen) == 0) {
-      ERROR_P1("%s", result + authlen);
+    /* These cause this function to  not be reentrant. */
+    static char my_auth_user[MAX_HEADER_LEN + 2];
+    static char my_auth_pass[MAX_HEADER_LEN + 2];
+    static char my_aka_OP[MAX_HEADER_LEN + 2];
+    static char my_aka_AMF[MAX_HEADER_LEN + 2];
+    static char my_aka_K[MAX_HEADER_LEN + 2];
+
+    createSendingMessage(auth_comp->comp_param.auth_param.auth_user, -2, my_auth_user, sizeof(my_auth_user));
+    createSendingMessage(auth_comp->comp_param.auth_param.auth_pass, -2, my_auth_pass, sizeof(my_auth_pass));
+    createSendingMessage(auth_comp->comp_param.auth_param.aka_K, -2, my_aka_K, sizeof(my_aka_K));
+    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,
+	  my_aka_OP, my_aka_AMF, my_aka_K, result + authlen) == 0) {
+      ERROR("%s", result + authlen);
     }
     authlen = strlen(result);
 
@@ -2314,64 +2274,47 @@
   return msg_buffer;
 }
 
-char* call::createSendingMessage(char *src, int P_index, bool skip_sanity)
-{
-  if (src == NULL) {
-	  ERROR("Unsupported 'send' message in scenario");
-  }
-
-  SendingMessage *msgsrc = new SendingMessage(src, skip_sanity);
-  char *msg = createSendingMessage(msgsrc, P_index);
-  delete msgsrc;
-  return msg;
-}
-
-
-
-#ifdef __3PCC__
 bool call::process_twinSippCom(char * msg)
 {
   int		  search_index;
   bool            found = false;
   T_ActionResult  actionResult;
 
-  if (!running) {
-    paused_calls.remove_paused_call(this);
-    add_running_call(this);
-  }
+  setRunning();
 
   if (checkInternalCmd(msg) == false) {
 
     for(search_index = msg_index;
-      search_index < scenario_len;
+      search_index < call_scenario->length;
       search_index++) {
-      if(scenario[search_index] -> M_type != MSG_TYPE_RECVCMD) {
-        if(scenario[search_index] -> optional) {
-          continue;
-        }
-        /* The received message is different from the expected one */
-	TRACE_MSG((s, "Unexpected control message received (I was expecting a different type of message):\n%s\n", msg));
-        return rejectCall();
+      if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECVCMD) {
+	if(call_scenario->messages[search_index] -> optional) {
+	  continue;
+	}
+	/* 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);
+	return rejectCall();
       } else {
-        if(extendedTwinSippMode){                   // 3pcc extended mode 
+	if(extendedTwinSippMode){                   // 3pcc extended mode
 	  if(check_peer_src(msg, search_index)){
-            found = true;
-            break;
+	    found = true;
+	    break;
 	  } else{
-	    WARNING_P1("Unexpected sender for the received peer message \n%s\n", msg);
+	    WARNING("Unexpected sender for the received peer message \n%s\n", msg);
 	    return rejectCall();
-	    }
-	 }
-	 else {
-        found = true;
-        break;
-      }
-    }
-    }
-    
+	  }
+	}
+	else {
+	  found = true;
+	  break;
+	}
+      }
+    }
+
     if (found) {
-      scenario[search_index]->M_nbCmdRecv ++;
-      
+      call_scenario->messages[search_index]->M_nbCmdRecv ++;
+      do_bookkeeping(search_index);
+
       // variable treatment
       // Remove \r, \n at the end of a received command
       // (necessary for transport, to be removed for usage)
@@ -2380,7 +2323,7 @@
         msg[strlen(msg)-2] = 0;
       }
       actionResult = executeAction(msg, search_index);
-      
+
       if(actionResult != call::E_AR_NO_ERROR) {
         // Store last action result if it is an error
         // and go on with the scenario
@@ -2390,12 +2333,11 @@
         }
       }
     } else {
-      TRACE_MSG((s, "Unexpected control message received (no such message found):\n%s\n", msg));
+      TRACE_MSG("Unexpected control message received (no such message found):\n%s\n", msg);
       return rejectCall();
     }
     msg_index = search_index; //update the state machine
     return(next());
-    
   } else {
     return (false);
   }
@@ -2426,7 +2368,7 @@
   if (strcmp(L_ptr1, "abort_call") == 0) {
     *L_ptr2 = L_backup;
     abortCall();
-    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
+    computeStat(CStat::E_CALL_FAILED);
     return (true);
   }
 
@@ -2454,7 +2396,7 @@
   if(!*L_ptr2) { return (false); }
   L_backup = *L_ptr2;
   *L_ptr2 = 0;
-  if (strcmp(L_ptr1, scenario[search_index] -> peer_src) == 0) {
+  if (strcmp(L_ptr1, call_scenario->messages[search_index] -> peer_src) == 0) {
     *L_ptr2 = L_backup;
     return(true);
   }
@@ -2462,7 +2404,6 @@
   *L_ptr2 = L_backup;
   return (false);
 }
-#endif
 
 
 void call::extract_cseq_method (char* method, char* msg)
@@ -2485,6 +2426,28 @@
       method[nbytes] = '\0';
     }
   }
+}
+
+void call::extract_transaction (char* txn, char* msg)
+{
+  char *otxn = txn;
+  char *via = get_header_content(msg, "via:");
+  if (!via) {
+    txn[0] = '\0';
+    return;
+  }
+
+  char *branch = strstr(via, ";branch=");
+  if (!branch) {
+    txn[0] = '\0';
+    return;
+  }
+
+  branch += strlen(";branch=");
+  while (*branch && *branch != ';' && *branch != ',' && !isspace(*branch)) {
+    *txn++ = *branch++;
+  }
+  *txn = '\0';
 }
 
 void call::formatNextReqUrl (char* next_req_url)
@@ -2634,39 +2597,50 @@
   }
 }
 
-bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod)
-{         
+bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn)
+{
   int        result;
-          
-  if ((reply_code) && ((scenario[index] -> recv_response) == reply_code) && \
-     (index == 0 || ((scenario[index]->recv_response_for_cseq_method_list) && \
-     (strstr(scenario[index]->recv_response_for_cseq_method_list, responsecseqmethod))))) {
-        return true;
-  }   
-    
-  if ((scenario[index] -> recv_request) && \
-     (!strcmp(scenario[index] -> recv_request, request))) {
-        return true;
-  } 
-  
-  if ((scenario[index] -> recv_request) && (scenario[index] -> regexp_match)) {
-  
-     if (scenario[index] -> regexp_compile == NULL) {
-        regex_t *re = new regex_t;
-        if (regcomp(re, scenario[index] -> recv_request, REG_EXTENDED|REG_NOSUB)) {
-           // regexp is not well formed
-           scenario[index] -> regexp_match = 0;
-           free(re);
-           return false;
-        }
-        scenario[index] -> regexp_compile = re;
-     }
-
-     result = regexec(scenario[index] -> regexp_compile, request, (size_t)0, NULL, 0);
-     if (!result) return true;
+  message *curmsg = call_scenario->messages[index];
+
+  if ((curmsg -> recv_request)) {
+    if (curmsg->regexp_match) {
+      if (curmsg -> regexp_compile == NULL) {
+	regex_t *re = new regex_t;
+	if (regcomp(re, curmsg -> recv_request, REG_EXTENDED|REG_NOSUB)) {
+	  ERROR("Invalid regular expression for index %d: %s", curmsg->recv_request);
+	}
+	curmsg -> regexp_compile = re;
+      }
+      return !regexec(curmsg -> regexp_compile, request, (size_t)0, NULL, 0);
+    } else {
+      return !strcmp(curmsg -> recv_request, 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)) {
+	return true;
+      } else {
+	return false;
+      }
+    } else if (index == 0) {
+      /* Always true for the first message. */
+      return true;
+    } else if (curmsg->recv_response_for_cseq_method_list &&
+	strstr(curmsg->recv_response_for_cseq_method_list, responsecseqmethod)) {
+      /* If we do not have a transaction defined, we just check the CSEQ method. */
+      return true;
+    } else {
+      return false;
+    }
   }
 
   return false;
+}
+
+void call::queue_up(char *msg) {
+  free(queued_msg);
+  queued_msg = strdup(msg);
 }
 
 bool call::process_incoming(char * msg)
@@ -2674,53 +2648,51 @@
   int             reply_code;
   static char     request[65];
   char            responsecseqmethod[65];
+  char            txn[MAX_HEADER_LEN];
   unsigned long   cookie;
   char          * ptr;
   int             search_index;
   bool            found = false;
   T_ActionResult  actionResult;
 
-  int             L_case = 0 ;
-
-  if (!running) {
-    paused_calls.remove_paused_call(this);
-    add_running_call(this);
-  }
+  setRunning();
 
   /* Ignore the messages received during a pause if -pause_msg_ign is set */
-  if(scenario[msg_index] -> M_type == MSG_TYPE_PAUSE && pause_msg_ign) return(true);
+  if(call_scenario->messages[msg_index] -> M_type == MSG_TYPE_PAUSE && pause_msg_ign) return(true);
 
   /* Authorize nop as a first command, even in server mode */
-  if((msg_index == 0) && (scenario[msg_index] -> M_type == MSG_TYPE_NOP)) {
-    actionResult = executeAction(NULL, msg_index);
-    return next();
+  if((msg_index == 0) && (call_scenario->messages[msg_index] -> M_type == MSG_TYPE_NOP)) {
+    queue_up (msg);
+    paused_until = 0;
+    return run();
   }
   responsecseqmethod[0] = '\0';
+  txn[0] = '\0';
 
   if((transport == T_UDP) && (retrans_enabled)) {
     /* Detects retransmissions from peer and retransmit the
      * message which was sent just after this one was received */
     cookie = hash(msg);
-    if(recv_retrans_hash == cookie) {
+    if((recv_retrans_recv_index >= 0) && (recv_retrans_hash == cookie)) {
 
       int status;
 
       if(lost(recv_retrans_recv_index)) {
-	TRACE_MSG((s, "%s message (retrans) lost (recv).",
-	      TRANSPORT_TO_STRING(transport)));
+	TRACE_MSG("%s message (retrans) lost (recv).",
+	      TRANSPORT_TO_STRING(transport));
 
 	if(comp_state) { comp_free(&comp_state); }
-	scenario[recv_retrans_recv_index] -> nb_lost++;
+	call_scenario->messages[recv_retrans_recv_index] -> nb_lost++;
 	return true;
       }
 
-      scenario[recv_retrans_recv_index] -> nb_recv_retrans++;
+      call_scenario->messages[recv_retrans_recv_index] -> nb_recv_retrans++;
 
       send_scene(recv_retrans_send_index, &status);
 
       if(status == 0) {
-	scenario[recv_retrans_send_index] -> nb_sent_retrans++;
-	CStat::instance()->computeStat(CStat::E_RETRANSMISSION);
+	call_scenario->messages[recv_retrans_send_index] -> nb_sent_retrans++;
+	computeStat(CStat::E_RETRANSMISSION);
       } else if(status < 0) {
 	return false;
       }
@@ -2728,7 +2700,7 @@
       return true;
     }
 
-    if(last_recv_hash == cookie) {
+    if((last_recv_index >= 0) && (last_recv_hash == cookie)) {
       /* This one has already been received, but not processed
        * yet => (has not triggered something yet) so we can discard.
        *
@@ -2742,7 +2714,7 @@
        * This case can also appear in case of message duplication by
        * the network. This should not be considered as an unexpected.
        */
-      scenario[last_recv_index]->nb_recv_retrans++;
+      call_scenario->messages[last_recv_index]->nb_recv_retrans++;
       return true;
     }
   }
@@ -2782,6 +2754,7 @@
     request[0]=0;
     // extract the cseq method from the response
     extract_cseq_method (responsecseqmethod, msg);
+    extract_transaction (txn, msg);
   } else if(ptr = strchr(msg, ' ')) {
     if((ptr - msg) < 64) {
       memcpy(request, msg, ptr - msg);
@@ -2803,21 +2776,21 @@
 
       reply_code = 0;
     } else {
-      ERROR_P1("SIP method too long in received message '%s'",
+      ERROR("SIP method too long in received message '%s'",
                msg);
     }
   } else {
-    ERROR_P1("Invalid sip message received '%s'",
+    ERROR("Invalid sip message received '%s'",
              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 < scenario_len;
+      search_index < call_scenario->length;
       search_index++) {
-    if(!matches_scenario(search_index, reply_code, request, responsecseqmethod)) {
-      if(scenario[search_index] -> optional) {
+    if(!matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) {
+      if(call_scenario->messages[search_index] -> optional) {
         continue;
       }
       /* The received message is different for the expected one */
@@ -2838,23 +2811,44 @@
     for(search_index = msg_index - 1;
         search_index >= 0;
         search_index--) {
-      if (scenario[search_index]->optional == OPTIONAL_FALSE) contig = false;
-      if(matches_scenario(search_index, reply_code, request, responsecseqmethod)) {
-        if (contig || scenario[search_index]->optional == OPTIONAL_GLOBAL) {
+      if (call_scenario->messages[search_index]->optional == OPTIONAL_FALSE) contig = false;
+      if(matches_scenario(search_index, reply_code, request, responsecseqmethod, txn)) {
+        if (contig || call_scenario->messages[search_index]->optional == OPTIONAL_GLOBAL) {
          found = true;
          break;  
         } else {
-          /*
-           * we received a non mandatory msg for an old transaction (this could be due to a retransmit.
-           * If this response is for an INVITE transaction, retransmit the ACK to quench retransmits.
-           */
-          if ( (reply_code) &&
-             (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)) ) &&
-             (scenario[search_index+1]->M_type == MSG_TYPE_SEND) &&
-             (scenario[search_index+1]->send_scheme->isAck()) ) {
-            sendBuffer(createSendingMessage(scenario[search_index+1] -> send_scheme, (search_index+1)));
-            return true;
-          }
+	  if (int checkTxn = call_scenario->messages[search_index]->response_txn) {
+	    /* This is a reply to an old transaction. */
+	    if (!strcmp(txnID[checkTxn - 1], 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);
+		  return true;
+		} else if (call_scenario->messages[search_index + 1]->M_type == MSG_TYPE_SEND && call_scenario->messages[search_index + 1]->send_scheme->isAck()) {
+		  /* 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)));
+		    return true;
+		  }
+		}
+		/* This is a non-provisional message for the transaction, and
+		 * we have already gotten our allowable response. */
+	    }
+	  } else {
+	    /*
+	     * we received a non mandatory msg for an old transaction (this could be due to a retransmit.
+	     * If this response is for an INVITE transaction, retransmit the ACK to quench retransmits.
+	     */
+	    if ( (reply_code) &&
+		(0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)) ) &&
+		(call_scenario->messages[search_index+1]->M_type == MSG_TYPE_SEND) &&
+		(call_scenario->messages[search_index+1]->send_scheme->isAck()) ) {
+	      sendBuffer(createSendingMessage(call_scenario->messages[search_index+1] -> send_scheme, (search_index+1)));
+	      return true;
+	    }
+	  }
         }
       }
     }
@@ -2862,17 +2856,43 @@
 
   /* If it is still not found, process an unexpected message */
   if(!found) {
-    if ((L_case = checkAutomaticResponseMode(request)) == 0) {
-      if (!process_unexpected(msg)) {
-        return false; // Call aborted by unexpected message handling
+    if (call_scenario->unexpected_jump >= 0) {
+      bool recursive = false;
+      if (call_scenario->retaddr >= 0) {
+	if (M_callVariableTable->getVar(call_scenario->retaddr)->getDouble() != 0) {
+	  /* We are already in a jump! */
+	  recursive = true;
+	} else {
+	  M_callVariableTable->getVar(call_scenario->retaddr)->setDouble(msg_index);
+	}
+      }
+      if (!recursive) {
+	if (call_scenario->pausedaddr >= 0) {
+	  M_callVariableTable->getVar(call_scenario->pausedaddr)->setDouble(paused_until);
+	}
+	msg_index = call_scenario->unexpected_jump;
+	queue_up(msg);
+	paused_until = 0;
+	return run();
+      } else {
+	if (!process_unexpected(msg)) {
+	  return false; // Call aborted by unexpected message handling
+	}
       }
     } else {
-      // call aborted by automatic response mode if needed
-      return automaticResponseMode(L_case, msg);
-    }
-  }
-
-  int test = (!found) ? -1 : scenario[search_index]->test;
+      T_AutoMode L_case;
+      if ((L_case = checkAutomaticResponseMode(request)) == 0) {
+	if (!process_unexpected(msg)) {
+	  return false; // Call aborted by unexpected message handling
+	}
+      } else {
+	// call aborted by automatic response mode if needed
+	return automaticResponseMode(L_case, msg);
+      }
+    }
+  }
+
+  int test = (!found) ? -1 : call_scenario->messages[search_index]->test;
   /* test==0: No branching"
    * test==-1 branching without testing"
    * test>0   branching with testing
@@ -2880,10 +2900,10 @@
 
   /* Simulate loss of messages */
   if(lost(search_index)) {
-    TRACE_MSG((s, "%s message lost (recv).", 
-               TRANSPORT_TO_STRING(transport)));
+    TRACE_MSG("%s message lost (recv).",
+               TRANSPORT_TO_STRING(transport));
     if(comp_state) { comp_free(&comp_state); }
-    scenario[search_index] -> nb_lost++;
+    call_scenario->messages[search_index] -> nb_lost++;
     return true;
   }
 
@@ -2892,12 +2912,12 @@
   do_bookkeeping(search_index);
 
   /* Increment the recv counter */
-  scenario[search_index] -> nb_recv++;
+  call_scenario->messages[search_index] -> nb_recv++;
 
   // Action treatment
   if (found) {
-    //WARNING_P1("---EXECUTE_ACTION_ON_MSG---%s---", msg); 
-    
+    //WARNING("---EXECUTE_ACTION_ON_MSG---%s---", msg);
+
     actionResult = executeAction(msg, search_index);
 
     if(actionResult != call::E_AR_NO_ERROR) {
@@ -2953,7 +2973,7 @@
   }
 
   /* store the route set only once. TODO: does not support target refreshes!! */
-  if (scenario[search_index] -> bShouldRecordRoutes &&
+  if (call_scenario->messages[search_index] -> bShouldRecordRoutes &&
           NULL == dialog_route_set ) {
 
       next_req_url = (char*) calloc(1, MAX_HEADER_LEN);
@@ -2962,7 +2982,7 @@
       memset(rr, 0, sizeof(rr));
       strcpy(rr, get_header_content(msg, (char*)"Record-Route:"));
 
-      // WARNING_P1("rr [%s]", rr);
+      // WARNING("rr [%s]", rr);
       char ch[MAX_HEADER_LEN];
       strcpy(ch, get_header_content(msg, (char*)"Contact:"));
 
@@ -2982,12 +3002,12 @@
       {
         computeRouteSetAndRemoteTargetUri (rr, ch, true);
       }
-      // WARNING_P1("next_req_url is [%s]", next_req_url);
+      // WARNING("next_req_url is [%s]", next_req_url);
   }
 
 #ifdef _USE_OPENSSL
   /* store the authentication info */
-  if ((scenario[search_index] -> bShouldAuthenticate) && 
+  if ((call_scenario->messages[search_index] -> bShouldAuthenticate) &&
           (reply_code == 401 || reply_code == 407)) {
 
       /* is a challenge */
@@ -3008,6 +3028,11 @@
       dialog_challenge_type = reply_code;
   }
 #endif
+
+  /* If we are not advancing state, we should quite before we change this stuff. */
+  if (!call_scenario->messages[search_index]->advance_state) {
+    return true;
+  }
 
   /* Store last received message information for all messages so that we can
    * correctly identify retransmissions, and use its body for inclusion
@@ -3019,32 +3044,30 @@
 
   /* If this was a mandatory message, or if there is an explicit next label set
    * we must update our state machine.  */
-  if (!(scenario[search_index] -> optional) ||
-       scenario[search_index]->next && 
-      ((test == -1) ||
-       (test <= maxVariableUsed && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet()))
+  if (!(call_scenario->messages[search_index] -> optional) ||
+       call_scenario->messages[search_index]->next &&
+       ((test == -1) || (M_callVariableTable->getVar(test)->isSet()))
      ) {
     /* If we are paused, then we need to wake up so that we properly go through the state machine. */
     paused_until = 0;
     msg_index = search_index;
     return next();
   } else {
-    unsigned int timeout = call_wake(this);
+    unsigned int timeout = wake();
     unsigned int candidate;
 
-    if (scenario[search_index]->next && test <= maxVariableUsed && 
-       M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet()) {
-      WARNING_P1("Last message generates an error and will not be used for next sends (for last_ variables):\r\n%s",msg);
+    if (call_scenario->messages[search_index]->next && M_callVariableTable->getVar(test)->isSet()) {
+      WARNING("Last message generates an error and will not be used for next sends (for last_ variables):\r\n%s",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 < scenario_len; search_index++) {
-      if(scenario[search_index] -> M_type != MSG_TYPE_RECV) {
-	break;
-      }
-      candidate = scenario[search_index] -> timeout;
+    for(search_index++; search_index < call_scenario->length; search_index++) {
+      if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECV) {
+	break;
+      }
+      candidate = call_scenario->messages[search_index] -> timeout;
       if (candidate == 0) {
 	if (defl_recv_timeout == 0) {
 	  continue;
@@ -3056,17 +3079,14 @@
       }
     }
 
-    if (!remove_running_call(this)) {
-      ERROR("Tried to remove a running call that wasn't running!\n");
-    }
-    paused_calls.add_paused_call(this, true);
+    setPaused();
   }
   return true;
 }
 
 double call::get_rhs(CAction *currentAction) {
   if (currentAction->getVarInId()) {
-    return M_callVariableTable[currentAction->getVarInId()]->getDouble();
+    return M_callVariableTable->getVar(currentAction->getVarInId())->getDouble();
   } else {
     return currentAction->getDoubleValue();
   }
@@ -3076,138 +3096,179 @@
 {
   CActions*  actions;
   CAction*   currentAction;
-  CVariable* scenVariable;
-  char       msgPart[MAX_SUB_MESSAGE_LENGTH];
-  int        currentId;
-
-  actions = scenario[scenarioIndex]->M_actions;
+
+  actions = call_scenario->messages[scenarioIndex]->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) {
-          currentId = currentAction->getVarId();
-          scenVariable = scenVariableTable[currentId][scenarioIndex];
-          if(scenVariable != NULL) {
-            if(currentAction->getLookingPlace() == CAction::E_LP_HDR) {
-              extractSubMessage
-                                (msg, 
-                                currentAction->getLookingChar(), 
-                                msgPart,
-                                currentAction->getCaseIndep(),
-                                currentAction->getOccurence(),
-                                currentAction->getHeadersOnly()); 
-        
-              if(strlen(msgPart) > 0) {
-          
-                scenVariable->executeRegExp(msgPart, 
-                                  M_callVariableTable,
-				  currentId,
-				  currentAction->getNbSubVarId(),
-                                  currentAction->getSubVarId());
-          
-                if( (!(M_callVariableTable[currentId]->isSet())) 
-                && (currentAction->getCheckIt() == true) ) {
-                  // the message doesn't match and the checkit 
-                  // action say it MUST match
-                  // Allow easier regexp debugging
-                  WARNING_P2("Failed regexp match: looking "
-                  "in '%s', with regexp '%s'", 
-                  msgPart, 
-                  scenVariable->
-                  getRegularExpression());
-                  // --> Call will be marked as failed
-                  return(call::E_AR_REGEXP_DOESNT_MATCH);
-                }
-              } else {// sub part of message not found
-                if( currentAction->getCheckIt() == true ) {
-                  // 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_P2("Failed regexp match: header %s not found in message %s\n", currentAction->getLookingChar(), msg);
-                  return(call::E_AR_HDR_NOT_FOUND);
-                } 
-              }
-            } else {// we must look in the entire message
-              // WARNING_P1("LOOKING IN MSG -%s-", msg);
-                scenVariable->executeRegExp(msg, 
-                                  M_callVariableTable,
-				  currentId,
-				  currentAction->getNbSubVarId(),
-                                  currentAction->getSubVarId());
-              if((!(M_callVariableTable[currentId]->isSet())) 
-              && (currentAction->getCheckIt() == true) ) {
-                // the message doesn't match and the checkit 
-                // action say it MUST match
-                // Allow easier regexp debugging
-                WARNING_P2("Failed regexp match: looking in '%s'"
-                ", with regexp '%s'", 
-                msg, 
-                scenVariable->getRegularExpression());
-                // --> rejecting the call
-                return(call::E_AR_REGEXP_DOESNT_MATCH);
-              }
-            }
-          } // end if scen variable != null
+	  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);
+	  }
         } else /* end action == E_AT_ASSIGN_FROM_REGEXP */ 
             if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) {
-	      M_callVariableTable[currentAction->getVarId()]->setDouble(currentAction->getDoubleValue());
+	      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;
+	  }
+	  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 {
+	    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);
+#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[currentAction->getVarId()]->getDouble();
+	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
 	  double operand = get_rhs(currentAction);
-	  M_callVariableTable[currentAction->getVarId()]->setDouble(value + operand);
+	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand);
         } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) {
-	  double value = M_callVariableTable[currentAction->getVarId()]->getDouble();
+	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
 	  double operand = get_rhs(currentAction);
-	  M_callVariableTable[currentAction->getVarId()]->setDouble(value - operand);
+	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand);
         } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) {
-	  double value = M_callVariableTable[currentAction->getVarId()]->getDouble();
+	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
 	  double operand = get_rhs(currentAction);
-	  M_callVariableTable[currentAction->getVarId()]->setDouble(value * operand);
+	  M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand);
         } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) {
-	  double value = M_callVariableTable[currentAction->getVarId()]->getDouble();
+	  double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
 	  double operand = get_rhs(currentAction);
 	  if (operand == 0) {
-	    WARNING_P2("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId());
+	    WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId());
 	  } else {
-	    M_callVariableTable[currentAction->getVarId()]->setDouble(value / operand);
+	    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[currentAction->getVarId()]->setBool(value);
+	  M_callVariableTable->getVar(currentAction->getVarId())->setBool(value);
         } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) {
-	  char *rhs = M_callVariableTable[currentAction->getVarInId()]->getString();
+	  char *rhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString();
 	  char *lhs = currentAction->getStringValue();
 	  int value = strcmp(rhs, lhs);
-	  M_callVariableTable[currentAction->getVarId()]->setDouble((double)value);
+	  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[currentAction->getVarInId()]->toDouble(&value)) {
-	    M_callVariableTable[currentAction->getVarId()]->setDouble(value);
+	  if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) {
+	    M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
 	  } else {
-	    WARNING_P2("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId());
+	    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[currentAction->getVarId()]->setDouble(value);
+	  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*/, true /* skip sanity check */);
+            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[currentAction->getVarId()]->setString(str);
+	    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*/, true /* skip sanity check */);
-            LOG_MSG((s, "%s\n", x));
+            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->getCmdLine(), -2 /* do not add crlf*/, true /* skip sanity check. */);
-                // TRACE_MSG((s, "Trying to execute [%s]", x)); 
+                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())
                 {
@@ -3225,7 +3286,7 @@
                          int ret;
                          ret = system(x); // second child runs
                          if(ret == -1) {
-                           WARNING_P1("system call error for %s",x);
+                           WARNING("system call error for %s",x);
                           }
                         }
                        exit(EXIT_OTHER); 
@@ -3237,7 +3298,7 @@
                        pid_t ret;
                        while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) {
                        if (ret != -1) {
-                          ERROR_P2("waitpid returns %1d for child %1d",ret,l_pid); 
+                          ERROR("waitpid returns %1d for child %1d",ret,l_pid);
                          }
                        }
                        break;
@@ -3363,39 +3424,54 @@
   }
 }
 
-void call::getFieldFromInputFile(const char *fileName, int field, char*& dest)
+void call::getFieldFromInputFile(const char *fileName, int field, SendingMessage *lineMsg, char*& dest)
 {
   if (inFiles.find(fileName) == inFiles.end()) {
-    ERROR_P1("Invalid injection file: %s", fileName);
+    ERROR("Invalid injection file: %s", fileName);
   }
   int line = (*m_lineNumber)[fileName];
+  if (lineMsg) {
+	char lineBuffer[20];
+	char *endptr;
+	createSendingMessage(lineMsg, -2, lineBuffer, sizeof(lineBuffer));
+	line = (int) strtod(lineBuffer, &endptr);
+	if (*endptr != 0) {
+	  ERROR("Invalid line number generated: '%s'", lineBuffer);
+	}
+	if (line > inFiles[fileName]->numLines()) {
+	  line = -1;
+	}
+  }
   if (line < 0) {
     return;
   }
   dest += inFiles[fileName]->getField(line, field, dest, SIPP_MAX_MSG_SIZE);
 }
 
-int  call::checkAutomaticResponseMode(char * P_recv) {
-
-  int L_res = 0 ;
+call::T_AutoMode call::checkAutomaticResponseMode(char * P_recv) {
+
+  int L_res = E_AM_DEFAULT ;
 
   if (strcmp(P_recv, "BYE")==0) {
-    L_res = 1 ;
+    return E_AM_UNEXP_BYE;
   } else if (strcmp(P_recv, "CANCEL") == 0) {
-    L_res = 2 ;
+    return E_AM_UNEXP_CANCEL;
   } else if (strcmp(P_recv, "PING") == 0) {
-    L_res = 3 ;
+    return E_AM_PING;
   } else if (((strcmp(P_recv, "INFO") == 0) || (strcmp(P_recv, "NOTIFY") == 0) || (strcmp(P_recv, "UPDATE") == 0)) 
                && (auto_answer == true)){
-    L_res = 4 ;
-  }
-
-  return (L_res) ;
-  
-}
-
-
-bool call::automaticResponseMode(int P_case, char * P_recv)
+    return E_AM_AA;
+  } else {
+    return E_AM_DEFAULT;
+  }
+}
+
+void call::setLastMsg(const char *msg) {
+  last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1);
+  strcpy(last_recv_msg, msg);
+}
+
+bool call::automaticResponseMode(T_AutoMode P_case, char * P_recv)
 {
 
   int res ;
@@ -3403,113 +3479,81 @@
   bool last_recv_msg_saved = false;
 
   switch (P_case) {
-  case 1: // response for an unexpected BYE
+  case E_AM_UNEXP_BYE: // response for an unexpected BYE
     // usage of last_ keywords
     last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1);
     strcpy(last_recv_msg, P_recv);
 
     // The BYE is unexpected, count it
-    scenario[msg_index] -> nb_unexp++;
-    if (default_behavior) {
-      WARNING_P1("Aborting call on an unexpected BYE for call: %s", (id==NULL)?"none":id);
-    sendBuffer(createSendingMessage(
-                    (char*)"SIP/2.0 200 OK\n"
-                    "[last_Via:]\n"
-                    "[last_From:]\n"
-                    "[last_To:]\n"
-                    "[last_Call-ID:]\n"
-                    "[last_CSeq:]\n"
-                    "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n"
-                    "Content-Length: 0\n\n"
-                    , -1)) ;
-
-#ifdef __3PCC__
+    call_scenario->messages[msg_index] -> nb_unexp++;
+    if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) {
+      WARNING("Aborting call on an unexpected BYE for call: %s", (id==NULL)?"none":id);
+      if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
+	sendBuffer(createSendingMessage(get_default_message("200"), -1));
+      }
+
+      // if twin socket call => reset the other part here
+      if (twinSippSocket && (msg_index > 0)) {
+	res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1));
+      }
+      computeStat(CStat::E_CALL_FAILED);
+      computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
+      delete this;
+    } else {
+      WARNING("Continuing call on an unexpected BYE for call: %s", (id==NULL)?"none":id);
+    }
+      break ;
+      
+  case E_AM_UNEXP_CANCEL: // response for an unexpected cancel
+    // usage of last_ keywords
+    last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1);
+    strcpy(last_recv_msg, P_recv);
+
+    // The CANCEL is unexpected, count it
+    call_scenario->messages[msg_index] -> nb_unexp++;
+    if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) {
+      WARNING("Aborting call on an unexpected CANCEL for call: %s", (id==NULL)?"none":id);
+      if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
+	sendBuffer(createSendingMessage(get_default_message("200"), -1));
+      }
+    
     // if twin socket call => reset the other part here 
     if (twinSippSocket && (msg_index > 0)) {
       res = sendCmdBuffer
-      (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n", -1));
-    }
-#endif /* __3PCC__ */
-      CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-      CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
-      delete_call(id);
+      (createSendingMessage(get_default_message("3pcc_abort"), -1));
+    }
+    
+    computeStat(CStat::E_CALL_FAILED);
+    computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
+    delete this;
     } else {
-      WARNING_P1("Continuing call on an unexpected BYE for call: %s", (id==NULL)?"none":id);
-    }
-      break ;
-      
-  case 2: // response for an unexpected cancel
-    // usage of last_ keywords
-    last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1);
-    strcpy(last_recv_msg, P_recv);
-
-    // The CANCEL is unexpected, count it
-    scenario[msg_index] -> nb_unexp++;
-    if (default_behavior) {
-      WARNING_P1("Aborting call on an unexpected CANCEL for call: %s", (id==NULL)?"none":id);
-    sendBuffer(createSendingMessage(
-                      (char*)"SIP/2.0 200 OK\n"
-                      "[last_Via:]\n"
-                      "[last_From:]\n"
-                      "[last_To:]\n"
-                      "[last_Call-ID:]\n"
-                      "[last_CSeq:]\n"
-                      "Contact: sip:sipp@[local_ip]:[local_port]\n"
-                      "Content-Length: 0\n\n"
-                      , -1)) ;
-    
-#ifdef __3PCC__
-    // if twin socket call => reset the other part here 
-    if (twinSippSocket && (msg_index > 0)) {
-      res = sendCmdBuffer
-      (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n", -1));
-    }
-#endif /* __3PCC__ */
-    
-    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
-    CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
-    delete_call(id);
-    } else {
-      WARNING_P1("Continuing call on unexpected CANCEL for call: %s", (id==NULL)?"none":id);
+      WARNING("Continuing call on unexpected CANCEL for call: %s", (id==NULL)?"none":id);
     }
     break ;
       
-  case 3: // response for a random ping
+  case E_AM_PING: // response for a random ping
     // usage of last_ keywords
     last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1);
     strcpy(last_recv_msg, P_recv);
     
-   if (default_behavior) {
-    WARNING_P1("Automatic response mode for an unexpected PING for call: %s", (id==NULL)?"none":id);
-    count_in_stats = false; // Call must not be counted in statistics
-    sendBuffer(createSendingMessage(
-                    (char*)"SIP/2.0 200 OK\n"
-                    "[last_Via:]\n"
-                    "[last_Call-ID:]\n"
-                    "[last_To:]\n"
-                    "[last_From:]\n"
-                    "[last_CSeq:]\n"
-                    "Contact: sip:sipp@[local_ip]:[local_port]\n"
-                    "Content-Length: 0\n\n"
-                    , -1)) ;
+   if (default_behaviors & DEFAULT_BEHAVIOR_PINGREPLY) {
+    WARNING("Automatic response mode for an unexpected PING for call: %s", (id==NULL)?"none":id);
+    sendBuffer(createSendingMessage(get_default_message("200"), -1));
     // Note: the call ends here but it is not marked as bad. PING is a 
     //       normal message.
-#ifdef __3PCC__
     // if twin socket call => reset the other part here 
     if (twinSippSocket && (msg_index > 0)) {
-      res = sendCmdBuffer
-      (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n\n",-1));
-    }
-#endif /* __3PCC__ */
+      res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1));
+    }
     
-    CStat::instance()->computeStat(CStat::E_AUTO_ANSWERED);
-    delete_call(id);
+    CStat::globalStat(CStat::E_AUTO_ANSWERED);
+    delete this;
     } else {
-      WARNING_P1("Do not answer on an unexpected PING for call: %s", (id==NULL)?"none":id);
+      WARNING("Do not answer on an unexpected PING for call: %s", (id==NULL)?"none":id);
     }
     break ;
 
-  case 4: // response for a random INFO, UPDATE or NOTIFY
+  case E_AM_AA: // response for a random INFO, UPDATE or NOTIFY
     // store previous last msg if msg is INFO, UPDATE or NOTIFY
     // restore last_recv_msg to previous one
     // after sending ok
@@ -3523,17 +3567,8 @@
     last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1);
     strcpy(last_recv_msg, P_recv);
 
-    WARNING_P1("Automatic response mode for an unexpected INFO, UPDATE or NOTIFY for call: %s", (id==NULL)?"none":id);
-    sendBuffer(createSendingMessage(
-                    (char*)"SIP/2.0 200 OK\n"
-                    "[last_Via:]\n"
-                    "[last_Call-ID:]\n"
-                    "[last_To:]\n"
-                    "[last_From:]\n"
-                    "[last_CSeq:]\n"
-                    "Contact: sip:sipp@[local_ip]:[local_port]\n"
-                    "Content-Length: 0\n\n"
-                    , -1)) ;
+    WARNING("Automatic response mode for an unexpected INFO, UPDATE or NOTIFY for call: %s", (id==NULL)?"none":id);
+    sendBuffer(createSendingMessage(get_default_message("200"), -1));
 
     // restore previous last msg
     if (last_recv_msg_saved == true) {
@@ -3544,52 +3579,16 @@
         old_last_recv_msg = NULL;
       }
     }
-    CStat::instance()->computeStat(CStat::E_AUTO_ANSWERED);
+    CStat::globalStat(CStat::E_AUTO_ANSWERED);
     return true;
     break;
 
-    case 5: // response for an out of call message
-    old_last_recv_msg = NULL;
-    if (last_recv_msg != NULL) {
-      last_recv_msg_saved = true;
-      old_last_recv_msg = (char *) malloc(strlen(last_recv_msg)+1);
-      strcpy(old_last_recv_msg,last_recv_msg);
-    }
-    // usage of last_ keywords
-    last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1);
-    strcpy(last_recv_msg, P_recv);
-
-    WARNING("Automatic response mode for an out of call message");
-    sendBuffer(createSendingMessage(
-                    (char*)"SIP/2.0 200 OK\n"
-                    "[last_Via:]\n"
-                    "[last_Call-ID:]\n"
-                    "[last_To:]\n"
-                    "[last_From:]\n"
-                    "[last_CSeq:]\n"
-                    "Contact: sip:sipp@[local_ip]:[local_port]\n"
-                    "Content-Length: 0\n\n"
-                    , -1)) ;
-
-    // restore previous last msg
-    if (last_recv_msg_saved == true) {
-      last_recv_msg = (char *) realloc(last_recv_msg, strlen(old_last_recv_msg) + 1);
-      strcpy(last_recv_msg, old_last_recv_msg);
-      if (old_last_recv_msg != NULL) {
-        free(old_last_recv_msg);
-        old_last_recv_msg = NULL;
-      }
-    }
-    CStat::instance()->computeStat(CStat::E_AUTO_ANSWERED);
-    return true;
-
     default:
-    ERROR_P1("Internal error for automaticResponseMode - mode %d is not implemented!", P_case);
+    ERROR("Internal error for automaticResponseMode - mode %d is not implemented!", P_case);
     break ;
   }
 
   return false;
-  
 }
 
 #ifdef PCAPPLAY

Modified: sip-tester/branches/upstream/current/sipp.hpp
URL: http://svn.debian.org/wsvn/pkg-voip/sip-tester/branches/upstream/current/sipp.hpp?rev=6622&op=diff
==============================================================================
--- sip-tester/branches/upstream/current/sipp.hpp (original)
+++ sip-tester/branches/upstream/current/sipp.hpp Sun Jan  4 02:47:02 2009
@@ -45,7 +45,13 @@
 #include <vector>
 #include <string>
 #include <map>
+#include <set>
 #include <math.h>
+#ifndef __SUNOS
+#include <curses.h>
+#else
+#include <stdarg.h>
+#endif
 
 #if defined(__HPUX) || defined(__SUNOS)
 #include <alloca.h>
@@ -56,6 +62,9 @@
 #include "xp_parser.h"
 #include "scenario.hpp"
 #include "screen.hpp"
+#include "task.hpp"
+#include "listener.hpp"
+#include "socketowner.hpp"
 #include "call.hpp"
 #include "comp.h"
 #include "stat.hpp"
@@ -93,7 +102,16 @@
 
 /************************** Constants **************************/
 
-#define SIPP_VERSION               20071128
+#ifdef SVN_VERSION
+# ifdef LOCAL_VERSION_EXTRA
+#  define SIPP_VERSION               SVN_VERSION LOCAL_VERSION_EXTRA
+# else
+#  define SIPP_VERSION               SVN_VERSION
+# endif
+#else
+# define SIPP_VERSION               "unknown"
+#endif
+
 #define T_UDP                      0
 #define T_TCP                      1
 #define T_TLS                      2
@@ -133,13 +151,12 @@
 /******************** Default parameters ***********************/
 
 #define DEFAULT_RATE                 10.0
+#define DEFAULT_RATE_SCALE           1.0
 #define DEFAULT_RATE_PERIOD_MS       1000
 #define DEFAULT_TRANSPORT            T_UDP
 #define DEFAULT_PORT                 5060  
 #define DEFAULT_MEDIA_PORT           6000
-#ifdef __3PCC__
 #define DEFAULT_3PCC_PORT            6060
-#endif
 #define DEFAULT_SERVICE              ((char *)"service")
 #define DEFAULT_AUTH_PASSWORD        ((char *)"password")
 #define DEFAULT_REPORT_FREQ          1000
@@ -148,14 +165,24 @@
 #define DEFAULT_FREQ_DUMP_RTT        200
 #define DEFAULT_MAX_MULTI_SOCKET     50000
 #define DEFAULT_CTRL_SOCKET_PORT     8888
+#define DEFAULT_DEADCALL_WAIT	     33000
+
+#define DEFAULT_BEHAVIOR_NONE	     0
+#define DEFAULT_BEHAVIOR_BYE	     1
+#define DEFAULT_BEHAVIOR_ABORTUNEXP  2
+#define DEFAULT_BEHAVIOR_PINGREPLY   4
+
+#define DEFAULT_BEHAVIOR_ALL	     (DEFAULT_BEHAVIOR_BYE | DEFAULT_BEHAVIOR_ABORTUNEXP | DEFAULT_BEHAVIOR_PINGREPLY)
 
 /************ User controls and command line options ***********/
 
 extern int                duration                _DEFVAL(0);
 extern double             rate                    _DEFVAL(DEFAULT_RATE);
+extern double             rate_scale              _DEFVAL(DEFAULT_RATE_SCALE);
 extern int	          rate_increase           _DEFVAL(0);
 extern int	          rate_max	          _DEFVAL(0);
-extern int                users                   _DEFVAL(0);
+extern bool	          rate_quit		  _DEFVAL(true);
+extern int                users                   _DEFVAL(-1);
 extern int               rate_period_ms           _DEFVAL(DEFAULT_RATE_PERIOD_MS);
 extern unsigned long      defl_recv_timeout       _DEFVAL(0);
 extern unsigned long      defl_send_timeout       _DEFVAL(0);
@@ -165,7 +192,8 @@
 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 bool               default_behavior        _DEFVAL(1);
+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 int                multisocket             _DEFVAL(0);
@@ -179,6 +207,7 @@
 extern unsigned long      report_freq             _DEFVAL(DEFAULT_REPORT_FREQ);
 extern unsigned long      report_freq_dumpLog     _DEFVAL
                                                 (DEFAULT_REPORT_FREQ_DUMP_LOG);
+extern bool		  periodic_rtd		  _DEFVAL(false);
 extern char		* stat_delimiter          _DEFVAL(";");
 
 extern bool               timeout_exit            _DEFVAL(false);
@@ -188,6 +217,7 @@
 
 extern unsigned int       max_multi_socket        _DEFVAL
                                                 (DEFAULT_MAX_MULTI_SOCKET);
+extern bool		  skip_rlimit		  _DEFVAL(false);
 
 extern unsigned int       timer_resolution        _DEFVAL(DEFAULT_TIMER_RESOLUTION);
 extern int                max_recv_loops          _DEFVAL(MAX_RECV_LOOPS_PER_CYCLE);
@@ -197,6 +227,7 @@
 extern char               local_ip_escaped[42];
 extern bool               local_ip_is_ipv6;    
 extern int                local_port              _DEFVAL(0);
+extern char               control_ip[40];
 extern int                control_port            _DEFVAL(0);
 extern int                buff_size               _DEFVAL(65535);
 extern int                tcp_readsize            _DEFVAL(65535);
@@ -222,7 +253,6 @@
 extern int                lose_packets            _DEFVAL(0);
 extern double             global_lost             _DEFVAL(0.0);
 extern char               remote_host[255]; 
-#ifdef __3PCC__
 extern char               twinSippHost[255];
 extern char               twinSippIp[40];
 extern char             * master_name;
@@ -230,8 +260,8 @@
 extern int                twinSippPort            _DEFVAL(DEFAULT_3PCC_PORT);
 extern bool               twinSippMode            _DEFVAL(false);
 extern bool               extendedTwinSippMode    _DEFVAL(false);
-#endif
-
+
+extern bool               nostdin                 _DEFVAL(false);        
 extern bool               backgroundMode          _DEFVAL(false);        
 extern bool               signalDump              _DEFVAL(false);        
 
@@ -266,11 +296,18 @@
 // extern field file management
 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);
 
 // free user id list
 extern list<int> freeUsers;
+extern list<int> retiredUsers;
+extern AllocVariableTable *globalVariables       _DEFVAL(NULL);
+extern AllocVariableTable *userVariables         _DEFVAL(NULL);
+typedef std::map<int, VariableTable *> int_vt_map;
+extern int_vt_map          userVarMap;
 
 //extern int      new_socket(bool P_use_ipv6, int P_type_socket, int * P_status);
 extern struct   sipp_socket *new_sipp_socket(bool use_ipv6, int transport);
@@ -278,6 +315,7 @@
 struct sipp_socket *sipp_accept_socket(struct sipp_socket *accept_socket);
 extern int	sipp_bind_socket(struct sipp_socket *socket, struct sockaddr_storage *saddr, int *port);
 extern int	sipp_connect_socket(struct sipp_socket *socket, struct sockaddr_storage *dest);
+extern int      sipp_reconnect_socket(struct sipp_socket *socket);
 extern void	sipp_customize_socket(struct sipp_socket *socket);
 extern int      delete_socket(int P_socket);
 extern int      min_socket          _DEFVAL(65535);
@@ -291,17 +329,13 @@
 
 /************************ Statistics **************************/
 
-extern unsigned long total_calls                  _DEFVAL(0);
 extern unsigned long last_report_calls            _DEFVAL(0);
 extern unsigned long nb_net_send_errors           _DEFVAL(0);
 extern unsigned long nb_net_cong                  _DEFVAL(0);
 extern unsigned long nb_net_recv_errors           _DEFVAL(0);
 extern bool          cpu_max                      _DEFVAL(false);
 extern bool          outbound_congestion          _DEFVAL(false);
-extern unsigned int  open_calls_peak              _DEFVAL(0);
-extern unsigned long open_calls_peak_time         _DEFVAL(0);
 extern int           open_calls_user_setting      _DEFVAL(0);
-extern int           nb_out_of_the_blue           _DEFVAL(0);
 extern int           resynch_send                 _DEFVAL(0);
 extern int           resynch_recv                 _DEFVAL(0);
 extern unsigned long rtp_pckts                    _DEFVAL(0);
@@ -315,7 +349,6 @@
 
 /************* Rate Control & Contexts variables **************/
 
-extern unsigned int  open_calls                   _DEFVAL(0);
 extern int           last_running_calls           _DEFVAL(0);
 extern int           last_woken_calls             _DEFVAL(0);
 extern int           last_paused_calls            _DEFVAL(0);
@@ -323,7 +356,6 @@
 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);
-extern unsigned long calls_since_last_rate_change _DEFVAL(0);
 
 /********************** Clock variables ***********************/
 
@@ -349,10 +381,13 @@
 extern int           user_port                    _DEFVAL(0);
 extern char          hostname[80];
 extern bool          is_ipv6                      _DEFVAL(false);
-extern int           start_calls                  _DEFVAL(0);
+
 extern int           reset_number                 _DEFVAL(0);
 extern int	     reset_close                  _DEFVAL(1);
 extern int	     reset_sleep                  _DEFVAL(1000);
+extern bool	     sendbuffer_warn              _DEFVAL(false);
+/* A list of sockets pending reset. */
+extern set<struct sipp_socket *> sockets_pending_reset;
 
 extern struct        addrinfo * local_addr_storage;
 
@@ -395,54 +430,36 @@
 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   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);
+extern bool   useCountf                           _DEFVAL(0);
 extern char * scenario_file;
 extern char * slave_cfg_file;
 
-#define TRACE_MSG(arg)      \
-{                           \
-  if(messagef) {            \
-    FILE * s = messagef;    \
-    fprintf arg;            \
-    fflush(messagef);       \
-  }                         \
-}
-
-#define TRACE_SHORTMSG(arg)  \
-{                            \
-  if(shortmessagef) {        \
-    FILE * s = shortmessagef;\
-    fprintf arg;             \
-    fflush(shortmessagef);   \
-  }                          \
-}
-
-#define LOG_MSG(arg)        \
-{                           \
-  if(logfile) {             \
-    FILE * s = logfile;     \
-    fprintf arg;            \
-    fflush(logfile);        \
-  }                         \
-}
-
-// TODO: finish the -trace_timeout option implementation
-
-/* #define TRACE_TIMEOUT(arg)  \
-{                           \
-  if(timeoutf) {            \
-    FILE * s = timeoutf;    \
-    fprintf arg;            \
-    fflush(timeoutf);       \
-  }                         \
-} */
+extern unsigned long long max_log_size		  _DEFVAL(0);
+extern unsigned long long ringbuffer_size	  _DEFVAL(0);
+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();
+void rotate_errorf();
 
 /********************* Mini-Parser Routines *******************/
 
@@ -471,6 +488,7 @@
 	char *buf;
 	size_t len;
 	size_t offset;
+	struct sockaddr_storage addr;
 	struct socketbuf *next;
 };
 
@@ -491,6 +509,7 @@
 	BIO *ss_bio;	/* The underlying BIO descriptor for this socket. */
 #endif
 	struct sockaddr_storage ss_remote_sockaddr; /* Who we are talking to. */
+	struct sockaddr_storage ss_dest; /* Who we are talking to. */
 
 
 	int ss_pollidx; /* The index of this socket in our poll structures. */
@@ -503,7 +522,7 @@
 };
 
 /* Write data to a socket. */
-int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flags);
+int write_socket(struct sipp_socket *socket, char *buffer, ssize_t len, int flags, struct sockaddr_storage *dest);
 /* Mark a socket as "bad". */
 void sipp_socket_invalidate(struct sipp_socket *socket);
 /* Actually free the socket. */
@@ -535,9 +554,9 @@
 char *get_peer_addr(char *);
 int get_decimal_from_hex(char hex);
 
-int reset_connections() ;
-int close_calls();
-int close_calls(struct sipp_socket *);
+bool reconnect_allowed();
+void reset_connection(struct sipp_socket *);
+void close_calls(struct sipp_socket *);
 int close_connections();
 int open_connections();
 void timeout_alarm(int);
@@ -559,4 +578,15 @@
 #undef extern
 #endif
 
+/* THis must go after the GLOBALS_FULL_DEFINITION, because we need the extern keyword. */
+#ifdef __cplusplus
+extern "C" {
+#endif
+int TRACE_MSG(char *fmt, ...);
+int TRACE_SHORTMSG(char *fmt, ...);
+int LOG_MSG(char *fmt, ...);
+#ifdef __cplusplus
+}
+#endif
+
 #endif // __SIPP__




More information about the Pkg-voip-commits mailing list