/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Author : Richard GAYRAUD - 04 Nov 2003
 *           Olivier Jacques
 *           From Hewlett Packard Company.
 *           Shriram Natarajan
 *           Peter Higginson
 *           Eric Miller
 *           Venkatesh
 *           Enrico Hartung
 *           Nasir Khan
 *           Lee Ballard
 *           Guillaume Teissier from FTR&D
 *           Wolfgang Beck
 *           Venkatesh
 *           Vlad Troyanker
 *           Charles P Wright from IBM Research
 *           Amit On from Followap
 *           Jan Andres from Freenet
 *           Ben Evans from Open Cloud
 *           Marc Van Diest from Belgacom
 *           Michael Dwyer from Cibation
 *           Roland Meub
 *           Andy Aicken
 *	     Martin H. VanLeeuwen
 */

#include <iterator>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>

#ifdef PCAPPLAY
#include "send_packets.h"
#endif
#include "sipp.hpp"
#include "deadcall.hpp"
#include "assert.h"

#define callDebug(args...) do { if (useCallDebugf) { _callDebug( args ); } } while (0)

#ifdef _USE_OPENSSL
extern  SSL                 *ssl_list[];
extern  struct pollfd        pollfiles[];
extern  SSL_CTX             *sip_trp_ssl_ctx;
#endif

extern  map<string, struct sipp_socket *>     map_perip_fd;

#ifdef PCAPPLAY
/* send_packets pthread wrapper */
void *send_wrapper(void *);
#endif
int call::dynamicId       = 0;
int call::maxDynamicId    = 10000+2000*4;      // FIXME both param to be in command line !!!!
int call::startDynamicId  = 10000;             // FIXME both param to be in command line !!!!
int call::stepDynamicId   = 4;                // FIXME both param to be in command line !!!!

/************** Call map and management routines **************/
static unsigned int next_number = 1;

unsigned int get_tdm_map_number() {
  unsigned int nb = 0;
  unsigned int i=0;
  unsigned int interval=0;
  unsigned int random=0;
  bool found = false;

  /* Find a number in the tdm_map which is not in use */
  interval = (tdm_map_a+1) * (tdm_map_b+1) * (tdm_map_c+1);
  random = rand() % interval;
  while ((i<interval) && (!found)) {
    if (tdm_map[(random + i - 1) % interval] == false) {
      nb = (random + i - 1) % interval;
      found = true;
    } 
    i++;
  } 

  if (!found) {
    return 0;
  } else {
    return nb+1;
  } 
}

/* 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];
    char *my_msg = strdup(msg);

    if (!my_msg) {
        return INADDR_NONE;
    }
    begin = strstr(my_msg, pattern);
    if (!begin) {
      free(my_msg);
      /* 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) {
      free(my_msg);
      return INADDR_NONE;
    }
    *end = '\0';
    memset(ip, 0, 32);
    strncpy(ip, begin, sizeof(ip) - 1);
    ip[sizeof(ip) - 1] = '\0';
    free(my_msg);
    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];
    char *my_msg = strdup(msg);

    memset(addr, 0, sizeof(*addr));
    memset(ip, 0, 128);

    if (!my_msg) {
        return 0;
    }
    begin = strstr(my_msg,pattern);
    if (!begin) {
      free(my_msg);
      /* 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) {
      free(my_msg);
      return 0;
    }
    *end = '\0';
    strncpy(ip, begin, sizeof(ip) -1);
    ip[sizeof(ip) - 1] = '\0';
    free(my_msg);
    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[7];

    if (pattype == PAT_AUDIO) {
      pattern = "m=audio ";
    } else if (pattype == PAT_VIDEO) {
      pattern = "m=video ";
    } else {
	ERROR("Internal error: Undefined media pattern %d\n", 3);
    }

    char *my_msg = strdup(msg);
    if (!my_msg) {
        return 0;
    }
    begin = strstr(my_msg, pattern);
    if (!begin) {
      free(my_msg);
      /* m=audio not found */
      return 0;
    }
    begin += strlen(pattern) - 1;
    end = strstr(begin, "\r\n");
    if (!end) {
      free(my_msg);
      ERROR("get_remote_port_media: no CRLF found");
    }
    *end = '\0';
    memset(number, 0, sizeof(number));
    strncpy(number, begin, sizeof(number) - 1);
    number[sizeof(number) - 1] = '\0';
    free(my_msg);
    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 call::hash(char * msg) {
  unsigned long hash = 0;
  int c;

  if (rtcheck == RTCHECK_FULL) {
    while ((c = *msg++))
      hash = c + (hash << 6) + (hash << 16) - hash;
  } else if (rtcheck == RTCHECK_LOOSE) {
    /* Based on section 11.5 (bullet 2) of RFC2543 we only take into account
     * the To, From, Call-ID, and CSeq values. */
      char *hdr = get_header_content(msg,"To:");
      while ((c = *hdr++))
	hash = c + (hash << 6) + (hash << 16) - hash;
      hdr = get_header_content(msg,"From:");
      while ((c = *hdr++))
	hash = c + (hash << 6) + (hash << 16) - hash;
      hdr = get_header_content(msg,"Call-ID:");
      while ((c = *hdr++))
	hash = c + (hash << 6) + (hash << 16) - hash;
      hdr = get_header_content(msg,"CSeq:");
      while ((c = *hdr++))
	hash = c + (hash << 6) + (hash << 16) - hash;
      /* For responses, we should also consider the code and body (if any),
       * because they are not nearly as well defined as the request retransmission. */
      if (!strncmp(msg, "SIP/2.0", strlen("SIP/2.0"))) {
	/* Add the first line into the hash. */
	hdr = msg + strlen("SIP/2.0");
	while ((c = *hdr++) && (c != '\r'))
	  hash = c + (hash << 6) + (hash << 16) - hash;
	/* Add the body (if any) into the hash. */
	hdr = strstr(msg, "\r\n\r\n");
	if (hdr) {
	  hdr += strlen("\r\n\r\n");
	  while ((c = *hdr++))
	    hash = c + (hash << 6) + (hash << 16) - hash;
	}
      }
  } else {
    ERROR("Internal error: Invalid rtcheck %d\n", rtcheck);
  }

  return hash;
}

/******************* Call class implementation ****************/
call::call(char *p_id, bool use_ipv6, int userId, struct sockaddr_storage *dest) : listener(p_id, true) {
  init(main_scenario, NULL, dest, p_id, userId, use_ipv6, false, 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. */, false);
}

call::call(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitialization) : listener(p_id, true) {
  init(call_scenario, socket, dest, p_id, userId, ipv6, isAutomatic, isInitialization);
}

call *call::add_call(int userId, bool ipv6, struct sockaddr_storage *dest)
{
  static char call_id[MAX_HEADER_LEN];

  char * src = call_id_string;
  int count = 0;

  if(!next_number) { next_number ++; }

  while (*src && count < MAX_HEADER_LEN-1) {
      if (*src == '%') {
          ++src;
          switch(*src++) {
          case 'u':
              count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%u", next_number);
              break;
          case 'p':
              count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%u", pid);
              break;
          case 's':
              count += snprintf(&call_id[count], MAX_HEADER_LEN-count-1,"%s", local_ip);
              break;
          default:      // treat all unknown sequences as %%
              call_id[count++] = '%';
              break;
          }
      } else {
        call_id[count++] = *src++;
      }
  }
  call_id[count] = 0;

  return new call(main_scenario, NULL, dest, call_id, userId, ipv6, false /* Not Auto. */, false);
}


void call::init(scenario * call_scenario, struct sipp_socket *socket, struct sockaddr_storage *dest, char * p_id, int userId, bool ipv6, bool isAutomatic, bool isInitCall)
{
  this->call_scenario = call_scenario;
  zombie = false;

  debugBuffer = NULL;
  debugLength = 0;

  msg_index = 0;
  last_send_index = 0;
  last_send_msg = NULL;
  last_send_len = 0;

  last_recv_hash = 0;
  last_recv_index = -1;
  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 ;
  ack_is_pending=false ;
  last_recv_msg = NULL;
  cseq = base_cseq;
  nb_last_delay = 0;
  use_ipv6 = ipv6;
  queued_msg = NULL;
  
  dialog_authentication = NULL;
  dialog_challenge_type = 0;

#ifdef _USE_OPENSSL
  m_ctx_ssl = NULL ;
  m_bio = NULL ;
#endif

#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;
  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->transactions.size() > 0) {
    transactions = (struct txnInstanceInfo *)malloc(sizeof(txnInstanceInfo) * call_scenario->transactions.size());
    memset(transactions, 0, sizeof(struct txnInstanceInfo) * call_scenario->transactions.size());
  } else {
    transactions = NULL;
  }

  // If not updated by a message we use the start time 
  // information to compute rtd information
  start_time_rtd = (unsigned long long *)malloc(sizeof(unsigned long long) * call_scenario->stats->nRtds());
  if (!start_time_rtd) {
    ERROR("Could not allocate RTD times!");
  }
  rtd_done = (bool *)malloc(sizeof(bool) * call_scenario->stats->nRtds());
  if (!start_time_rtd) {
    ERROR("Could not allocate RTD done!");
  }
  for (i = 0; i < call_scenario->stats->nRtds(); i++) {
    start_time_rtd[i] = getmicroseconds();
    rtd_done[i] = false;
  }

  // by default, last action result is NO_ERROR
  last_action_result = call::E_AR_NO_ERROR;

  this->userId = userId;

  /* For automatic answer calls to an out of call request, we must not */
  /* increment the input files line numbers to not disturb */
  /* the input files read mechanism (otherwise some lines risk */
  /* to be systematically skipped */
  if (!isAutomatic) {
    m_lineNumber = new file_line_map();
    for (file_map::iterator file_it = inFiles.begin();
	file_it != inFiles.end();
	file_it++) {
      (*m_lineNumber)[file_it->first] = file_it->second->nextLine(userId);
    }
  } else {
    m_lineNumber = NULL;
  }
  this->initCall = isInitCall;

#ifdef PCAPPLAY
  memset(&(play_args_a.to), 0, sizeof(struct sockaddr_storage));
  memset(&(play_args_v.to), 0, sizeof(struct sockaddr_storage));
  memset(&(play_args_a.from), 0, sizeof(struct sockaddr_storage));
  memset(&(play_args_v.from), 0, sizeof(struct sockaddr_storage));
  hasMediaInformation = 0;
  media_thread = 0;
#endif

  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;
    }
  }

  callDebug("Starting call %s\n", id);

  setRunning();
}

int call::_callDebug(char *fmt, ...) {
    va_list ap;

    if (!useCallDebugf) {
      return 0;
    }

    /* First we figure out how much to allocate. */
    va_start(ap, fmt);
    int ret = vsnprintf(NULL, 0, fmt, ap);
    va_end(ap);

    debugBuffer = (char *)realloc(debugBuffer, debugLength + ret + TIME_LENGTH + 2);
    if (!debugBuffer) {
      ERROR("Could not allocate buffer (%d bytes) for callDebug file!", debugLength + ret + TIME_LENGTH + 2);
    }

    struct timeval now;
    gettimeofday(&now, NULL);
    debugLength += snprintf(debugBuffer + debugLength, TIME_LENGTH + 2, "%s ", CStat::formatTime(&now));

    va_start(ap, fmt);
    debugLength += vsnprintf(debugBuffer + debugLength, ret + 1, fmt, ap);
    va_end(ap);

    return ret;
}

call::~call()
{
  computeStat(CStat::E_ADD_CALL_DURATION, clock_tick - start_time);

  if(comp_state) { comp_free(&comp_state); }

  if (call_remote_socket && (call_remote_socket != main_remote_socket)) {
    sipp_close_socket(call_remote_socket);
  }

  /* Deletion of the call variable */
  if(M_callVariableTable) {
    M_callVariableTable->putTable();
  }
  if (m_lineNumber) {
    delete m_lineNumber;
  }
  if (userId) {
    opentask::freeUser(userId);
  }

  if (transactions) {
    for (unsigned int i = 0; i < call_scenario->transactions.size(); i++) {
      free(transactions[i].txnID);
    }
    free(transactions);
  }

  if(last_recv_msg) { free(last_recv_msg); }
  if(last_send_msg) { free(last_send_msg); }
  if(peer_tag) { free(peer_tag); }

  if(dialog_route_set) {
       free(dialog_route_set);
  }

  if(next_req_url) {
       free(next_req_url);
  }


  if(dialog_authentication) {
       free(dialog_authentication);
  }

  if (use_tdmmap) {
    tdm_map[tdm_map_number] = false;
  }
  
# ifdef PCAPPLAY
  if (media_thread != 0) {
    pthread_cancel(media_thread);
    pthread_join(media_thread, NULL);
  }
#endif


  free(start_time_rtd);
  free(rtd_done);
  free(debugBuffer);
}

void call::computeStat (CStat::E_Action P_action) {
  if (initCall) {
    return;
  }
  call_scenario->stats->computeStat(P_action);
}

void call::computeStat (CStat::E_Action P_action, unsigned long P_value) {
  if (initCall) {
    return;
  }
  call_scenario->stats->computeStat(P_action, P_value);
}

void call::computeStat (CStat::E_Action P_action, unsigned long P_value, int which) {
  if (initCall) {
    return;
  }
  call_scenario->stats->computeStat(P_action, P_value, which);
}

/* 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 %u)", s, next_retrans);
  }
  if (paused_until) {
    sprintf(s, "%s (paused until %u)", s, paused_until);
  }
  if (recv_timeout) {
    sprintf(s, "%s (recv timeout %u)", s, recv_timeout);
  }
  if (send_timeout) {
    sprintf(s, "%s (send timeout %u)", s, send_timeout);
  }
  WARNING("%s", s);
}

bool call::connect_socket_if_needed()
{
  bool existing;

  if(call_socket) return true;
  if(!multisocket) return true;

  if(transport == T_UDP) {
    struct sockaddr_storage saddr;

    if(sendMode != MODE_CLIENT)
      return true;

    char peripaddr[256];
    if (!peripsocket) {
      if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) {
	 ERROR_NO("Unable to get a UDP socket (1)");
      }
    } else {
      char *tmp = peripaddr;
      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()) {
	// Socket does not exist
	if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) {
	  ERROR_NO("Unable to get a UDP socket (2)");
	} else {
	  /* Ensure that it stays persistent, because it is recorded in the map. */
	  call_socket->ss_count++;
	  map_perip_fd[peripaddr] = call_socket;
	}
      } else {
	// Socket exists already
	associate_socket(i->second);
	existing = true;
	i->second->ss_count++;
      }
    }
    if (existing) {
	return true;
    }

    memset(&saddr, 0, sizeof(struct sockaddr_storage));

    memcpy(&saddr,
	   local_addr_storage->ai_addr,
           SOCK_ADDR_SIZE(
             _RCAST(struct sockaddr_storage *,local_addr_storage->ai_addr)));

    if (use_ipv6) {
      saddr.ss_family       = AF_INET6;
    } else {
      saddr.ss_family       = AF_INET;
    }
    
    if (peripsocket) {
      struct addrinfo * h ;
      struct addrinfo   hints;
      memset((char*)&hints, 0, sizeof(hints));
      hints.ai_flags  = AI_PASSIVE;
      hints.ai_family = PF_UNSPEC;
      getaddrinfo(peripaddr,
                  NULL,
                  &hints,
                  &h); 
      memcpy(&saddr,
             h->ai_addr,
             SOCK_ADDR_SIZE(
                _RCAST(struct sockaddr_storage *,h->ai_addr)));

      if (use_ipv6) {
       (_RCAST(struct sockaddr_in6 *, &saddr))->sin6_port = htons(local_port);
      } else {
       (_RCAST(struct sockaddr_in *, &saddr))->sin_port = htons(local_port);
      }
    }

    if (sipp_bind_socket(call_socket, &saddr, &call_port)) {
      ERROR_NO("Unable to bind UDP socket");
    }
  } else { /* TCP, SCTP or TLS. */
    struct sockaddr_storage *L_dest = &remote_sockaddr;

    if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) {
      ERROR_NO("Unable to get a TCP/SCTP/TLS socket");
    }

    if (existing) {
      return true;
    }
    
    sipp_customize_socket(call_socket);

    if (use_remote_sending_addr) {
      L_dest = &remote_sending_sockaddr;
    }

    if (sipp_connect_socket(call_socket, L_dest)) {
      if (reconnect_allowed()) {
        if(errno == EINVAL){
          /* This occurs sometime on HPUX but is not a true INVAL */
          WARNING("Unable to connect a TCP/SCTP/TLS socket, remote peer error");
        } else {
          WARNING("Unable to connect a TCP/SCTP/TLS socket");
        }
	/* This connection failed.  We must be in multisocket mode, because
         * otherwise we would already have a call_socket.  This call can not
         * succeed, but does not affect any of our other calls. We do decrement
	 * the reconnection counter however. */
	if (reset_number != -1) {
	  reset_number--;
	}

	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 */
	  ERROR("Unable to connect a TCP/SCTP/TLS socket, remote peer error");
	} else {
	  ERROR_NO("Unable to connect a TCP/SCTP/TLS socket");
	}
      }
    }
  }
  return true;
}

bool call::lost(int index)
{
  static int inited = 0;
  double percent = global_lost;

  if(!lose_packets) return false;

  if (call_scenario->messages[index]->lost >= 0) {
    percent = call_scenario->messages[index]->lost;
  }

  if (percent == 0) {
    return false;
  }

  if(!inited) {
    srand((unsigned int) time(NULL));
    inited = 1;
  }

  return (((double)rand() / (double)RAND_MAX) < (percent / 100.0));
}

int call::send_raw(char * msg, int index, int len) 
{
  struct sipp_socket *sock;
  int rc;

  callDebug("Sending %s message for call %s (index %d, hash %u):\n%s\n\n", TRANSPORT_TO_STRING(transport), id, index, hash(msg), msg);
 
  if (useShortMessagef == 1) {
      struct timeval currentTime;
      GET_TIME (&currentTime);
      char* cs=get_header_content(msg,"CSeq:");
      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 message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport));
    callDebug("%s message voluntary lost (while sending) (index %d, hash %u).\n", TRANSPORT_TO_STRING(transport), index, hash(msg));
    
    if(comp_state) { comp_free(&comp_state); }
    call_scenario->messages[index] -> nb_lost++;
    return 0;
  }
  
  sock = call_socket;

  if ((use_remote_sending_addr) && (sendMode == MODE_SERVER)) {
    if (!call_remote_socket) {
      if (multisocket || !main_remote_socket) {
	struct sockaddr_storage *L_dest = &remote_sending_sockaddr;

	if((call_remote_socket= new_sipp_socket(use_ipv6, transport)) == NULL) {
	  ERROR_NO("Unable to get a socket for rsa option");
	}

	sipp_customize_socket(call_remote_socket);

	if(transport != T_UDP) {
	  if (sipp_connect_socket(call_remote_socket, L_dest)) {
	    if(errno == EINVAL){
	      /* This occurs sometime on HPUX but is not a true INVAL */
	      ERROR("Unable to connect a %s socket for rsa option, remote peer error", TRANSPORT_TO_STRING(transport));
	    } else {
	      ERROR_NO("Unable to connect a socket for rsa option");
	    }
	  }
	}
	if (!multisocket) {
	  main_remote_socket = call_remote_socket;
	}
      }

      if (!multisocket) {
	call_remote_socket = main_remote_socket;
	main_remote_socket->ss_count++;
      }
    }
    sock=call_remote_socket ;
  }

  // If the length hasn't been explicitly specified, treat the message as a string
  if (len==0) {
    len = strlen(msg);
  }
  
  assert(sock);

  rc = write_socket(sock, msg, len, WS_BUFFER, &call_peer);
  if(rc == -1 && errno == EWOULDBLOCK) {
    return -1;
  }

  if(rc < 0) {
    computeStat(CStat::E_CALL_FAILED);
    computeStat(CStat::E_FAILED_CANNOT_SEND_MSG);
    delete this;
  }

  return rc; /* OK */
}

/* This method is used to send messages that are not */
/* part of the XML scenario                          */
void call::sendBuffer(char * msg, int len)
{
  /* call send_raw but with a special scenario index */
  if (send_raw(msg, -1, len) < 0) {
    if (sendbuffer_warn) {
      ERROR_NO("Error sending raw message");
    } else {
      WARNING_NO("Error sending raw message");
    }
  }
}


char * call::compute_cseq(char * src)
{
  static char cseq[MAX_HEADER_LEN];

    /* If we find a CSeq in incoming msg */
  char * last_header = get_last_header("CSeq:");
    if(last_header) {
      int i;
      /* Extract the integer value of the last CSeq */
      last_header = strstr(last_header, ":");
      last_header++;
      while(isspace(*last_header)) last_header++;
      sscanf(last_header,"%d", &i);
      /* Add 1 to the last CSeq value */
      sprintf(cseq, "%s%d",  "CSeq: ", (i+1));
    } else {
      sprintf(cseq, "%s",  "CSeq: 2");
    }
    return cseq;
}

char * call::get_header_field_code(char *msg, char * name)
{
  static char code[MAX_HEADER_LEN];
  char * last_header;
  int i;

    last_header = NULL;
    i = 0;
    /* If we find the field in msg */
    last_header = get_header_content(msg, name);
    if(last_header) {
      /* Extract the integer value of the field */
      while(isspace(*last_header)) last_header++;
      sscanf(last_header,"%d", &i);
      sprintf(code, "%s %d", name, i);
    }
    return code;
}

char * call::get_last_header(char * name)
{
  int len;

  if((!last_recv_msg) || (!strlen(last_recv_msg))) {
    return NULL;
  }

  len = strlen(name);

  /* 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("call::get_last_header: Header to parse bigger than %d (%zu)", MAX_HEADER_LEN, strlen(name));
  }

  if (name[len - 1] == ':') {
    return get_header(last_recv_msg, name, false);
  } else {
    char with_colon[MAX_HEADER_LEN];
    sprintf(with_colon, "%s:", name);
    return get_header(last_recv_msg, with_colon, false);
  }
}

char * call::get_header_content(char* message, char * name)
{
  return get_header(message, name, true);
}

/* If content is true, we only return the header's contents. */
char * call::get_header(char* message, char * name, bool content)
{
  /* non reentrant. consider accepting char buffer as param */
  static char last_header[MAX_HEADER_LEN * 10];
  char * src, *dest, *start, *ptr;
  /* Are we searching for a short form header? */
  bool short_form = false;
  bool first_time = true;
  char src_tmp[MAX_HEADER_LEN + 1];

  /* returns empty string in case of error */
  last_header[0] = '\0';

  if((!message) || (!strlen(message))) {
    return last_header;
  }

  /* for safety's sake */
  if (NULL == name || NULL == strrchr(name, ':')) {
    WARNING("Can not searching for header (no colon): %s", name ? name : "(null)");
    return last_header;
  }

  do
  {
    snprintf(src_tmp, MAX_HEADER_LEN, "\n%s", name);
    src = message;
    dest = last_header;

    while((src = strcasestr2(src, src_tmp))) {
      if (content || !first_time) {
        /* just want the header's content */
        src += strlen(name) + 1;
      } else {
	     src++;
      }
      first_time = false;
      ptr = strchr(src, '\n');

      /* Multiline headers always begin with a tab or a space
       * on the subsequent lines */
      while((ptr) &&
	  ((*(ptr+1) == ' ' ) ||
	   (*(ptr+1) == '\t')    )) {
	ptr = strchr(ptr + 1, '\n'); 
      }

      if(ptr) { *ptr = 0; }
      // Add "," when several headers are present
      if (dest != last_header) {
	/* Remove trailing whitespaces, tabs, and CRs */
	while ((dest > last_header) &&
	    ((*(dest-1) == ' ') || (*(dest-1) == '\r') || (*(dest-1) == '\n') || (*(dest-1) == '\t'))) {
	  *(--dest) = 0;
	}

	dest += sprintf(dest, ",");
      }
      dest += sprintf(dest, "%s", src);
      if(ptr) { *ptr = '\n'; }

      src++;
    }
    /* We found the header. */
    if(dest != last_header) {
	break;
    }
    /* We didn't find the header, even in its short form. */
    if (short_form) {
      return last_header;
    }

    /* We should retry with the short form. */
    short_form = true;
    if (!strcasecmp(name, "call-id:")) {
      name = "i:";
    } else if (!strcasecmp(name, "contact:")) {
      name = "m:";
    } else if (!strcasecmp(name, "content-encoding:")) {
      name = "e:";
    } else if (!strcasecmp(name, "content-length:")) {
      name = "l:";
    } else if (!strcasecmp(name, "content-type:")) {
      name = "c:";
    } else if (!strcasecmp(name, "from:")) {
      name = "f:";
    } else if (!strcasecmp(name, "to:")) {
      name = "t:";
    } else if (!strcasecmp(name, "via:")) {
      name = "v:";
    } else {
      /* There is no short form to try. */
      return last_header;
    }
  }
  while (1);

  *(dest--) = 0;

  /* Remove trailing whitespaces, tabs, and CRs */
  while ((dest > last_header) && 
         ((*dest == ' ') || (*dest == '\r')|| (*dest == '\t'))) {
    *(dest--) = 0;
  }
 
  /* Remove leading whitespaces */
  for (start = last_header; *start == ' '; start++);

  /* remove enclosed CRs in multilines */
  /* don't remove enclosed CRs for multiple headers (e.g. Via) (Rhys) */
  while((ptr = strstr(last_header, "\r\n")) != NULL
        && (   *(ptr + 2) == ' ' 
            || *(ptr + 2) == '\r' 
            || *(ptr + 2) == '\t') ) {
    /* Use strlen(ptr) to include trailing zero */
    memmove(ptr, ptr+1, strlen(ptr));
  }

  /* Remove illegal double CR characters */
  while((ptr = strstr(last_header, "\r\r")) != NULL) {
    memmove(ptr, ptr+1, strlen(ptr));
  }
  /* Remove illegal double Newline characters */  
  while((ptr = strstr(last_header, "\n\n")) != NULL) {
    memmove(ptr, ptr+1, strlen(ptr));
  }

  return start;
}

char * call::get_first_line(char * message)
{
  /* non reentrant. consider accepting char buffer as param */
  static char last_header[MAX_HEADER_LEN * 10];
  char * src, *dest;

  /* returns empty string in case of error */
  memset(last_header, 0, sizeof(last_header));

  if((!message) || (!strlen(message))) {
    return last_header;
  }

  src = message;
  dest = last_header;
  
  int i=0;
  while (*src){
    if((*src=='\n')||(*src=='\r')){
      break;
    }
    else
    {
      last_header[i]=*src;
    }
    i++;
    src++;
  }
  
  return last_header;
}

/* Return the last request URI from the To header. On any error returns the
 * empty string.  The caller must free the result. */
char * call::get_last_request_uri ()
{
     char * tmp;
     char * tmp2;
     char * last_request_uri;
     int tmp_len;

     char * last_To = get_last_header("To:");
     if (!last_To) {
	return strdup("");
     }

     tmp = strchr(last_To, '<');
     if (!tmp) {
	return strdup("");
     }
     tmp++;

     tmp2 = strchr(last_To, '>');
     if (!tmp2) {
	return strdup("");
     }

     tmp_len = strlen(tmp) - strlen(tmp2);
     if (tmp_len < 0) {
	return strdup("");
     }

     if(!(last_request_uri = (char *) malloc(tmp_len+1))) ERROR("Cannot allocate !\n");
     memset(last_request_uri, 0, sizeof(last_request_uri));
     if(tmp && (tmp_len > 0)){
       strncpy(last_request_uri, tmp, tmp_len);
     }
     last_request_uri[tmp_len] = '\0';
     return last_request_uri;
  
}

char * call::send_scene(int index, int *send_status, int *len)
{
#define MAX_MSG_NAME_SIZE 30
  static char msg_name[MAX_MSG_NAME_SIZE];
  char *L_ptr1 ;
  char *L_ptr2 ;
  int uselen = 0;
  int tmplen;
  char *hdrbdry;

  assert(send_status);

  /* Socket port must be known before string substitution */
  if (!connect_socket_if_needed()) {
    *send_status = -2;
    return NULL;
  }

  assert(call_socket);

  if (call_socket->ss_congested) {
    *send_status = -1;
    return NULL;
  }

  assert(call_scenario->messages[index]->send_scheme);

  if (!len) {
	len = &uselen;
  }

  char * dest;
  dest = createSendingMessage(call_scenario->messages[index] -> send_scheme, index, len);

  if (dest) {
    L_ptr1=msg_name ;
    L_ptr2=dest ;
    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 ;
  }

  /* Fix: Remove extra "\r\n" if message body ends with "\r\n\r\n" */
  tmplen = (*len) - 1;
  if ((dest[tmplen] == dest[tmplen-2] && dest[tmplen] == '\n')
      && (dest[tmplen-1] == dest[tmplen-3] && dest[tmplen-1] == '\r'))  {
    hdrbdry = strstr(dest, "\r\n\r\n");
    if (NULL != hdrbdry &&  hdrbdry != dest+(tmplen-3))  {
        *len = (*len) - 2;
    }
  }

  *send_status = send_raw(dest, index, *len);

  return dest;
}

void call::do_bookkeeping(message *curmsg) {
  /* If this message increments a counter, do it now. */
  if(int counter = curmsg -> counter) {
    computeStat(CStat::E_ADD_GENERIC_COUNTER, 1, counter - 1);
  }

  /* If this message can be used to compute RTD, do it now */
  if(int rtd = curmsg -> start_rtd) {
    start_time_rtd[rtd - 1] = getmicroseconds();
  }

  if(int rtd = curmsg -> stop_rtd) {
    if (!rtd_done[rtd - 1]) {
      unsigned long long start = start_time_rtd[rtd - 1];
      unsigned long long end = getmicroseconds();

      if(dumpInRtt) {
	call_scenario->stats->computeRtt(start, end, rtd);
      }

      computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION,
	  (end - start) / 1000, rtd - 1);

      if (!curmsg -> repeat_rtd) {
	rtd_done[rtd - 1] = true;
      }
    }
  }
}

void call::tcpClose() {
  terminate(CStat::E_FAILED_TCP_CLOSED);
}

void 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 && !initCall) {
	  sprintf(reason_str, "regexp match failure at index %d", msg_index);
	  new deadcall(id, reason_str);
	}
	break;
            case call::E_AR_REGEXP_SHOULDNT_MATCH:
                computeStat(CStat::E_CALL_FAILED);
                computeStat(CStat::E_FAILED_REGEXP_SHOULDNT_MATCH);
                if (deadcall_wait && !initCall) {
                    sprintf(reason_str, "regexp matched, but shouldn't at index %d", msg_index);
                    new deadcall(id, reason_str);
                }
                break;
      case call::E_AR_HDR_NOT_FOUND:
	computeStat(CStat::E_CALL_FAILED);
	computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND);
	if (deadcall_wait && !initCall) {
	  sprintf(reason_str, "regexp header not found at index %d", msg_index);
	  new deadcall(id, reason_str);
	}
	break;
      case E_AR_CONNECT_FAILED:
	computeStat(CStat::E_CALL_FAILED);
	computeStat(CStat::E_FAILED_TCP_CONNECT);
	if (deadcall_wait && !initCall) {
	  sprintf(reason_str, "connection failed %d", msg_index);
	  new deadcall(id, reason_str);
	}
	break;
      case call::E_AR_NO_ERROR:
      case call::E_AR_STOP_CALL:
	/* Do nothing. */
	break;
    }
  } else {
    if (reason == CStat::E_CALL_SUCCESSFULLY_ENDED || timewait) {
      computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED);
      if (deadcall_wait && !initCall) {
	new deadcall(id, "successful");
      }
    } else {
      computeStat(CStat::E_CALL_FAILED);
      if (reason != CStat::E_NO_ACTION) {
	computeStat(reason);
      }
      if (deadcall_wait && !initCall) {
	sprintf(reason_str, "failed at index %d", msg_index);
	new deadcall(id, reason_str);
      }
    }
  }
  delete this;
}

bool call::next()
{
  msgvec * msgs = &call_scenario->messages;
  if (initCall) {
    msgs = &call_scenario->initmessages;
  }

  int test = (*msgs)[msg_index]->test;
  /* What is the next message index? */
  /* Default without branching: use the next message */
  int new_msg_index = msg_index+1;
  /* If branch needed, overwrite this default */
  if ( ((*msgs)[msg_index]->next >= 0) &&
       ((test == -1) || M_callVariableTable->getVar(test)->isSet())
     ) {
    /* Branching possible, check the probability */
    int chance = (*msgs)[msg_index]->chance;
    if ((chance <= 0) || (rand() > chance )) {
      /* Branch == overwrite with the 'next' attribute value */
      new_msg_index = (*msgs)[msg_index]->next;
    }
  }
  msg_index=new_msg_index;
  recv_timeout = 0;
  if(msg_index >= (int)((*msgs).size())) {
    terminate(CStat::E_CALL_SUCCESSFULLY_ENDED);
    return false;
  }

  return run();
}

bool call::executeMessage(message *curmsg) {
  if(curmsg -> pause_distribution || curmsg->pause_variable != -1) {
    unsigned int pause;
    if (curmsg->pause_distribution) {
      pause  = (int)(curmsg -> pause_distribution -> sample());
    } else {
      int varId = curmsg->pause_variable;
      pause = (int) M_callVariableTable->getVar(varId)->getDouble();
    }
    if (pause < 0) {
      pause = 0;
    }
    if (pause > INT_MAX) {
      pause = INT_MAX;
    }
    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 */
    curmsg->sessions++;
    do_bookkeeping(curmsg);
    executeAction(NULL, curmsg);
    callDebug("Pausing call until %d (is now %d).\n", paused_until, clock_tick);
    setPaused();
    return true;
  }
  else if(curmsg -> M_type == MSG_TYPE_SENDCMD) {
    int send_status;

    if(next_retrans) {
      return true;
    }

    send_status = sendCmdMessage(curmsg);

    if(send_status != 0) { /* Send error */
      return false; /* call deleted */
    }
    curmsg -> M_nbCmdSent++;
    next_retrans = 0;

    do_bookkeeping(curmsg);
    executeAction(NULL, curmsg);
    return(next());
  }
  else if(curmsg -> M_type == MSG_TYPE_NOP) {
    callDebug("Executing NOP at index %d.\n", curmsg->index);
    do_bookkeeping(curmsg);
    executeAction(NULL, curmsg);
    return(next());
  }

  else if(curmsg -> send_scheme) {
    char * msg_snd;
    int msgLen;
    int send_status;

    /* Do not send a new message until the previous one which had
     * retransmission enabled is acknowledged */

    if(next_retrans) {
      setPaused();
      return true;
    }

    /* Handle counters and RTDs for this message. */
    do_bookkeeping(curmsg);

    /* decide whether to increment cseq or not 
     * basically increment for anything except response, ACK or CANCEL 
     * Note that cseq is only used by the [cseq] keyword, and
     * not by default
     */

    int incr_cseq = 0;
    if (!curmsg->send_scheme->isAck() &&
        !curmsg->send_scheme->isCancel() &&
        !curmsg->send_scheme->isResponse()) {
          ++cseq;
          incr_cseq = 1;
    }
    
    msg_snd = send_scene(msg_index, &send_status, &msgLen);
    if(send_status == -1 && errno == EWOULDBLOCK) {
      if (incr_cseq) --cseq;
      /* Have we set the timeout yet? */
      if (send_timeout) {
	/* If we have actually timed out. */
	if (clock_tick > send_timeout) {
	  WARNING("Call-Id: %s, send timeout on message %s:%d: aborting call",
	      id, curmsg->desc, curmsg->index);
	  computeStat(CStat::E_CALL_FAILED);
	  computeStat(CStat::E_FAILED_TIMEOUT_ON_SEND);
	  if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
	    return (abortCall(true));
	  } else {
	    delete this;
	    return false;
	  }
	}
      } else if (curmsg->timeout) {
	/* Initialize the send timeout to the per message 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;
      }
      return true; /* No step, nothing done, retry later */
    } else if(send_status < 0) { /* Send error */
      /* The call was already deleted by connect_socket_if_needed or send_raw,
       * so we should no longer access members. */
      return false;
    }
    /* We have sent the message, so the timeout is no longer needed. */
    send_timeout = 0;

    last_send_index = curmsg->index;
    last_send_len = msgLen;
    last_send_msg = (char *) realloc(last_send_msg, msgLen+1);
    memcpy(last_send_msg, msg_snd, msgLen);
    last_send_msg[msgLen] = '\0';

    if (curmsg->start_txn) {
      transactions[curmsg->start_txn - 1].txnID = (char *)realloc(transactions[curmsg->start_txn - 1].txnID, MAX_HEADER_LEN);
      extract_transaction(transactions[curmsg->start_txn - 1].txnID, last_send_msg);
    }
    if (curmsg->ack_txn) {
      transactions[curmsg->ack_txn - 1].ackIndex = curmsg->index;
    }

    if(last_recv_index >= 0) {
      /* 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 = curmsg->index;

      callDebug("Set Retransmission Hash: %u (recv index %d, send index %d)\n", recv_retrans_hash, recv_retrans_recv_index, recv_retrans_send_index);

      /* Prevent from detecting the cause relation between send and recv 
       * in the next valid send */
      last_recv_hash = 0;
    }

    /* Update retransmission information */
    if(curmsg -> retrans_delay) {
      if((transport == T_UDP) && (retrans_enabled)) {
        next_retrans = clock_tick + curmsg -> retrans_delay;
        nb_retrans = 0;
        nb_last_delay = curmsg->retrans_delay;
      }
    } else {
      next_retrans = 0;
    }

    executeAction(msg_snd, curmsg);

    /* Update scenario statistics */
    curmsg -> nb_sent++;

    return next();
  } else if (curmsg->M_type == MSG_TYPE_RECV
         || curmsg->M_type == MSG_TYPE_RECVCMD
                                                 ) {
    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 > getmilliseconds()) {
	setPaused();
	return true;
      }
      recv_timeout = 0;
      curmsg->nb_timeout++;
      if (curmsg->on_timeout < 0) {
        // if you set a timeout but not a label, the call is aborted 
        WARNING("Call-Id: %s, receive timeout on message %s:%d without label to jump to (ontimeout attribute): aborting call",
                   id, curmsg->desc, curmsg->index);
        computeStat(CStat::E_CALL_FAILED);
        computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
        if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
          return (abortCall(true));
        } else {
          delete this;
          return false;
        }
      }
      WARNING("Call-Id: %s, receive timeout on message %s:%d, jumping to label %d",
                  id, curmsg->desc, curmsg->index, curmsg->on_timeout);
      /* FIXME: We should do something like set index here, but it probably
       * does not matter too much as only nops are allowed in the init stanza. */
      msg_index = curmsg->on_timeout;
      recv_timeout = 0;
      if (msg_index < (int)call_scenario->messages.size()) return true;
      // special case - the label points to the end - finish the call
      computeStat(CStat::E_CALL_FAILED);
      computeStat(CStat::E_FAILED_TIMEOUT_ON_RECV);
      if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
        return (abortCall(true));
      } else {
        delete this;
        return false;
      }
    } else if (curmsg->timeout || defl_recv_timeout) {
      if (curmsg->timeout)
        // If timeout is specified on message receive, use it
        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. */
	setPaused();
    }
  } else {
    WARNING("Unknown message type at %s:%d: %d", curmsg->desc, curmsg->index, curmsg->M_type);
  }
  return true;
}

bool call::run()
{
  bool            bInviteTransaction = false;

  assert(running);

  if (zombie) {
    delete this;
    return false;
  }

  getmilliseconds();

  message *curmsg;
  if (initCall) {
    if(msg_index >= (int)call_scenario->initmessages.size()) {
      ERROR("Scenario initialization overrun for call %s (%p) (index = %d)\n", id, this, msg_index);
    }
    curmsg = call_scenario->initmessages[msg_index];
  } else {
    if(msg_index >= (int)call_scenario->messages.size()) {
      ERROR("Scenario overrun for call %s (%p) (index = %d)\n", id, this, msg_index);
    }
    curmsg = call_scenario->messages[msg_index];
  }

  callDebug("Processing message %d of type %d for call %s at %u.\n", msg_index, curmsg->M_type, id, clock_tick);

  if (curmsg->condexec != -1) {
    bool exec = M_callVariableTable->getVar(curmsg->condexec)->isSet();
    if (curmsg->condexec_inverse) {
	exec = !exec;
    }
    if (!exec) {
     callDebug("Conditional variable %s %s set, so skipping message %d.\n", call_scenario->allocVars->getName(curmsg->condexec), curmsg->condexec_inverse ? "" : "not", msg_index);
     return next();
    }
  }

  /* Manages retransmissions or delete if max retrans reached */
  if(next_retrans && (next_retrans < clock_tick)) {
    nb_retrans++;

    if ( (0 == strncmp (last_send_msg, "INVITE", 6)) )
    {
      bInviteTransaction = true;
    }

    int rtAllowed = min(bInviteTransaction ? max_invite_retrans : max_non_invite_retrans, max_udp_retrans);

    callDebug("Retransmisison required (%d retransmissions, max %d)\n", nb_retrans, rtAllowed);

    if(nb_retrans > rtAllowed) {
      call_scenario->messages[last_send_index] -> nb_timeout ++;
      if (call_scenario->messages[last_send_index]->on_timeout >= 0) {  // action on timeout
          WARNING("Call-Id: %s, timeout on max UDP retrans for message %d, jumping to label %d ",
                      id, msg_index, call_scenario->messages[last_send_index]->on_timeout);
          msg_index = call_scenario->messages[last_send_index]->on_timeout;
          next_retrans = 0;
          recv_timeout = 0;
          if (msg_index < (int)call_scenario->messages.size()) {
		return true;
	  }

          // here if asked to go to the last label  delete the call
          computeStat(CStat::E_CALL_FAILED);
          computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
          if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
            // Abort the call by sending proper SIP message
            return(abortCall(true));
          } else {
            // Just delete existing call
            delete this;
            return false;
          }
      }
      computeStat(CStat::E_CALL_FAILED);
      computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
      if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
        // Abort the call by sending proper SIP message
        WARNING("Aborting call on UDP retransmission timeout for Call-ID '%s'", id);
        return(abortCall(true));
      } else {
        // Just delete existing call
        delete this;
        return false;
      }
    } else {
      nb_last_delay *= 2;
      if (global_t2 < nb_last_delay)
      {
        if (!bInviteTransaction)
        {
          nb_last_delay = global_t2;
      }
      }
      if(send_raw(last_send_msg, last_send_index, last_send_len) < -1) {
        return false;
      }
      call_scenario->messages[last_send_index] -> nb_sent_retrans++;
      computeStat(CStat::E_RETRANSMISSION);
      next_retrans = clock_tick + nb_last_delay;
    }
  }

  if(paused_until) {
    /* Process a pending pause instruction until delay expiration */
    if(paused_until > clock_tick) {
      callDebug("Call is paused until %d (now %d).\n", paused_until, clock_tick);
      setPaused();
      callDebug("Running: %d (wake %d).\n", running, wake());
      return true;
    }
    /* Our pause is over. */
    callDebug("Pause complete, waking up.\n");
    paused_until = 0;
    return next();
  }
  return executeMessage(curmsg);
}

char *default_message_names[] = {
	"3pcc_abort",
	"ack",
	"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)
{
  char buffer[MAX_HEADER_LEN];
  char *desc = buffer;

  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 (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' ", curmsg -> recv_response);
    }
  } else if (curmsg -> M_type == MSG_TYPE_SEND) {
      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending ");
  } else if (curmsg -> M_type == MSG_TYPE_PAUSE) {
      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while pausing ");
  } else if (curmsg -> M_type == MSG_TYPE_SENDCMD) {
      desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "while sending command ");
  } 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 ", curmsg->M_type);
  }
  desc += snprintf(desc, MAX_HEADER_LEN - (desc - buffer), "(index %d)", msg_index);

  WARNING("%s, received '%s'", buffer, msg);

  TRACE_MSG("-----------------------------------------------\n"
             "Unexpected %s message received:\n\n%s\n",
             TRANSPORT_TO_STRING(transport),
             msg);

  callDebug("Unexpected %s message received (index %d, hash %u):\n\n%s\n",
             TRANSPORT_TO_STRING(transport), msg_index, hash(msg), msg);

  if (default_behaviors & DEFAULT_BEHAVIOR_ABORTUNEXP) {
    // if twin socket call => reset the other part here 
    if (twinSippSocket && (msg_index > 0)) {
      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);

    computeStat(CStat::E_CALL_FAILED);
    computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
    if (default_behaviors & DEFAULT_BEHAVIOR_BYE) {
      return (abortCall(true));
    } 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(false);
}

bool call::abortCall(bool writeLog)
{
  int is_inv;

  char * src_recv = NULL ;

  callDebug("Aborting call %s (index %d).\n", id, msg_index);

  if (last_send_msg != NULL) {
    is_inv = !strncmp(last_send_msg, "INVITE", 6);
  } else {
    is_inv = false;
  }  
  if ((creationMode != MODE_SERVER) && (msg_index > 0)) {
    if ((call_established == false) && (is_inv)) {
      src_recv = last_recv_msg ;
      char   L_msg_buffer[SIPP_MAX_MSG_SIZE];
      L_msg_buffer[0] = '\0';

      // Answer unexpected errors (4XX, 5XX and beyond) with an ACK 
      // Contributed by F. Tarek Rogers
      if((src_recv) && (get_reply_code(src_recv) >= 400)) {
        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. */
        if (ack_is_pending == true) {
          /* If an ACK is expected from the other side, send it
           * and send a BYE afterwards                           */
          ack_is_pending = false;
          /* Send an ACK */
	  sendBuffer(createSendingMessage(get_default_message("ack"), -1));

          /* Send the BYE */
	  sendBuffer(createSendingMessage(get_default_message("bye"), -1));
        } else {
          /* Send a CANCEL */
	  sendBuffer(createSendingMessage(get_default_message("cancel"), -1));
        }
      } else {
        /* Call is not established and the reply is not a 4XX, 5XX */
        /* and we didn't received any message. This is the case when */
        /* we are aborting after having send an INVITE and not received */
        /* any answer. */
        /* Do nothing ! */
      }
    } else if (last_recv_msg) {
      /* The call may not be established, if we haven't yet received a message,
       * because the earlier check depends on the first message being an INVITE
       * (although it could be something like a message message, therefore we
       * check that we received a message. */
      char   L_msg_buffer[SIPP_MAX_MSG_SIZE];
      L_msg_buffer[0] = '\0';
      sendBuffer(createSendingMessage(get_default_message("bye"), -1));
    }
  }

  if (writeLog && useCallDebugf) {
    TRACE_CALLDEBUG ("-------------------------------------------------------------------------------\n", id);
    TRACE_CALLDEBUG ("Call debugging information for call %s:\n", id);
    TRACE_CALLDEBUG("%s", debugBuffer);
  }

  stopListening();
  deadcall *deadcall_ptr = NULL;
  if (deadcall_wait && !initCall) {
    char reason[100];
    sprintf(reason, "aborted at index %d", msg_index);
    deadcall_ptr = new deadcall(id, reason);
  }
  delete this;

  return false;
}

bool call::rejectCall()
{
  computeStat(CStat::E_CALL_FAILED);
  computeStat(CStat::E_FAILED_CALL_REJECTED);
  delete this;
  return false;
}


int call::sendCmdMessage(message *curmsg)
{
  char * dest;
  char delimitor[2];
  delimitor[0]=27;
  delimitor[1]=0;

  /* 3pcc extended mode */
  char * peer_dest;
  struct sipp_socket **peer_socket;

  if(curmsg -> M_sendCmdData) {
    // WARNING("---PREPARING_TWIN_CMD---%s---", scenario[index] -> M_sendCmdData);
    dest = createSendingMessage(curmsg -> M_sendCmdData, -1);
    strcat(dest, delimitor);
    //WARNING("---SEND_TWIN_CMD---%s---", dest);

    int rc;

    /* 3pcc extended mode */
    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, &call_peer);
    }else {
      rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER, &call_peer);
    }
    if(rc <  0) {
      computeStat(CStat::E_CALL_FAILED);
      computeStat(CStat::E_FAILED_CMD_NOT_SENT);
      delete this;
      return(-1);
    }

    return(0);
  }
  else
    return(-1);
}


int call::sendCmdBuffer(char* cmd)
{
  char * dest;
  char delimitor[2];
  int  rc;

  delimitor[0]=27;
  delimitor[1]=0;

  dest = cmd ;

  strcat(dest, delimitor);

  rc = write_socket(twinSippSocket, dest, strlen(dest), WS_BUFFER, &twinSippSocket->ss_remote_sockaddr);
  if(rc <  0) {
    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, int *msgLen) {
  static char msg_buffer[SIPP_MAX_MSG_SIZE+2];
  return createSendingMessage(src, P_index, msg_buffer, sizeof(msg_buffer), msgLen);
}

char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buffer, int buf_len, int *msgLen)
{
  char * length_marker = NULL;
  char * auth_marker = NULL;
  MessageComponent *auth_comp = NULL;
  bool auth_comp_allocated = false;
  int    len_offset = 0;
  char *dest = msg_buffer;
  bool supresscrlf = false;

  *dest = '\0';

  for (int i = 0; i < src->numComponents(); i++) {
    MessageComponent *comp = src->getComponent(i);
    int left = buf_len - (dest - msg_buffer);
    switch(comp->type) {
      case E_Message_Literal:
	if (supresscrlf) {
	  char *ptr = comp->literal;
	  while (isspace(*ptr)) ptr++;
	  dest += snprintf(dest, left, "%s", ptr);
	  supresscrlf = false;
	} else {
	  memcpy(dest, comp->literal, comp->literalLen);
	  dest += comp->literalLen;
          *dest = '\0';
	}
	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);
	break;
      case E_Message_Local_IP:
	dest += snprintf(dest, left, "%s", local_ip_escaped);
	break;
      case E_Message_Local_Port:
	int port;
	if((transport == T_UDP) && (multisocket) && (sendMode != MODE_SERVER)) {
	  port = call_port;
	} else {
	  port =  local_port;
	}
	dest += snprintf(dest, left, "%d", port + comp->offset);
	break;
      case E_Message_Transport:
	dest += snprintf(dest, left, "%s", TRANSPORT_TO_STRING(transport));
	break;
      case E_Message_Local_IP_Type:
	dest += snprintf(dest, left, "%s", (local_ip_is_ipv6 ? "6" : "4"));
	break;
      case E_Message_Server_IP: {
	  /* We should do this conversion once per socket creation, rather than
	   * repeating it every single time. */
	  struct sockaddr_storage server_sockaddr;

	  sipp_socklen_t len = SOCK_ADDR_SIZE(&server_sockaddr);
	  getsockname(call_socket->ss_fd,
	      (sockaddr *)(void *)&server_sockaddr, &len);

	  if (server_sockaddr.ss_family == AF_INET6) {
	    char * temp_dest;
	    temp_dest = (char *) malloc(INET6_ADDRSTRLEN);
	    memset(temp_dest,0,INET6_ADDRSTRLEN);
	    inet_ntop(AF_INET6,
		&((_RCAST(struct sockaddr_in6 *,&server_sockaddr))->sin6_addr),
		temp_dest,
		INET6_ADDRSTRLEN);
	    dest += snprintf(dest, left, "%s",temp_dest);
	  } else {
	    dest += snprintf(dest, left, "%s",
		inet_ntoa((_RCAST(struct sockaddr_in *,&server_sockaddr))->sin_addr));
	  }
	}
	break;
      case E_Message_Media_IP:
	dest += snprintf(dest, left, "%s", media_ip_escaped);
	break;
      case E_Message_Media_Port:
      case E_Message_Auto_Media_Port: {
	int port = media_port + comp->offset;
	if (comp->type == E_Message_Auto_Media_Port) {
	  port = media_port + (4 * (number - 1)) % 10000 + comp->offset;
	}
#ifdef PCAPPLAY
	char *begin = dest;
	while (begin > msg_buffer) {
	  if (*begin == '\n') {
	    break;
	  }
	  begin--;
	}
	if (begin == msg_buffer) {
	  ERROR("Can not find beginning of a line for the media port!\n");
	}
	if (strstr(begin, "audio")) {
	  if (media_ip_is_ipv6) {
	    (_RCAST(struct sockaddr_in6 *, &(play_args_a.from)))->sin6_port = port;
	  } else {
	    (_RCAST(struct sockaddr_in *, &(play_args_a.from)))->sin_port = port;
	  }
	} else if (strstr(begin, "video")) {
	  if (media_ip_is_ipv6) {
	    (_RCAST(struct sockaddr_in6 *, &(play_args_v.from)))->sin6_port = port;
	  } else {
	    (_RCAST(struct sockaddr_in *, &(play_args_v.from)))->sin_port = port;
	  }
	} else {
	  ERROR("media_port keyword with no audio or video on the current line (%s)", begin);
	}
#endif
	dest += sprintf(dest, "%u", port);
	break;
      }
      case E_Message_Media_IP_Type:
	dest += snprintf(dest, left, "%s", (media_ip_is_ipv6 ? "6" : "4"));
	break;
      case E_Message_Call_Number:
	dest += snprintf(dest, left, "%u", number);
	break;
      case E_Message_DynamicId:
        dest += snprintf(dest, left, "%u", call::dynamicId);
        // increment at each request
        dynamicId += stepDynamicId;
        if ( this->dynamicId > maxDynamicId ) { call::dynamicId = call::startDynamicId; } ;
      break;
      case E_Message_Call_ID:
	dest += snprintf(dest, left, "%s", id);
	break;
      case E_Message_CSEQ:
	dest += snprintf(dest, left, "%u", cseq + comp->offset);
	break;
      case E_Message_PID:
	dest += snprintf(dest, left, "%d", pid);
	break;
      case E_Message_Service:
	dest += snprintf(dest, left, "%s", service);
	break;
      case E_Message_Branch:
	/* Branch is magic cookie + call number + message index in scenario */
	if(P_index == -2){
	  dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, msg_index-1 + comp->offset);
	} else {
	  dest += snprintf(dest, left, "z9hG4bK-%u-%u-%d", pid, number, P_index + comp->offset);
	}
	break;
      case E_Message_Index:
	dest += snprintf(dest, left, "%d", P_index);
	break;
      case E_Message_Next_Url:
	if (next_req_url) {
	  dest += sprintf(dest, "%s", next_req_url);
	}
	break;
      case E_Message_Len:
	length_marker = dest;
	dest += snprintf(dest, left, "     ");
	len_offset = comp->offset;
	break;
      case E_Message_Authentication:
	if (auth_marker) {
	  ERROR("Only one [authentication] keyword is currently supported!\n");
	}
	auth_marker = dest;
	dest += snprintf(dest, left, "[authentication place holder]");
	auth_comp = comp;
	break;
      case E_Message_Peer_Tag_Param:
	if(peer_tag) {
	  dest += snprintf(dest, left, ";tag=%s", peer_tag);
	}
	break;
      case E_Message_Routes:
	if (dialog_route_set) {
	  dest += sprintf(dest, "Route: %s", dialog_route_set);
	} else if (*(dest - 1) == '\n') {
	  supresscrlf = true;
	}
	break;
      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_SippVersion:
	dest += snprintf(dest, left, "%s", SIPP_VERSION);
	break;
      case E_Message_Variable: {
	 int varId = comp->varId;
	 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");
	 }
	 if (*(dest - 1) == '\n') {
	   supresscrlf = true;
	 }
	 break;
      }
      case E_Message_Fill: {
        int varId = comp->varId;
	int length = (int) M_callVariableTable->getVar(varId)->getDouble();
	if (length < 0) {
	  length = 0;
	}
	char *filltext = comp->literal;
	int filllen = strlen(filltext);
	if (filllen == 0) {
	  ERROR("Internal error: [fill] keyword has zero-length text.");
	}
	for (int i = 0, j = 0; i < length; i++, j++) {
	  *dest++ = filltext[j % filllen];
	}
	*dest = '\0';
	break;
      }
      case E_Message_File: {
        char buffer[MAX_HEADER_LEN];
	createSendingMessage(comp->comp_param.filename, -2, buffer, sizeof(buffer));
	FILE *f = fopen(buffer, "r");
	if (!f) {
	  ERROR("Could not open '%s': %s\n", buffer, strerror(errno));
	}
	int ret;
	while ((ret = fread(dest, 1, left, f)) > 0) {
		left -= ret;
		dest += ret;
	}
	if (ret < 0) {
	  ERROR("Error reading '%s': %s\n", buffer, strerror(errno));
	}
	fclose(f);
	break;
      }
      case E_Message_Injection: {
	char *orig_dest = dest;
	getFieldFromInputFile(comp->comp_param.field_param.filename, comp->comp_param.field_param.field, comp->comp_param.field_param.line, dest);
	/* We are injecting an authentication line. */
	if (char *tmp = strstr(orig_dest, "[authentication")) {
	  if (auth_marker) {
	    ERROR("Only one [authentication] keyword is currently supported!\n");
	  }
	  auth_marker = tmp;
	  auth_comp = (struct MessageComponent *)calloc(1, sizeof(struct MessageComponent));
	  if (!auth_comp) { ERROR("Out of memory!"); }
	  auth_comp_allocated = true;

	  tmp = strchr(auth_marker, ']');
	  char c = *tmp;
	  *tmp = '\0';
	  SendingMessage::parseAuthenticationKeyword(call_scenario, auth_comp, auth_marker);
	  *tmp = c;
	}
	if (*(dest - 1) == '\n') {
	  supresscrlf = true;
	}
	break;
      }
      case E_Message_Last_Header: {
	char * last_header = get_last_header(comp->literal);
	if(last_header) {
	  dest += sprintf(dest, "%s", last_header);
	}
	if (*(dest - 1) == '\n') {
	  supresscrlf = true;
	}
	break;
      }
      case E_Message_Custom: {
	dest += comp->comp_param.fxn(this, comp, dest, left);
	break;
      }
      case E_Message_Last_Message:
        if(last_recv_msg && strlen(last_recv_msg)) {
	  dest += sprintf(dest, "%s", last_recv_msg);
	}
	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;
      }
      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)
	  ERROR("[tdmmap] keyword without -tdmmap parameter on command line");
	dest += snprintf(dest, left, "%d.%d.%d/%d",
	    tdm_map_x+(int((tdm_map_number)/((tdm_map_b+1)*(tdm_map_c+1))))%(tdm_map_a+1),
	    tdm_map_h,
	    tdm_map_y+(int((tdm_map_number)/(tdm_map_c+1)))%(tdm_map_b+1),
	    tdm_map_z+(tdm_map_number)%(tdm_map_c+1)
	    );
	break;
    }
  }
  /* Need the body for length and auth-int calculation */
  char *body;
  char *auth_body = NULL;
  if (length_marker || auth_marker) {
    body = strstr(msg_buffer, "\r\n\r\n");
    if (body) {
	auth_body = body;    
	auth_body += strlen("\r\n\r\n");
    }
  }

  /* Fix up the length. */
  if (length_marker) {
    if (auth_marker > body) {
      ERROR("The authentication keyword should appear in the message header, not the body!");
    }

    if (body && dest - body > 4 && dest - body < 100004) {
      char tmp = length_marker[5];
      sprintf(length_marker, "%5u", (unsigned)(dest - body - 4 + len_offset));
      length_marker[5] = tmp;
    } else {
      // Other cases: Content-Length is 0
      sprintf(length_marker, "    0\r\n\r\n");
    }
  }

  if (msgLen) {
    *msgLen = dest - msg_buffer;
  }

  /*
   * The authentication substitution must be done outside the above
   * loop because auth-int will use the body (which must have already
   * been keyword substituted) to build the md5 hash
   */
  if (auth_marker) {
    if (!dialog_authentication) {
      ERROR("Authentication keyword without dialog_authentication!");
    }

    int	   auth_marker_len;
    char * tmp;
    int  authlen;

    auth_marker_len = (strchr(auth_marker, ']') + 1) - auth_marker;

    /* Need the Method name from the CSeq of the Challenge */
    char method[MAX_HEADER_LEN];
    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);

    if (!auth_body) {
      auth_body = "";
    }

    /* Determine the type of credentials. */
    char result[MAX_HEADER_LEN];
    if (dialog_challenge_type == 401) {
      /* Registrars use Authorization */
      authlen = sprintf(result, "Authorization: ");
    } else {
      /* Proxies use Proxy-Authorization */
      authlen = sprintf(result, "Proxy-Authorization: ");
    }

    /* Build the auth credenticals */
    char uri[MAX_HEADER_LEN];
    sprintf (uri, "%s:%d", remote_ip, remote_port);
    /* 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, auth_body, dialog_authentication,
	  my_aka_OP, my_aka_AMF, my_aka_K, result + authlen) == 0) {
      ERROR("%s", result + authlen);
    }
    authlen = strlen(result);

    /* Shift the end of the message to its rightful place. */
    memmove(auth_marker + authlen, auth_marker + auth_marker_len, strlen(auth_marker + auth_marker_len) + 1);
    /* Copy our result into the hole. */
    memcpy(auth_marker, result, authlen);
    if (msgLen) {
	*msgLen += (authlen -  auth_marker_len);
    }
  }

  if (auth_comp_allocated) {
    SendingMessage::freeMessageComponent(auth_comp);
  }

  return msg_buffer;
}

bool call::process_twinSippCom(char * msg)
{
  int		  search_index;
  bool            found = false;
  T_ActionResult  actionResult;

  callDebug("Processing incoming command for call-ID %s:\n%s\n\n", id, msg);

  setRunning();

  if (checkInternalCmd(msg) == false) {

    for(search_index = msg_index;
      search_index < (int)call_scenario->messages.size();
      search_index++) {
      if(call_scenario->messages[search_index] -> M_type != MSG_TYPE_RECVCMD) {
	if(call_scenario->messages[search_index] -> optional) {
	  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);
	callDebug("Unexpected control message received (I was expecting a different type of message):\n%s\n\n", msg);
	return rejectCall();
      } else {
	if(extendedTwinSippMode){                   // 3pcc extended mode
	  if(check_peer_src(msg, search_index)){
	    found = true;
	    break;
	  } else{
	    WARNING("Unexpected sender for the received peer message \n%s\n", msg);
	    return rejectCall();
	  }
	}
	else {
	  found = true;
	  break;
	}
      }
    }

    if (found) {
      call_scenario->messages[search_index]->M_nbCmdRecv ++;
      do_bookkeeping(call_scenario->messages[search_index]);

      // variable treatment
      // Remove \r, \n at the end of a received command
      // (necessary for transport, to be removed for usage)
      while ( (msg[strlen(msg)-1] == '\n') &&
      (msg[strlen(msg)-2] == '\r') ) {
        msg[strlen(msg)-2] = 0;
      }
      actionResult = executeAction(msg, call_scenario->messages[search_index]);

      if(actionResult != call::E_AR_NO_ERROR) {
        // Store last action result if it is an error
        // and go on with the scenario
        call::last_action_result = actionResult;
        if (actionResult == E_AR_STOP_CALL) {
            return rejectCall();
        } else if (actionResult == E_AR_CONNECT_FAILED) {
	  terminate(CStat::E_FAILED_TCP_CONNECT);
	  return false;
	}
      }
    } else {
      TRACE_MSG("Unexpected control message received (no such message found):\n%s\n", msg);
      callDebug("Unexpected control message received (no such message found):\n%s\n\n", msg);
      return rejectCall();
    }
    msg_index = search_index; //update the state machine
    return(next());
  } else {
    return (false);
  }
}

bool call::checkInternalCmd(char * cmd)
{

  char * L_ptr1, * L_ptr2, L_backup;

  L_ptr1 = strstr(cmd, "internal-cmd:");
  if (!L_ptr1) {return (false);}
  L_ptr1 += 13 ;
  while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; }
  if (!(*L_ptr1)) {return (false);}
  L_ptr2 = L_ptr1;
  while((*L_ptr2) && 
        (*L_ptr2 != ' ') && 
        (*L_ptr2 != '\t') && 
        (*L_ptr2 != '\r') && 
        (*L_ptr2 != '\n')) { 
    L_ptr2 ++;
  } 
  if(!*L_ptr2) { return (false); }
  L_backup = *L_ptr2;
  *L_ptr2 = 0;

  if (strcmp(L_ptr1, "abort_call") == 0) {
    *L_ptr2 = L_backup;
    abortCall(true);
    computeStat(CStat::E_CALL_FAILED);
    return (true);
  }

  *L_ptr2 = L_backup;
  return (false);
}

bool call::check_peer_src(char * msg, int search_index)
{
 char * L_ptr1, * L_ptr2, L_backup ;

 L_ptr1 = strstr(msg, "From:");
 if (!L_ptr1) {return (false);}
 L_ptr1 += 5 ;
 while((*L_ptr1 == ' ') || (*L_ptr1 == '\t')) { L_ptr1++; }
 if (!(*L_ptr1)) {return (false);}
 L_ptr2 = L_ptr1;
  while((*L_ptr2) &&
        (*L_ptr2 != ' ') &&
        (*L_ptr2 != '\t') &&
        (*L_ptr2 != '\r') &&
        (*L_ptr2 != '\n')) {
    L_ptr2 ++;
  }
  if(!*L_ptr2) { return (false); }
  L_backup = *L_ptr2;
  *L_ptr2 = 0;
  if (strcmp(L_ptr1, call_scenario->messages[search_index] -> peer_src) == 0) {
    *L_ptr2 = L_backup;
    return(true);
  }
 
  *L_ptr2 = L_backup;
  return (false);
}


void call::extract_cseq_method (char* method, char* msg)
{
  char* cseq ;
  if ((cseq = strstr (msg, "CSeq")))
  {
    char * value ;
    if (( value = strchr (cseq,  ':')))
    {
      value++;
      while ( isspace(*value)) value++;  // ignore any white spaces after the :
      while ( !isspace(*value)) value++;  // ignore the CSEQ numnber
      value++;
      char *end = value;
      int nbytes = 0;
      /* A '\r' terminates the line, so we want to catch that too. */
      while ((*end != '\r') && (*end != '\n')) { end++; nbytes++; }
      if (nbytes > 0) strncpy (method, value, nbytes);
      method[nbytes] = '\0';
    }
  }
}

void call::extract_transaction (char* txn, char* msg)
{
  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)
{

  /* clean up the next_req_url -- Record routes may have extra gunk
     that needs to be removed
   */
  char* actual_req_url = strchr(next_req_url, '<');
  if (actual_req_url) 
  {
    /* using a temporary buffer */
    char tempBuffer[MAX_HEADER_LEN];
    strcpy(tempBuffer, actual_req_url + 1);
    actual_req_url = strrchr(tempBuffer, '>');
    *actual_req_url = '\0';
    strcpy(next_req_url, tempBuffer);
  }

}

void call::computeRouteSetAndRemoteTargetUri (char* rr, char* contact, bool bRequestIncoming)
{
  if (0 >=strlen (rr))
  {
    //
    // there are no RR headers. Simply set up the contact as our target uri
    //
    if (0 < strlen(contact))
    {
      strcpy (next_req_url, contact);
    }

    formatNextReqUrl(next_req_url);

    return;
  }

  char actual_rr[MAX_HEADER_LEN];
  char targetURI[MAX_HEADER_LEN];
  memset(actual_rr, 0, sizeof(actual_rr));

  bool isFirst = true;
  bool bCopyContactToRR = false;

  while (1)
  {
      char* pointer = NULL;
      if (bRequestIncoming)
      {
        pointer = strchr (rr, ',');
      }
      else
      {
        pointer = strrchr(rr, ',');
      }

      if (pointer) 
      {
        if (!isFirst) 
        {
          if (strlen(actual_rr) )
          {
            strcat(actual_rr, pointer + 1);
          }
          else
          {
            strcpy(actual_rr, pointer + 1);
          }
          strcat(actual_rr, ",");
        } 
        else 
        {
          isFirst = false;
          if (NULL == strstr (pointer, ";lr"))
          {
            /* bottom most RR is the next_req_url */
            strcpy (targetURI, pointer + 1);
            bCopyContactToRR = true;
          }
          else
          {
            /* the hop is a loose router. Thus, the target URI should be the
             * contact
             */
            strcpy (targetURI, contact);
            strcpy(actual_rr, pointer + 1);
            strcat(actual_rr, ",");
          }
        }
      } 
      else 
      {
        if (!isFirst) 
        {
            strcat(actual_rr, rr);
        } 
        //
        // this is the *only* RR header that was found
        //
        else 
        {
          if (NULL == strstr (rr, ";lr"))
          {
            /* bottom most RR is the next_req_url */
            strcpy (targetURI, rr);
            bCopyContactToRR = true;
          }
          else
          {
            /* the hop is a loose router. Thus, the target URI should be the
             * contact
             */
            strcpy (actual_rr, rr);
            strcpy (targetURI, contact);
          }
        }
        break;
      }
      *pointer = '\0';
  }

  if (bCopyContactToRR)
  {
    if (0 < strlen (actual_rr))
    {
      strcat(actual_rr, ",");
      strcat(actual_rr, contact);
    }
    else
    {
      strcpy(actual_rr, contact);
    }
  }

  if (strlen(actual_rr)) 
  {
    dialog_route_set = (char *)
        calloc(1, strlen(actual_rr) + 2);
    sprintf(dialog_route_set, "%s", actual_rr);
  } 

  if (strlen (targetURI))
  {
    strcpy (next_req_url, targetURI);
    formatNextReqUrl (next_req_url);
  }
}

bool call::matches_scenario(unsigned int index, int reply_code, char * request, char * responsecseqmethod, char *txn)
{
  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 (transactions[curmsg->response_txn - 1].txnID && !strcmp(transactions[curmsg->response_txn - 1].txnID, 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, struct sockaddr_storage *src)
{
  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;

  getmilliseconds();
  callDebug("Processing %d byte incoming message for call-ID %s (hash %u):\n%s\n\n", strlen(msg), id, hash(msg), msg);

  setRunning();

  /* Ignore the messages received during a pause if -pause_msg_ign is set */
  if(call_scenario->messages[msg_index] -> M_type == MSG_TYPE_PAUSE && pause_msg_ign) return(true);

  /* Get our destination if we have none. */
  if (call_peer.ss_family == AF_UNSPEC && src) {
    memcpy(&call_peer, src, SOCK_ADDR_SIZE(src));
  }

  /* Authorize nop as a first command, even in server mode */
  if((msg_index == 0) && (call_scenario->messages[msg_index] -> M_type == MSG_TYPE_NOP)) {
    queue_up (msg);
    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_recv_index >= 0) && (recv_retrans_hash == cookie)) {

      int status;

      if(lost(recv_retrans_recv_index)) {
	TRACE_MSG("%s message (retrans) lost (recv).",
	      TRANSPORT_TO_STRING(transport));
	callDebug("%s message (retrans) lost (recv) (hash %u)\n", TRANSPORT_TO_STRING(transport), hash(msg));

	if(comp_state) { comp_free(&comp_state); }
	call_scenario->messages[recv_retrans_recv_index] -> nb_lost++;
	return true;
      }

      call_scenario->messages[recv_retrans_recv_index] -> nb_recv_retrans++;

      send_scene(recv_retrans_send_index, &status, NULL);

      if(status >= 0) {
	call_scenario->messages[recv_retrans_send_index] -> nb_sent_retrans++;
	computeStat(CStat::E_RETRANSMISSION);
      } else if(status < 0) {
	return false;
      }

      return true;
    }

    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.
       *
       * This case appears when the UAS has send a 200 but not received
       * a ACK yet. Thus, the UAS retransmit the 200 (invite transaction)
       * until it receives a ACK. In this case, it nevers sends the 200
       * from the  BYE, until it has reveiced the previous 200. Thus,
       * the UAC retransmit the BYE, and this BYE is considered as an
       * unexpected.
       *
       * This case can also appear in case of message duplication by
       * the network. This should not be considered as an unexpected.
       */
      call_scenario->messages[last_recv_index]->nb_recv_retrans++;
      return true;
    }
  }

  /* Is it a response ? */
  if((msg[0] == 'S') && 
     (msg[1] == 'I') &&
     (msg[2] == 'P') &&
     (msg[3] == '/') &&
     (msg[4] == '2') &&
     (msg[5] == '.') &&
     (msg[6] == '0')    ) {    

    reply_code = get_reply_code(msg);
    if(!reply_code) {
      if (!process_unexpected(msg)) {
        return false; // Call aborted by unexpected message handling
      }
#ifdef PCAPPLAY
    } else if ((hasMedia == 1) && *(strstr(msg, "\r\n\r\n")+4) != '\0') {
      /* Get media info if we find something like an SDP */
      get_remote_media_addr(msg);
#endif
    }
    /* It is a response: update peer_tag */
    ptr = get_peer_tag(msg);
    if (ptr) {
      if(strlen(ptr) > (MAX_HEADER_LEN - 1)) {
        ERROR("Peer tag too long. Change MAX_HEADER_LEN and recompile sipp");
      }
      if(peer_tag) { free(peer_tag); }
      peer_tag = strdup(ptr);
      if (!peer_tag) {
	ERROR("Out of memory allocating peer tag.");
      }
    }
    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);
      request[ptr - msg] = 0;
      // Check if we received an ACK => call established
      if (strcmp(request,"ACK")==0) {
        call_established=true;
      }
#ifdef PCAPPLAY
      /* In case of INVITE or re-INVITE, ACK or PRACK
         get the media info if needed (= we got a pcap
         play action) */
      if ((strncmp(request, "INVITE", 6) == 0) 
       || (strncmp(request, "ACK", 3) == 0) 
       || (strncmp(request, "PRACK", 5) == 0)     		
       && (hasMedia == 1)) 
        get_remote_media_addr(msg);
#endif

      reply_code = 0;
    } else {
      ERROR("SIP method too long in received message '%s'",
               msg);
    }
  } else {
    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 < (int)call_scenario->messages.size();
      search_index++) {
    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 */
      break;
    }

    found = true;
    /* TODO : this is a little buggy: If a 100 trying from an INVITE
     * is delayed by the network until the BYE is sent, it may
     * stop BYE transmission erroneously, if the BYE also expects
     * a 100 trying. */    
    break;
  }

  /* Try to find it in the old non-mandatory receptions */
  if(!found) {
    bool contig = true;
    for(search_index = msg_index - 1;
        search_index >= 0;
        search_index--) {
      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 {
	  if (int checkTxn = call_scenario->messages[search_index]->response_txn) {
	    /* This is a reply to an old transaction. */
	    if (!strcmp(transactions[checkTxn - 1].txnID, txn)) {
		/* This reply is provisional, so it should have no effect if we recieve it out-of-order. */
		if (reply_code >= 100 && reply_code <= 199) {
		  TRACE_MSG("-----------------------------------------------\n"
		      "Ignoring provisional %s message for transaction %s:\n\n%s\n",
		      TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg);
		  callDebug("Ignoring provisional %s message for transaction %s (hash %u):\n\n%s\n",
		      TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg);
		  return true;
		} else if (int ackIndex = transactions[checkTxn - 1].ackIndex) {
		  /* This is the message before an ACK, so verify that this is an invite transaction. */
		  assert (call_scenario->transactions[checkTxn - 1].isInvite);
		  sendBuffer(createSendingMessage(call_scenario->messages[ackIndex] -> send_scheme, ackIndex));
		  return true;
		} else {
		  assert (!call_scenario->transactions[checkTxn - 1].isInvite);
		  /* This is a non-provisional message for the transaction, and
		   * we have already gotten our allowable response.  Just make sure
		   * that it is not a retransmission of the final response. */
		  if (transactions[checkTxn - 1].txnResp == hash(msg)) {
		    /* We have gotten this retransmission out-of-order, let's just ignore it. */
		    TRACE_MSG("-----------------------------------------------\n"
			"Ignoring final %s message for transaction %s:\n\n%s\n",
			TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, msg);
		    callDebug("Ignoring final %s message for transaction %s (hash %u):\n\n%s\n",
			TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg);
		    WARNING("Ignoring final %s message for transaction %s (hash %u):\n\n%s\n",
			TRANSPORT_TO_STRING(transport), call_scenario->transactions[checkTxn - 1].name, hash(msg), msg);
		    return true;
		  }
		}
	    }
	  } 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;
	    }
	  }
        }
      }
    }
  }

  /* If it is still not found, process an unexpected message */
  if(!found) {
    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 {
      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
   */

  /* Simulate loss of messages */
  if(lost(search_index)) {
    TRACE_MSG("%s message lost (recv).",
               TRANSPORT_TO_STRING(transport));
    callDebug("%s message lost (recv) (hash %u).\n",
               TRANSPORT_TO_STRING(transport), hash(msg));
    if(comp_state) { comp_free(&comp_state); }
    call_scenario->messages[search_index] -> nb_lost++;
    return true;
  }

  /* If we are part of a transaction, mark this as the final response. */
  if (int checkTxn = call_scenario->messages[search_index]->response_txn) {
    transactions[checkTxn - 1].txnResp = hash(msg);
  }


  /* Handle counters and RTDs for this message. */
  do_bookkeeping(call_scenario->messages[search_index]);

  /* Increment the recv counter */
  call_scenario->messages[search_index] -> nb_recv++;

  // Action treatment
  if (found) {
    //WARNING("---EXECUTE_ACTION_ON_MSG---%s---", msg);

    actionResult = executeAction(msg, call_scenario->messages[search_index]);

    if(actionResult != call::E_AR_NO_ERROR) {
      // Store last action result if it is an error
      // and go on with the scenario
      call::last_action_result = actionResult;
      if (actionResult == E_AR_STOP_CALL) {
          return rejectCall();
      } else if (actionResult == E_AR_CONNECT_FAILED) {
	  terminate(CStat::E_FAILED_TCP_CONNECT);
	  return false;
      }
    }
  }

  if (request) { // update [cseq] with received CSeq
    unsigned long int rcseq = get_cseq_value(msg);
    if (rcseq > cseq) cseq = rcseq;
  }

  /* This is an ACK/PRACK or a response, and its index is greater than the 
   * current active retransmission message, so we stop the retrans timer. 
   * True also for CANCEL and BYE that we also want to answer to */
  if(((reply_code) ||
      ((!strcmp(request, "ACK")) ||
       (!strcmp(request, "CANCEL")) || (!strcmp(request, "BYE")) ||
       (!strcmp(request, "PRACK"))))  &&
     (search_index > last_send_index)) {
   /*
    * We should stop any retransmission timers on receipt of a provisional response only for INVITE
    * transactions. Non INVITE transactions continue to retransmit at T2 until a final response is 
    * received
    */
    if ( (0 == reply_code) || // means this is a request.
         (200 <= reply_code) ||  // final response
         ((0 != reply_code) && (0 == strncmp (responsecseqmethod, "INVITE", strlen(responsecseqmethod)))) ) // prov for INVITE
    {
    next_retrans = 0;
  }
    else
    {
      /*
       * We are here due to a provisional response for non INVITE. Update our next retransmit.
       */
      next_retrans = clock_tick + global_t2;
      nb_last_delay = global_t2;

    }
  }

  /* This is a response with 200 so set the flag indicating that an
   * ACK is pending (used to prevent from release a call with CANCEL
   * when an ACK+BYE should be sent instead)                         */
  if (reply_code == 200) {
    ack_is_pending = true;
  }

  /* store the route set only once. TODO: does not support target refreshes!! */
  if (call_scenario->messages[search_index] -> bShouldRecordRoutes &&
          NULL == dialog_route_set ) {

      next_req_url = (char*) realloc(next_req_url, MAX_HEADER_LEN);

      char rr[MAX_HEADER_LEN];
      memset(rr, 0, sizeof(rr));
      strcpy(rr, get_header_content(msg, (char*)"Record-Route:"));

      // WARNING("rr [%s]", rr);
      char ch[MAX_HEADER_LEN];
      strcpy(ch, get_header_content(msg, (char*)"Contact:"));

      /* decorate the contact with '<' and '>' if it does not have it */
      char* contDecorator = strchr(ch, '<');
      if (NULL == contDecorator) {
         char tempBuffer[MAX_HEADER_LEN];
         sprintf(tempBuffer, "<%s>", ch);
         strcpy(ch, tempBuffer);
      }

      /* should cache the route set */
      if (reply_code) {
        computeRouteSetAndRemoteTargetUri (rr, ch, false);
      }
      else
      {
        computeRouteSetAndRemoteTargetUri (rr, ch, true);
      }
      // WARNING("next_req_url is [%s]", next_req_url);
  }

  /* store the authentication info */
  if ((call_scenario->messages[search_index] -> bShouldAuthenticate) &&
          (reply_code == 401 || reply_code == 407)) {

      /* is a challenge */
      char auth[MAX_HEADER_LEN];
      memset(auth, 0, sizeof(auth));
      strcpy(auth, get_header_content(msg, (char*)"Proxy-Authenticate:"));
      if (auth[0] == 0) {
        strcpy(auth, get_header_content(msg, (char*)"WWW-Authenticate:"));
      }
      if (auth[0] == 0) {
        ERROR("Couldn't find 'Proxy-Authenticate' or 'WWW-Authenticate' in 401 or 407!");
      }

      dialog_authentication = (char *) realloc(dialog_authentication, strlen(auth) + 2);
      sprintf(dialog_authentication, "%s", auth);

      /* Store the code of the challenge for building the proper header */
      dialog_challenge_type = reply_code;
  }

  /* 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
   * in our messages. */
  last_recv_index = search_index;
  last_recv_hash = cookie;
  callDebug("Set Last Recv Hash: %u (recv index %d)\n", last_recv_hash, last_recv_index);
  last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1);
  strcpy(last_recv_msg, msg);

  /* If this was a mandatory message, or if there is an explicit next label set
   * we must update our state machine.  */
  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 = wake();
    unsigned int candidate;

    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 < (int)call_scenario->messages.size(); 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;
	}
	candidate = defl_recv_timeout;
      }
      if (!timeout || (clock_tick + candidate < timeout)) {
	timeout = clock_tick + candidate;
      }
    }

    setPaused();
  }
  return true;
}

double call::get_rhs(CAction *currentAction) {
  if (currentAction->getVarInId()) {
    return M_callVariableTable->getVar(currentAction->getVarInId())->getDouble();
  } else {
    return currentAction->getDoubleValue();
  }
}

call::T_ActionResult call::executeAction(char * msg, message *curmsg)
{
  CActions*  actions;
  CAction*   currentAction;

  actions = curmsg->M_actions;
  // looking for action to do on this message
  if(actions == NULL) {
    return(call::E_AR_NO_ERROR);
  }

  for(int i=0; i<actions->getActionSize(); i++) {
    currentAction = actions->getAction(i);
    if(currentAction == NULL) {
      continue;
    }

    if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) {
      char msgPart[MAX_SUB_MESSAGE_LENGTH];

      /* Where to look. */
      char *haystack;

      if(currentAction->getLookingPlace() == CAction::E_LP_HDR) {
	extractSubMessage (msg,
	    currentAction->getLookingChar(),
	    msgPart,
	    currentAction->getCaseIndep(),
	    currentAction->getOccurence(),
	    currentAction->getHeadersOnly());
	if(currentAction->getCheckIt() == true && (strlen(msgPart) < 0)) {
	  // the sub message is not found and the checking action say it
	  // MUST match --> Call will be marked as failed but will go on
	  WARNING("Failed regexp match: header %s not found in message %s\n", currentAction->getLookingChar(), msg);
	  return(call::E_AR_HDR_NOT_FOUND);
	}
	haystack = msgPart;
      } else if(currentAction->getLookingPlace() == CAction::E_LP_BODY) {
	haystack = strstr(msg, "\r\n\r\n");
	if (!haystack) {
	  if (currentAction->getCheckIt() == true) {
	    WARNING("Failed regexp match: body not found in message %s\n", msg);
	    return(call::E_AR_HDR_NOT_FOUND);
	  }
	  msgPart[0] = '\0';
	  haystack = msgPart;
	}
	haystack += strlen("\r\n\r\n");
      } else if(currentAction->getLookingPlace() == CAction::E_LP_MSG) {
	haystack = msg;
      } else if(currentAction->getLookingPlace() == CAction::E_LP_VAR) {
	/* Get the input variable. */
	haystack = M_callVariableTable->getVar(currentAction->getVarInId())->getString();
	if (!haystack) {
	  if (currentAction->getCheckIt() == true) {
	    WARNING("Failed regexp match: variable $%d not set\n", currentAction->getVarInId());
	    return(call::E_AR_HDR_NOT_FOUND);
	  }
	}
      } else {
	ERROR("Invalid looking place: %d\n", currentAction->getLookingPlace());
      }
      currentAction->executeRegExp(haystack, M_callVariableTable);

      if( (!(M_callVariableTable->getVar(currentAction->getVarId())->isSet())) && (currentAction->getCheckIt() == true) ) {
	// the message doesn't match and the checkit action say it MUST match
	// Allow easier regexp debugging
	WARNING("Failed regexp match: looking in '%s', with regexp '%s'",
	    haystack, currentAction->getRegularExpression());
	return(call::E_AR_REGEXP_DOESNT_MATCH);
            } else if ( ((M_callVariableTable->getVar(currentAction->getVarId())->isSet())) &&
                        (currentAction->getCheckItInverse() == true) )
            {
                // The inverse of the above
                WARNING("Regexp matched but should not: looking in '%s', with regexp '%s'",
                        haystack, currentAction->getRegularExpression());
                return(call::E_AR_REGEXP_SHOULDNT_MATCH);
      }
    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_VALUE) {
      double operand = get_rhs(currentAction);
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(operand);
    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_INDEX) {
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(msg_index);
    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_GETTIMEOFDAY) {
      struct timeval tv;
      gettimeofday(&tv, NULL);
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)tv.tv_sec);
      M_callVariableTable->getVar(currentAction->getSubVarId(0))->setDouble((double)tv.tv_usec);
    } else if (currentAction->getActionType() == CAction::E_AT_LOOKUP) {
      /* Create strings from the sending messages. */
      char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2));
      char *key = strdup(createSendingMessage(currentAction->getMessage(1), -2));

      if (inFiles.find(file) == inFiles.end()) {
	ERROR("Invalid injection file for insert: %s", file);
      }

      double value = inFiles[file]->lookup(key);

      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
      free(file);
      free(key);
    } else if (currentAction->getActionType() == CAction::E_AT_INSERT) {
      /* Create strings from the sending messages. */
      char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2));
      char *value = strdup(createSendingMessage(currentAction->getMessage(1), -2));

      if (inFiles.find(file) == inFiles.end()) {
	ERROR("Invalid injection file for insert: %s", file);
      }

      inFiles[file]->insert(value);

      free(file);
      free(value);
    } else if (currentAction->getActionType() == CAction::E_AT_REPLACE) {
      /* Create strings from the sending messages. */
      char *file = strdup(createSendingMessage(currentAction->getMessage(0), -2));
      char *line = strdup(createSendingMessage(currentAction->getMessage(1), -2));
      char *value = strdup(createSendingMessage(currentAction->getMessage(2), -2));

      if (inFiles.find(file) == inFiles.end()) {
	ERROR("Invalid injection file for replace: %s", file);
      }

      char *endptr;
      int lineNum = (int)strtod(line, &endptr);
      if (*endptr) {
	ERROR("Invalid line number for replace: %s", line);
      }

      inFiles[file]->replace(lineNum, value);

      free(file);
      free(line);
      free(value);
    } else if (currentAction->getActionType() == CAction::E_AT_CLOSE_CON) {
      if (call_socket) {
	sipp_socket_invalidate(call_socket);
	sipp_close_socket(call_socket);
	call_socket = NULL;
      }
    } else if (currentAction->getActionType() == CAction::E_AT_SET_DEST) {
      /* Change the destination for this call. */
      char *str_host = strdup(createSendingMessage(currentAction->getMessage(0), -2));
      char *str_port = strdup(createSendingMessage(currentAction->getMessage(1), -2));
      char *str_protocol = strdup(createSendingMessage(currentAction->getMessage(2), -2));

      char *endptr;
      int port = (int)strtod(str_port, &endptr);
      if (*endptr) {
	ERROR("Invalid port for setdest: %s", str_port);
      }

      int protocol;
      if (!strcmp(str_protocol, "udp") || !strcmp(str_protocol, "UDP")) {
	protocol = T_UDP;
      } else if (!strcmp(str_protocol, "tcp") || !strcmp(str_protocol, "TCP")) {
	protocol = T_TCP;
      } else if (!strcmp(str_protocol, "tls") || !strcmp(str_protocol, "TLS")) {
	protocol = T_TLS;
      } else if (!strcmp(str_protocol, "sctp") || !strcmp(str_protocol, "SCTP")) {
	protocol = T_SCTP;
      } else {
	ERROR("Unknown transport for setdest: '%s'", str_protocol);
      }

      if (!call_socket && ((protocol == T_TCP && transport == T_TCP) ||
                           (protocol == T_SCTP && transport == T_SCTP))) {
	bool existing;
	if ((associate_socket(new_sipp_call_socket(use_ipv6, transport, &existing))) == NULL) {
      switch (protocol) {
        case T_SCTP:
	      ERROR_NO("Unable to get a SCTP socket");
          break;
        default:
	      ERROR_NO("Unable to get a TCP socket");
      }
	}

	if (!existing) {
	  sipp_customize_socket(call_socket);
	}
      }


      if (protocol != call_socket->ss_transport) {
	  ERROR("Can not switch protocols during setdest.");
      }

      if (protocol == T_UDP) {
	/* Nothing to do. */
      } else if (protocol == T_TLS) {
	ERROR("Changing destinations is not supported for TLS.");
      } else if (protocol == T_TCP || protocol == T_SCTP) {
	if (!multisocket) {
	  ERROR("Changing destinations for TCP or SCTP requires multisocket mode.");
	}
	if (call_socket->ss_count > 1) {
	  ERROR("Can not change destinations for a TCP/SCTP socket that has more than one user.");
	}
      }

      struct addrinfo   hints;
      struct addrinfo * local_addr;
      memset((char*)&hints, 0, sizeof(hints));
      hints.ai_flags  = AI_PASSIVE;
      hints.ai_family = PF_UNSPEC;
      is_ipv6 = false;

      if (getaddrinfo(str_host, NULL, &hints, &local_addr) != 0) {
	ERROR("Unknown host '%s' for setdest", str_host);
      }
      if (_RCAST(struct sockaddr_storage *, local_addr->ai_addr)->ss_family != call_peer.ss_family) {
	ERROR("Can not switch between IPv4 and IPV6 using setdest!");
      }
      memcpy(&call_peer, local_addr->ai_addr, SOCK_ADDR_SIZE(_RCAST(struct sockaddr_storage *,local_addr->ai_addr)));
      if (call_peer.ss_family == AF_INET) {
	(_RCAST(struct sockaddr_in *,&call_peer))->sin_port = htons(port);
      } else {
	(_RCAST(struct sockaddr_in6 *,&call_peer))->sin6_port = htons(port);
      }
      memcpy(&call_socket->ss_dest, &call_peer, SOCK_ADDR_SIZE(_RCAST(struct sockaddr_storage *,&call_peer)));

      free(str_host);
      free(str_port);
      free(str_protocol);

      if (protocol == T_TCP || protocol == T_SCTP) {
	close(call_socket->ss_fd);
	call_socket->ss_fd = -1;
	call_socket->ss_changed_dest = true;
	if (sipp_reconnect_socket(call_socket)) {
	  if (reconnect_allowed()) {
	    if(errno == EINVAL){
	      /* This occurs sometime on HPUX but is not a true INVAL */
	      WARNING("Unable to connect a TCP/SCTP/TLS socket, remote peer error");
	    } else {
	      WARNING("Unable to connect a TCP/SCTP/TLS socket");
	    }
	    /* This connection failed.  We must be in multisocket mode, because
	     * otherwise we would already have a call_socket.  This call can not
	     * succeed, but does not affect any of our other calls. We do decrement
	     * the reconnection counter however. */
	    if (reset_number != -1) {
	      reset_number--;
	    }

	    return E_AR_CONNECT_FAILED;
	  } else {
	    if(errno == EINVAL){
	      /* This occurs sometime on HPUX but is not a true INVAL */
	      ERROR("Unable to connect a TCP/SCTP/TLS socket, remote peer error");
	    } else {
	      ERROR_NO("Unable to connect a TCP/SCTP/TLS socket");
	    }
	  }
	}
      }
    } 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);
    } else if (currentAction->getActionType() == CAction::E_AT_JUMP) {
      double operand = get_rhs(currentAction);
      msg_index = (int)operand - 1;
    } else if (currentAction->getActionType() == CAction::E_AT_PAUSE_RESTORE) {
      double operand = get_rhs(currentAction);
      paused_until = (int)operand;
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_ADD) {
      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
      double operand = get_rhs(currentAction);
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value + operand);
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_SUBTRACT) {
      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
      double operand = get_rhs(currentAction);
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value - operand);
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_MULTIPLY) {
      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
      double operand = get_rhs(currentAction);
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value * operand);
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_DIVIDE) {
      double value = M_callVariableTable->getVar(currentAction->getVarId())->getDouble();
      double operand = get_rhs(currentAction);
      if (operand == 0) {
	WARNING("Action failure: Can not divide by zero ($%d/$%d)!\n", currentAction->getVarId(), currentAction->getVarInId());
      } else {
	M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value / operand);
      }
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_TEST) {
      double value = currentAction->compare(M_callVariableTable);
      M_callVariableTable->getVar(currentAction->getVarId())->setBool(value);
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_STRCMP) {
      char *rhs = M_callVariableTable->getVar(currentAction->getVarInId())->getString();
      char *lhs;
      if (currentAction->getVarIn2Id()) {
	lhs = M_callVariableTable->getVar(currentAction->getVarIn2Id())->getString();
      } else {
	lhs = currentAction->getStringValue();
      }
      int value = strcmp(rhs, lhs);
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble((double)value);
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_TRIM) {
      CCallVariable *var = M_callVariableTable->getVar(currentAction->getVarId());
      char *in = var->getString();
      char *p = in;
      while (isspace(*p)) {
	p++;
      }
      char *q = strdup(p);
      var->setString(q);
      int l = strlen(q);
      for (int i = l - 1; i >= 0 & isspace(q[i]); i--) {
	q[i] = '\0';
      }
    } else if (currentAction->getActionType() == CAction::E_AT_VAR_TO_DOUBLE) {
      double value;

      if (M_callVariableTable->getVar(currentAction->getVarInId())->toDouble(&value)) {
	M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
      } else {
	WARNING("Invalid double conversion from $%d to $%d", currentAction->getVarInId(), currentAction->getVarId());
      }
    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_SAMPLE) {
      double value = currentAction->getDistribution()->sample();
      M_callVariableTable->getVar(currentAction->getVarId())->setDouble(value);
    } else if (currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_STRING) {
      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
      char *str = strdup(x);
      if (!str) {
	ERROR("Out of memory duplicating string for assignment!");
      }
      M_callVariableTable->getVar(currentAction->getVarId())->setString(str);
    } else if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) {
      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
      LOG_MSG("%s\n", x);
    } else if (currentAction->getActionType() == CAction::E_AT_LOG_WARNING) {
      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
      WARNING("%s", x);
    } else if (currentAction->getActionType() == CAction::E_AT_LOG_ERROR) {
      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
      ERROR("%s", x);
    } else if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) {
      char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
      // TRACE_MSG("Trying to execute [%s]", x);
      pid_t l_pid;
      switch(l_pid = fork())
      {
	case -1:
	  // error when forking !
	  ERROR_NO("Forking error main");
	  break;

	case 0:
	  // first child process - execute the command
	  if((l_pid = fork()) < 0) {
	    ERROR_NO("Forking error child");
	  } else {
	    if( l_pid == 0){
	      int ret;
	      ret = system(x); // second child runs
	      if(ret == -1) {
		WARNING("system call error for %s",x);
	      }
	    }
	    exit(EXIT_OTHER); 
	  }
	  break;
	default:
	  // parent process continue
	  // reap first child immediately
	  pid_t ret;
	  while ((ret=waitpid(l_pid, NULL, 0)) != l_pid) {
	    if (ret != -1) {
	      ERROR("waitpid returns %1d for child %1d",ret,l_pid);
	    }
	  }
	  break;
      }
    } else if (currentAction->getActionType() == CAction::E_AT_EXEC_INTCMD) {
      switch (currentAction->getIntCmd())
      {
	case CAction::E_INTCMD_STOP_ALL:
	  quitting = 1;
	  break;
	case CAction::E_INTCMD_STOP_NOW:
	  screen_exit(EXIT_TEST_RES_INTERNAL);
	  break;
	case CAction::E_INTCMD_STOPCALL:
	default:
	  return(call::E_AR_STOP_CALL);
	  break;
      }
#ifdef PCAPPLAY
    } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) ||
	(currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) {
      play_args_t *play_args;
      if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) {
	play_args = &(this->play_args_a);
      } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) {
	play_args = &(this->play_args_v);
      }
      play_args->pcap = currentAction->getPcapPkts();
      /* port number is set in [auto_]media_port interpolation */
      if (media_ip_is_ipv6) {
	struct sockaddr_in6 *from = (struct sockaddr_in6 *)(void *) &(play_args->from);
	from->sin6_family = AF_INET6;
	inet_pton(AF_INET6, media_ip, &(from->sin6_addr));
      }
      else {
	struct sockaddr_in *from = (struct sockaddr_in *)(void *) &(play_args->from);
	from->sin_family = AF_INET;
	from->sin_addr.s_addr = inet_addr(media_ip);
      }
      /* Create a thread to send RTP packets */
      pthread_attr_t attr;
      pthread_attr_init(&attr);
#ifndef PTHREAD_STACK_MIN
#define PTHREAD_STACK_MIN	16384
#endif
      //pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
      if (media_thread != 0) {
        // If a media_thread is already active, kill it before starting a new one
        pthread_cancel(media_thread);
        pthread_join(media_thread, NULL);
        media_thread = 0;
      }
      int ret = pthread_create(&media_thread, &attr, send_wrapper,
	  (void *) play_args);
      if(ret)
	ERROR("Can create thread to send RTP packets");
      pthread_attr_destroy(&attr);
#endif
    } else {
      ERROR("call::executeAction unknown action");
    }
  } // end for
  return(call::E_AR_NO_ERROR);
}

void call::extractSubMessage(char * msg, char * matchingString, char* result, bool case_indep, int occurrence, bool headers) {

 char *ptr, *ptr1;
  int sizeOf;
  int i = 0;
 int len = strlen(matchingString);
 char mat1 = tolower(*matchingString);
 char mat2 = toupper(*matchingString);

 ptr = msg;
 while (*ptr) { 
   if (!case_indep) {
     ptr = strstr(ptr, matchingString);
     if (ptr == NULL) break;
     if (headers == true && ptr != msg && *(ptr-1) != '\n') {
       ++ptr;
       continue; 
     }
   } else {
     if (headers) {
       if (ptr != msg) {
         ptr = strchr(ptr, '\n');
         if (ptr == NULL) break;
         ++ptr;
         if (*ptr == 0) break;
       }
     } else {
       ptr1 = strchr(ptr, mat1);
       ptr = strchr(ptr, mat2);
       if (ptr == NULL) {
         if (ptr1 == NULL) break;
         ptr = ptr1;
       } else {
         if (ptr1 != NULL && ptr1 < ptr) ptr = ptr1; 
       }
     }
     if (strncasecmp(ptr, matchingString, len) != 0) {
       ++ptr;
       continue;
     }
   }
   // here with ptr pointing to a matching string
   if (occurrence <= 1) break; 
   --occurrence;
   ++ptr;
 }

 if(ptr != NULL && *ptr != 0) {
   strncpy(result, ptr+len, MAX_SUB_MESSAGE_LENGTH);
    sizeOf = strlen(result);
    if(sizeOf >= MAX_SUB_MESSAGE_LENGTH)  
      sizeOf = MAX_SUB_MESSAGE_LENGTH-1;
    while((i<sizeOf) && (result[i] != '\n') && (result[i] != '\r'))
      i++;
    result[i] = '\0';
  } else {
    result[0] = '\0';
  }
}

void call::getFieldFromInputFile(const char *fileName, int field, SendingMessage *lineMsg, char*& dest)
{
  if (inFiles.find(fileName) == inFiles.end()) {
    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);
}

call::T_AutoMode call::checkAutomaticResponseMode(char * P_recv) {
  if (strcmp(P_recv, "BYE")==0) {
    return E_AM_UNEXP_BYE;
  } else if (strcmp(P_recv, "CANCEL") == 0) {
    return E_AM_UNEXP_CANCEL;
  } else if (strcmp(P_recv, "PING") == 0) {
    return E_AM_PING;
  } else if (((strcmp(P_recv, "INFO") == 0) || (strcmp(P_recv, "NOTIFY") == 0) || (strcmp(P_recv, "UPDATE") == 0)) 
               && (auto_answer == true)){
    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 ;
  char * old_last_recv_msg = NULL;
  bool last_recv_msg_saved = false;

  switch (P_case) {
  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
    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(get_default_message("3pcc_abort"), -1));
    }
    
    computeStat(CStat::E_CALL_FAILED);
    computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
    delete this;
    } else {
      WARNING("Continuing call on unexpected CANCEL for call: %s", (id==NULL)?"none":id);
    }
    break ;
      
  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_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.
    // if twin socket call => reset the other part here 
    if (twinSippSocket && (msg_index > 0)) {
      res = sendCmdBuffer(createSendingMessage(get_default_message("3pcc_abort"), -1));
    }
    
    CStat::globalStat(CStat::E_AUTO_ANSWERED);
    delete this;
    } else {
      WARNING("Do not answer on an unexpected PING for call: %s", (id==NULL)?"none":id);
    }
    break ;

  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
    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 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) {
      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::globalStat(CStat::E_AUTO_ANSWERED);
    return true;
    break;

    default:
    ERROR("Internal error for automaticResponseMode - mode %d is not implemented!", P_case);
    break ;
  }

  return false;
}

#ifdef PCAPPLAY
void *send_wrapper(void *arg)
{
  play_args_t *s = (play_args_t *) arg;
  //struct sched_param param;
  //int ret;
  //param.sched_priority = 10;
  //ret = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
  //if(ret)
  //  ERROR("Can't set RTP play thread realtime parameters");
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
  send_packets(s);
  pthread_exit(NULL);
  return NULL;
}
#endif
