/*
 *  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
 */

#include <iterator>
#include <algorithm>
#include <fstream>
#include <iostream>
#include "sipp.hpp"

#define KEYWORD_SIZE 64

call_map calls;

/************** Call map and management routines **************/

call_map * get_calls()
{
  return & calls;
}

static unsigned int next_number = 1;

call * add_call(char * call_id, bool ipv6)
{
  call * new_call;

  new_call = new call(call_id, ipv6);

  if(!new_call) {
    ERROR("Memory Overflow");
  }

  calls[std::string(call_id)] = new_call;

  if(!next_number) { next_number ++; }
  new_call -> number = next_number;

  /* Vital counters update */
  next_number++;
  open_calls++;

  /* Statistics update */
  calls_since_last_rate_change++;
  total_calls ++;

  if(open_calls > open_calls_peak) { 
    open_calls_peak = open_calls;
    open_calls_peak_time = clock_tick / 1000;
  }
  return new_call;
}

call * add_call(bool ipv6)
{
  static char call_id[MAX_HEADER_LEN];
  
  call * new_call;
  
  if(!next_number) { next_number ++; }
  
  sprintf(call_id, 
	  "%u-%u@%s",
          next_number, pid, local_ip);
  
  return add_call(call_id, ipv6);
}

call * get_call(char * call_id)
{
  call * call_ptr;
  
  call_ptr = calls[std::string(call_id)];

  if(!call_ptr) {
    calls.erase(std::string(call_id));
  }
  
  return call_ptr;
}

void delete_call(char * call_id)
{
  call * call_ptr = calls[std::string(call_id)];

  if(!calls.erase(std::string(call_id))) {
    ERROR("Call not erased from map");
  } 

  if(call_ptr) {
    delete call_ptr;
    open_calls--;
  } else {
    ERROR("Call not found");
  }
}

/******* Very simple hash for retransmission detection  *******/

unsigned long hash(char * msg) {
  unsigned long rv = 0;
  int len = strlen(msg);
  int index = 0;
  
  if (len > 4) {
    rv  = msg[0] + msg[1] + msg[2] + msg[3];
  }
  
  if (len > 12) {
    rv += msg[9] + msg[10] + msg[11] + msg[12];
  }

  rv <<= 8;
  rv += len;
  rv <<= 16;
  
  for (index = 0; index < len; index ++) {
    rv += + msg[index] * index;
  }
  
  return rv;
}

/******************* Call class implementation ****************/

call::InputFileUsage call::m_usage   = call::InputFileSequentialOrder;
int                  call::m_counter = 0;

call::call(char * p_id, bool ipv6) : use_ipv6(ipv6)
{
  memset(this, 0, sizeof(call));
  id = strdup(p_id);
  start_time = clock_tick;
  call_established=false ;
  count_in_stats=true ;
  ack_is_pending=false ;
  last_recv_msg = NULL;
  
  call_remote_socket = 0;
  
  // initialising the CallVariable with the Scenario variable
  for(int i=0; i<SCEN_VARIABLE_SIZE; i++) 
    {
      if(scenVariableTable[i] != NULL) {
        M_callVariableTable[i] = new CCallVariable();
        if (M_callVariableTable[i] == NULL) {
          ERROR ("call variable allocation failed");
        }
      } else {
        M_callVariableTable[i] = NULL;
      }
    }

  // If not updated by a message we use the start time 
  // information to compute rtd information
  start_time_rtd  = clock_tick; 

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

  if (InputFileRandomOrder == m_usage) {
      m_localLineNumber = rand() % numLinesInFile;
  } else {
      m_localLineNumber = m_counter++;
      if (m_counter >= numLinesInFile) {
          m_counter = 0;
      }

  }

  peer_tag[0] = '\0';
}

call::~call()
{
  deleted += 1;
  
  if(comp_state) { comp_free(&comp_state); }

  if(count_in_stats) {
    CStat::instance()->computeStat(CStat::E_ADD_CALL_DURATION, 
                                   clock_tick - start_time);
  }

  call_duration_sum += clock_tick - start_time;
  call_duration_nb++;
  
  if(pollset_index) {
    pollset_remove(pollset_index);
    shutdown(call_socket, SHUT_RDWR);
    close(call_socket);
  }

  /* Deletion of the call variable */ 
  for(int i=0; i<SCEN_VARIABLE_SIZE; i++) {
    if(M_callVariableTable[i] != NULL) {
      delete M_callVariableTable[i] ;
      M_callVariableTable[i] = NULL;
    }
  }

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

  if(dialog_route_set) {
       free(dialog_route_set);
  }

#ifdef _USE_OPENSSL
  if(dialog_authentication) {
       free(dialog_authentication);
  }
#endif
  call_established= false ;
}
 
void call::connect_socket_if_needed()
{
	 
  if(call_socket) return;
  if(!multisocket) return;

  if(transport == T_UDP) {
    struct sockaddr_storage saddr;
    sipp_socklen_t len;
    
    if(toolMode != MODE_CLIENT) return;

    if((call_socket = socket(use_ipv6 ? AF_INET6 : AF_INET,
                             SOCK_DGRAM,
                             0)) == -1) {
      ERROR_NO("Unable to get an UDP socket");
    }
    
    memset(&saddr, 0, sizeof(struct sockaddr_storage));

    saddr.ss_family       = AF_INET;
    
    if(bind(call_socket,
            (sockaddr *)(void *)&saddr,
            use_ipv6 ? sizeof(struct sockaddr_in6) :
                       sizeof(struct sockaddr_in))) {
      ERROR_NO("Unable to bind UDP socket");
    }
    
    if (use_ipv6) {
      len = sizeof(struct sockaddr_in6);
    } else {
      len = sizeof(struct sockaddr_in);
    }

    getsockname(call_socket, 
                (sockaddr *)(void *)&saddr,
                &len);

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

  } else { /* TCP */

    if((call_socket= socket(use_ipv6 ? AF_INET6 : AF_INET,
                            SOCK_STREAM,
                            0))== -1) {
      ERROR_NO("Unable to get a TCP socket");
    }
    
    sipp_customize_socket(call_socket);

    /*
    struct sockaddr_storage *L_dest = &remote_sockaddr;

    if (use_remote_sending_addr) {
        L_dest = &remote_sending_sockaddr ;
     }
    */
              //  (struct sockaddr *)(void *)&remote_sockaddr,
              //  (struct sockaddr *)(void *)L_dest,
               // use_ipv6 ? sizeof(struct sockaddr_in6) :
                //           sizeof(struct sockaddr_in))) {
		
    if(connect(call_socket,
               (struct sockaddr *)(void *)&remote_sockaddr,
	        SOCK_ADDR_SIZE(&remote_sockaddr))) {
      
      if(errno == EINVAL){
        /* This occurs sometime on HPUX but is not a true INVAL */
        ERROR("Unable to connect a TCP socket, remote peer error");
      } else {
        ERROR_NO("Unable to connect a TCP socket");
      }
    }
  }

  /* Asks to receive incoming messages */
  pollset_index = pollset_add(this, call_socket);
}

bool lost(int percent)
{
  static int inited = 0;

  if(!lose_packets) return false;
  if(!percent) return false;

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

  if((rand() % 100) < percent) {
    return true;
  } else {
    return false;
  }
}

int call::send_raw(char * msg, int index) 
{
  void ** state;
  int sock;
  int rc;
#ifdef _USE_OPENSSL
  SSL *ssl;
  extern SSL *ssl_list[];
#endif
  struct timeval currentTime;
  GET_TIME (&currentTime);
  TRACE_MSG((s, "----------------------------------------------- %s\n"
             "%s message sent:\n\n%s\n",
             CStat::instance()->formatTime(&currentTime),
             TRANSPORT_TO_STRING(transport),
             msg));
  
  if(lost(scenario[index] -> lost)) {
    TRACE_MSG((s, "%s message voluntary lost (while sending).", TRANSPORT_TO_STRING(transport)));
    
    if(comp_state) { comp_free(&comp_state); }
    scenario[index] -> nb_lost++;
    return 0;
  }
  
  if(call_socket) {
    state = &comp_state;
    sock = call_socket;

    if (use_remote_sending_addr) {
      if (!call_remote_socket) {
        struct sockaddr_storage *L_dest = &remote_sending_sockaddr;
        if((call_remote_socket= socket(use_ipv6 ? AF_INET6 : AF_INET,
                            SOCK_STREAM,
                            0))== -1) {
          ERROR_NO("Unable to get a TCP socket");
        }
    
        sipp_customize_socket(call_remote_socket);

        if(connect(call_remote_socket,
               (struct sockaddr *)(void *)L_dest,
	        SOCK_ADDR_SIZE(&remote_sockaddr))) {
          if(errno == EINVAL){
            /* This occurs sometime on HPUX but is not a true INVAL */
            ERROR("Unable to connect a TCP socket, remote peer error");
          } else {
            ERROR_NO("Unable to connect a TCP socket");
          }
        }
      }
      sock=call_remote_socket ;
    }

#ifdef _USE_OPENSSL
    ssl  = ssl_list[sock];
#endif
  } else {
    state = &monosocket_comp_state;
    if(transport == T_UDP) {
      sock = main_socket;
    } else {
      sock = tcp_multiplex;
#ifdef _USE_OPENSSL
      ssl = ssl_tcp_multiplex;
#endif
    }
  }

#ifdef _USE_OPENSSL
  if ( transport == T_TLS ) {
    rc = send_message_tls(ssl, state, msg);
  } else {
#endif
  rc = send_message(sock, state, msg);
#ifdef _USE_OPENSSL
  }
#endif

  if(rc == -1) return -1;

  if(rc < -1) {
    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
    CStat::instance()->computeStat(CStat::E_FAILED_CANNOT_SEND_MSG);
    delete_call(id);
  }
  
  return rc; /* OK */
}

int call::sendBuffer(char * msg) 
{
  void ** state;
  int sock;
  int rc;

  TRACE_MSG((s, "-----------------------------------------------\n"
             "%s message send:\n\n%s\n",
             TRANSPORT_TO_STRING(transport),
             msg));
  
  if(call_socket) {
    state = &comp_state;
    sock = call_socket;
  } else {
    state = &monosocket_comp_state;
    if(transport == T_UDP) {
      sock = main_socket;
    } else {
      sock = tcp_multiplex;
    }
  }

  rc = send_message(sock, state, msg);

  if(rc == -1) return -1;

  if(rc < -1) {
    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
    CStat::instance()->computeStat(CStat::E_FAILED_CANNOT_SEND_MSG);
    delete_call(id);
  }
  
  return rc; /* OK */
}


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

  if(dest = strstr(src, "CSeq")) {
    /* If we find a CSeq in incoming msg */
    char * last_header = get_last_header(dest+4);
    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;
  } else {
    return NULL;
  }
}

char * call::get_last_header(char * name)
{
  static char last_header[MAX_HEADER_LEN * 10];
  char * src, *dest, *ptr;
  char src_tmp[1024] = "\n";

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

  src = last_recv_msg;
  dest = last_header;

  strcpy(src_tmp+1,name);
  while(src = strcasestr2(src, src_tmp)) {
    src++;
    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 \r\n when several Via header are present (AgM) 
    if (dest != last_header) {
      dest += sprintf(dest, "\r\n");
    }
    dest += sprintf(dest, "%s", src);
    if(ptr) { *ptr = '\n'; }
    
    src++;
  }
  
  if(dest == last_header) {
    return NULL;
  }

  *(dest--) = 0;

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

  /* 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 last_header;
}

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

  /* 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;
  
  /* for safety's sake */
  if (NULL == name || NULL == strrchr(name, ':')) {
      return last_header;
  }

  while(src = strstr(src, name)) {

      /* just want the header's content */
      src += strlen(name);

    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) {
      dest += sprintf(dest, ",");
    }
    dest += sprintf(dest, "%s", src);
    if(ptr) { *ptr = '\n'; }
    
    src++;
  }
  
  if(dest == last_header) {
    return last_header;
  }

  *(dest--) = 0;

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

  /* remove enclosed CRs in multilines */
  while(ptr = strchr(last_header, '\r')) {
    /* Use strlen(ptr) to include trailing zero */
    memmove(ptr, ptr+1, strlen(ptr));
  }

  return last_header;
}

char * call::send_scene(int index, int *send_status)
{
  static char msg_buffer[SIPP_MAX_MSG_SIZE];

#define MAX_MSG_NAME_SIZE 30
  static char msg_name[MAX_MSG_NAME_SIZE];
  char *L_ptr1 ;
  char *L_ptr2 ;

  /* Socket port must be known before string substitution */
  connect_socket_if_needed();
  
  if(scenario[index] -> send_scheme) {
    char * dest;
    dest = createSendingMessage(scenario[index] -> send_scheme, index);
    strcpy(msg_buffer, dest);

    if (dest) {
      L_ptr1=msg_name ;
      L_ptr2=msg_buffer ;
      while ((*L_ptr2 != ' ') && (*L_ptr2 != '\n') && (*L_ptr2 != '\t'))  {
        *L_ptr1 = *L_ptr2;
        L_ptr1 ++;
        L_ptr2 ++;
      }
      *L_ptr1 = '\0' ;
    }

    if (strcmp(msg_name,"ACK") == 0) {
      call_established = true ;
      ack_is_pending = false ;
    }

    if(send_status) {
      *send_status = 
        send_raw(msg_buffer, index);
    } else {
      send_raw(msg_buffer, index);
    }
  } else {
    ERROR("Unsupported 'send' message in scenario");
  }

  return msg_buffer;
}

bool call::next()
{
  int test = scenario[msg_index]->test;
  /* What is the next message index? */
  if ( scenario[msg_index]->next && 
       ((test == -1) ||
        (test < SCEN_VARIABLE_SIZE && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet()))
     ) {
    /* For branching, use the 'next' attribute value */
         msg_index = labelArray[scenario[msg_index]->next];
  } else {
    /* Without branching, use the next message */
  msg_index++;
  }
  if(msg_index >= scenario_len) {
    // Call end -> was it successful?
    if(call::last_action_result != call::E_AR_NO_ERROR) {
      switch(call::last_action_result) {
        case call::E_AR_REGEXP_DOESNT_MATCH:
          CStat::instance()->computeStat(CStat::E_CALL_FAILED);
          CStat::instance()->computeStat(CStat::E_FAILED_REGEXP_DOESNT_MATCH);
          break;
        case call::E_AR_HDR_NOT_FOUND:
          CStat::instance()->computeStat(CStat::E_CALL_FAILED);
          CStat::instance()->computeStat(CStat::E_FAILED_REGEXP_HDR_NOT_FOUND);
          break;
      }
    } else {
      CStat::instance()->computeStat(CStat::E_CALL_SUCCESSFULLY_ENDED);
    }
    delete_call(id);
    return false;
  }

  return run();
}

bool call::run()
{
  if(msg_index >= scenario_len) {
    ERROR_P3("Scenario overrun for call %s (%08x) (index = %d)\n", 
             id, this, msg_index);
  }

  /* Manages retransmissions or delete if max retrans reached */
  if(next_retrans && (next_retrans < clock_tick)) {
    nb_retrans++;
    
    if(nb_retrans > UDP_MAX_RETRANS) {
      scenario[last_send_index] -> nb_timeout ++;
      CStat::instance()->computeStat(CStat::E_CALL_FAILED);
      CStat::instance()->computeStat(CStat::E_FAILED_MAX_UDP_RETRANS);
      if (default_behavior) {
        // Abort the call by sending proper SIP message
        WARNING_P1("Aborting call on UDP retransmission timeout for Call-ID '%s'", id);
        return(abortCall());
      } else {
        // Just delete existing call
      delete_call(id);
      return false;
      }
    } else {
      unsigned int delay = scenario[last_send_index] -> retrans_delay;
      unsigned int pow = nb_retrans;
      while(pow--) {
        delay *= 2;
      }
      if(send_raw(last_send_msg, last_send_index) < -1) {
        return false;
      }
      scenario[last_send_index] -> nb_sent_retrans++;
      next_retrans = clock_tick + delay;
    }
  }

  if(paused_until) {
    /* Process a pending pause instruction until delay expiration */
    if(paused_until > clock_tick) {
      return true;
    } else {
      paused_until = 0;
      return next();
    }
  } else if(scenario[msg_index] -> pause) {
    /* Starts a pause instruction */
    if((scenario[msg_index] -> pause) &&
       ((scenario[msg_index] -> pause) == -1)) {
      paused_until = clock_tick + duration;
    } else {
      paused_until = clock_tick + scenario[msg_index] -> pause;
    }
    return run(); /* In case delay is 0 */
    
  } 
#ifdef __3PCC__
  else if(scenario[msg_index] -> M_type == MSG_TYPE_SENDCMD) {
    int send_status;

    if(next_retrans) {
      return true;
    }

    send_status = sendCmdMessage(msg_index);

    if(send_status != 0) { /* Send error */
      return false; /* call deleted */
    }
    scenario[msg_index] -> M_nbCmdSent++;
    next_retrans = 0;
    return(next());
  }
#endif
  else if(scenario[msg_index] -> send_scheme) {

    char * msg_snd;
    int send_status;

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

    if(next_retrans) {
      return true;
    }

    /* If this message can be used to compute RTD, do it now */
    if(!rtd_done) {
      if(scenario[msg_index] -> start_rtd) {
        start_time_rtd = clock_tick;
      }
  
      if(scenario[msg_index] -> stop_rtd) {
        rtd_sum += clock_tick - start_time_rtd;
        CStat::instance()->computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION,
                                           clock_tick - start_time_rtd);
        rtd_nb ++;
        rtd_done = true;
      }
    }
  
    msg_snd = send_scene(msg_index, &send_status);

    if(send_status == -1) { /* Would Block on TCP */
      return true; /* No step, nothing done, retry later */
    }

    if(send_status <-1) { /* Send error */
      return false; /* call deleted */
    }
    
    last_send_index = msg_index;
    last_send_msg = (char *) realloc(last_send_msg, strlen(msg_snd) + 1);
    strcpy(last_send_msg, msg_snd);

    if(last_recv_hash) {
      /* We are sending just after msg reception. There is a great
       * chance that we will be asked to retransmit this message */
      recv_retrans_hash       = last_recv_hash;
      recv_retrans_recv_index = last_recv_index;
      recv_retrans_send_index = msg_index;
    
      /* Prevent from detecting the cause relation between send and recv 
       * in the next valid send */
      last_recv_hash = 0;
    }

    /* Update retransmission information */
    if(scenario[msg_index] -> retrans_delay) {
      if((transport == T_UDP) && (retrans_enabled)) {
        next_retrans = clock_tick + scenario[msg_index] -> retrans_delay;
        nb_retrans = 0;
      }
    } else {
      next_retrans = 0;
    }
    
    /* Update scenario statistics */
    scenario[msg_index] -> nb_sent++;

    return next();
  }
  return true;
}

bool call::process_unexpected(char * msg)
{
  int search_index;
  static int first = 1;
  int res ;
  
  scenario[msg_index] -> nb_unexp++;
  
  if (scenario[msg_index] -> recv_request) {
    if (default_behavior) {
      WARNING_P3("Aborting call on unexpected message for Call-ID '%s': while expecting '%s', received '%s' ",
                id, scenario[msg_index] -> recv_request, msg);
  } else {
      WARNING_P3("Continuing call on unexpected message for Call-ID '%s': while expecting '%s', received '%s' ",
                  id, scenario[msg_index] -> recv_request, msg);
    }
  } else {
    if (default_behavior) {
      WARNING_P3("Aborting call on unexpected message for Call-ID '%s': while expecting '%d' response, received '%s' ", 
                  id, scenario[msg_index] -> recv_response, msg);
    } else {
      WARNING_P3("Continuing call on unexpected message for Call-ID '%s': while expecting '%d' response, received '%s' ", 
                id, scenario[msg_index] -> recv_response, msg);
  }
  }
  
  TRACE_MSG((s, "-----------------------------------------------\n"
             "Unexpected %s message received:\n\n%s\n",
             TRANSPORT_TO_STRING(transport),
             msg));
  
  if (default_behavior) {
#ifdef __3PCC__
  // if twin socket call => reset the other part here 
  if (twinSippSocket && (msg_index > 0)) {
    //WARNING_P2("call-ID '%s', internal-cmd: abort_call %s",id, "");
    res = sendCmdBuffer
      (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n", -1));
  }
#endif /* __3PCC__ */

  // usage of last_ keywords => for call aborting
  last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1);
  strcpy(last_recv_msg, msg);

    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
    CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
  return (abortCall());
  } else {
    // Do not abort call nor send anything in reply if default behavior is disabled
    return true;
  }
}

bool call::abortCall()
{
  int res ;

  if ((toolMode != MODE_SERVER) && (msg_index > 0)) {
    if (call_established == false) {
      char * src = last_recv_msg ;
      char * dest   ;
      char   L_msg_buffer[SIPP_MAX_MSG_SIZE];
      L_msg_buffer[0] = '\0';
      char * L_param = L_msg_buffer;

      // Answer unexpected errors (4XX, 5XX and beyond) with an ACK 
      // Contributed by F. Tarek Rogers
      if((src) && (get_reply_code(src) > 400)) {
       sendBuffer(createSendingMessage(
         (char*)"ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0\n"
           "Via: SIP/2.0/[transport] [local_ip]:[local_port]\n"
           "From: sipp <sip:sipp@[local_ip]:[local_port]>;tag=[call_number]\n"
           "To: sut <sip:[service]@[remote_ip]:[remote_port]>[peer_tag_param]\n"
           "Call-ID: [call_id]\n"
           "CSeq: 1 ACK\n"
           "Contact: sip:sipp@[local_ip]:[local_port]\n"
           "Max-Forwards: 70\n"
           "Subject: Performance Test\n"
           "Content-Length: 0\n\n"
         , -1));
      } else if (src) {
        /* 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 */
          strcpy(L_param, "ACK sip:[service]@[remote_ip]:[remote_port] SIP/2.0\n");
          sprintf(L_param, "%s%s", L_param, "[last_Via:]\n");
          sprintf(L_param, "%s%s", L_param, "[last_From:]\n");
          sprintf(L_param, "%s%s", L_param, "[last_To:]\n");
          sprintf(L_param, "%s%s",  L_param, "[last_Call-ID:]\n");
          /* The CSeq of an ACK relating to an INVITE must be the same as  */
          /* the one from the INVITE.                                      */
          /* Let's simplify this by putting 1 (no support for re-invite in */
          /* 3PCC?)                                                        */
          /* FIXME: store CSeq from last INVITE and re-use it              */
          sprintf(L_param, "%sCSeq: 1 ACK\n", L_param);
          sprintf(L_param, "%s%s", L_param, "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
          sprintf(L_param, "%s%s", L_param,  "Content-Length: 0\n");
          res = sendBuffer(createSendingMessage((char*)(L_param),-1));
          
          /* Send the BYE */
          strcpy(L_param, "BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0\n");
          sprintf(L_param, "%s%s", L_param, "[last_Via:]\n");
          sprintf(L_param, "%s%s", L_param, "[last_From:]\n");
          sprintf(L_param, "%s%s", L_param, "[last_To:]\n");
          sprintf(L_param, "%s%s",  L_param, "[last_Call-ID:]\n");
          char * cseq;
          cseq = compute_cseq(src);
          if (cseq != NULL) {
            sprintf(L_param, "%s%s BYE\n", L_param, compute_cseq(src));
          }
          sprintf(L_param, "%s%s", L_param, "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
          sprintf(L_param, "%s%s", L_param,  "Content-Length: 0\n");
          res = sendBuffer(createSendingMessage((char*)(L_param),-1));
        } else {
          /* Send a CANCEL */
          strcpy(L_param, "CANCEL sip:[service]@[remote_ip]:[remote_port] SIP/2.0\n");
          sprintf(L_param, "%s%s", L_param, "[last_Via:]\n");
          sprintf(L_param, "%s%s", L_param, "[last_From:]\n");
          sprintf(L_param, "%s%s", L_param, "[last_To:]\n");
          sprintf(L_param, "%s%s",  L_param, "[last_Call-ID:]\n");
          char * cseq;
          cseq = compute_cseq(src);
          if (cseq != NULL) {
            sprintf(L_param, "%s%s CANCEL\n", L_param, compute_cseq(src));
          }
          sprintf(L_param, "%s%s", L_param, "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
          sprintf(L_param, "%s%s", L_param,  "Content-Length: 0\n");
          res = sendBuffer(createSendingMessage((char*)(L_param),-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 {
      /* Call is established */
      char * src = last_recv_msg ;
      char   L_msg_buffer[SIPP_MAX_MSG_SIZE];
      L_msg_buffer[0] = '\0';
      char * L_param = L_msg_buffer;
      strcpy(L_param, "BYE sip:[service]@[remote_ip]:[remote_port] SIP/2.0\n");
      sprintf(L_param, "%s%s", L_param, "[last_Via:]\n");
      sprintf(L_param, "%s%s", L_param, "[last_From:]\n");
      sprintf(L_param, "%s%s", L_param, "[last_To:]\n");
      sprintf(L_param, "%s%s",  L_param, "[last_Call-ID:]\n");
      char * cseq;
      cseq = compute_cseq(src);
      if (cseq != NULL) {
        sprintf(L_param, "%s%s BYE\n", L_param, compute_cseq(src));
      }
      sprintf(L_param, "%s%s", L_param, "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n");
      sprintf(L_param, "%s%s", L_param,  "Content-Length: 0\n");
      res = sendBuffer(createSendingMessage((char*)(L_param),-1));
    }
  }

  delete_call(id);
  return false;
}

bool call::rejectCall()
{
  CStat::instance()->computeStat(CStat::E_CALL_FAILED);
  CStat::instance()->computeStat(CStat::E_FAILED_CALL_REJECTED);
  delete_call(id);
  return false;
}


#ifdef __3PCC__
int call::sendCmdMessage(int index)
{
  char * dest;
  char delimitor[2];
  delimitor[0]=27;
  delimitor[1]=0;

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

    int rc;

    rc = send(twinSippSocket, 
              dest, 
              strlen(dest), 
              0);
    if(rc <  0) {
      CStat::instance()->computeStat(CStat::E_CALL_FAILED);
      CStat::instance()->computeStat(CStat::E_FAILED_CMD_NOT_SENT);
      delete_call(id);
      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 = send(twinSippSocket, 
            dest, 
            strlen(dest), 
            0);
  if(rc <  0) {
    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
    CStat::instance()->computeStat(CStat::E_FAILED_CMD_NOT_SENT);
    delete_call(id);
    return(-1);
  }

  return(0);
}

#endif

char* call::createSendingMessage(char * src, int P_index)
{
  static char msg_buffer[SIPP_MAX_MSG_SIZE+2];

  if(src != NULL) {
    char * dest = msg_buffer;
    char * key;
    char * length_marker = NULL;
    int    offset = 0;


    while(*src) {
      if(*src == '[') {
        char keyword [KEYWORD_SIZE+1];
        src++;
        key = strchr(src, ']');
        if((!key) || ((key - src) > KEYWORD_SIZE) || (!(key - src))){
          ERROR("Syntax error or invalid [keyword] in scenario");
        }
        memcpy(keyword, src,  key - src);
 
        keyword[key - src] = 0;
        src = key + 1;
        // allow +n for numeric variables
        if ((key = strchr(keyword,'+'))) {
           offset = atoi(key);
           *key = 0;
        }

        if(!strcmp(keyword, "remote_ip")) {
          dest += sprintf(dest, "%s", remote_ip_escaped);
        } else if(!strcmp(keyword, "remote_port")) {
          dest += sprintf(dest, "%u", remote_port + offset);
        } else if(!strcmp(keyword, "transport")) {
          dest += sprintf(dest, "%s", TRANSPORT_TO_STRING(transport));
        } else if(!strcmp(keyword, "local_ip")) {
          dest += sprintf(dest, "%s", local_ip_escaped);
        } else if(!strcmp(keyword, "local_port")) {
          if((transport == T_UDP) && (multisocket)) {
            dest += sprintf(dest, "%u", call_port + offset);
          } else {
            dest += sprintf(dest, "%u", local_port + offset);
          }
        } else if(!strcmp(keyword, "media_ip")) {
          dest += sprintf(dest, "%s", media_ip_escaped);
        } else if(!strcmp(keyword, "media_port")) {
          dest += sprintf(dest, "%u", media_port + offset);
        } else if(!strcmp(keyword, "call_number")) {
          dest += sprintf(dest, "%lu", number);
        } else if(!strcmp(keyword, "call_id")) {
          dest += sprintf(dest, "%s", id);
        } else if(!strcmp(keyword, "pid")) {
          dest += sprintf(dest, "%u", pid);
        } else if(!strcmp(keyword, "service")) {
          dest += sprintf(dest, "%s", service);
        } else if(!strncmp(keyword, "field", 5)) {
            char* local_dest = dest;
            getFieldFromInputFile(keyword, m_localLineNumber, dest);
            if (dest == local_dest && ('\r' == *(local_dest-1) || '\n' == *(local_dest-1))) {
                /* If the line begins with a field value and there
                 * is nothing to add for this field, 
                 * Jump to the end of line in scenario. SN 
                 */
                while((*src) && (*src != '\n')) {
                    src++;
                }
                if(*src == '\n') {
                    src++;
                }
            }
        } else if(!strcmp(keyword, "peer_tag_param")) {
          if(peer_tag && strlen(peer_tag)) {
            dest += sprintf(dest, ";tag=%s", peer_tag);
          }
        } else if(strstr(keyword, "$")) {
          int varId = atoi(keyword+1);
          if(varId < SCEN_VARIABLE_SIZE) {
            if(M_callVariableTable[varId] != NULL) {
              if(M_callVariableTable[varId]->isSet()) {
                dest += sprintf(dest, "%s",
                                M_callVariableTable[varId]->
                                getMatchingValue());
                // WARNING_P1("VARIABLE --%s--", M_callVariableTable[varId]->getMatchingValue());
              } else {
                dest += sprintf(dest, "%s", "");
              }
            }
          }
        } else if(strstr(keyword, "last_")) {
          char * last_header = get_last_header(keyword+5);
          if(last_header) {
            dest += sprintf(dest, "%s", last_header);
          } else {
            /* Jump to the end of line in scenario if nothing
             * to insert in place of this header. */
            while((*src) && (*src != '\n')) {
              src++;
            }
            if(*src == '\n') {
              src++;
            }
          }
        } else if(strstr(keyword, "routes")) {
            if (dialog_route_set) {
                dest += sprintf(dest, "Route: %s", dialog_route_set);
            }
#ifdef _USE_OPENSSL
        } else if(strstr(keyword, "authentication")) {
            /* This keyword is substituted below */
            dest += sprintf(dest, "[%s]", keyword);
#endif
        } else if(strstr(keyword, "len")) {
            length_marker = dest;
            // dest += sprintf(dest, "   0");
            dest += sprintf(dest, "    ");
        } else {
          ERROR_P1("Unsupported keyword '%s' in xml scenario file",
                   keyword);
        }
      } else if (*src == '\n') {
        *dest++ = '\r';
        *dest++ = *src++;
      } else {
        *dest++ = *src++;
      }
    }
    *dest = 0;

#ifdef _USE_OPENSSL
    /* 
     * 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((src = strstr(msg_buffer, "[authentication")) && dialog_authentication) {

        char my_auth_user[KEYWORD_SIZE];
        char my_auth_pass[KEYWORD_SIZE];
        char * tmp;

        strcpy(my_auth_user, service);
        strcpy(my_auth_pass, auth_password);

        /* Look for optional username and password paramaters */
        if(tmp = strstr(src, "username=")) {
            tmp += strlen("username=");
            key = tmp;
            while (*key) {
                if (((key - src) > KEYWORD_SIZE) || (!(key - src))) {
                    ERROR("Syntax error parsing authentication paramaters");
                } else if (*key == ']' || *key < 33 || *key > 126) {
                    memset(my_auth_user, 0, sizeof(my_auth_user));
                    strncpy(my_auth_user, tmp, key-tmp);
                    break;
                }
                key++;
            }
        }

        if(tmp = strstr(src, "password=")) {
            tmp += strlen("password=");
            key = tmp;
            while (*key) {
                if (((key - src) > KEYWORD_SIZE) || (!(key - src))) {
                    ERROR("Syntax error parsing authentication paramaters");
                } else if (*key == ']' || *key < 33 || *key > 126) {
                    memset(my_auth_pass, 0, sizeof(my_auth_pass));
                    strncpy(my_auth_pass, tmp, key-tmp);
                    break;
                }
                key++;
            }
        }

        /* Need the Method name from the CSeq of the Challenge */
        char method[MAX_HEADER_LEN];
        tmp = get_last_header("CSeq") + 5;
        if(!tmp) {
            ERROR("Could not extract method from cseq of challenge");
        }
        while(isspace(*tmp) || isdigit(*tmp)) tmp++;
        sscanf(tmp,"%s", &method);

        /* Need the body for auth-int calculation */
        char body[SIPP_MAX_MSG_SIZE];
        memset(body, 0, sizeof(body));
        tmp = msg_buffer;
        while(*(tmp+4)) {
            if (*tmp == '\r' && *(tmp + 1) == '\n' &&
                    *(tmp + 2) == '\r' && *(tmp + 3) == '\n') {
                sprintf(body, "%s", tmp+4);
                break;
            }
            tmp++;                      
        }

        /* Build the auth credenticals */
        char result[MAX_HEADER_LEN];
        if (createAuthHeader(my_auth_user, my_auth_pass, method, remote_ip,
                body, dialog_authentication, result) == 0) {
            ERROR_P1("%s", result);
        }
   
        char tmp_buffer[SIPP_MAX_MSG_SIZE];
        dest = strncpy(tmp_buffer, msg_buffer, src - msg_buffer);
        dest += src - msg_buffer;
        key = strchr(src, ']');
        src += key - src + 1;

        if (dialog_challenge_type == 401) {
            /* Registrars use Authorization */
            dest += sprintf(dest, "Authorization: %s", result);
        } else {
            /* Proxies use Proxy-Authorization */
            dest += sprintf(dest, "Proxy-Authorization: %s", result);
        }
        dest += sprintf(dest, "%s", src);
        strcpy(msg_buffer, tmp_buffer);
    }
#endif

    // Remove all \r, \n but 1 at the end of a message to send 
    int len = strlen(msg_buffer);
    while ( (msg_buffer[len-1] == '\n') &&
            (msg_buffer[len-2] == '\r') &&
            (msg_buffer[len-3] == '\n') &&
            (msg_buffer[len-4] == '\r')) {
      msg_buffer[len-2] = 0;
      len -= 2;
    }

    int    L_flag_crlf = 0 ; // don't need add crlf
    int    L_content_length = 0;

    if(P_index == -1 ) {
      L_flag_crlf = 1 ; // Add crlf
    } else if(P_index >= 0 ) {
      message::ContentLengthFlag L_flag_content = scenario[P_index] -> content_length_flag ; 
      switch (L_flag_content) {
        case  message::ContentLengthValueZero :
          L_flag_crlf = 1;
          break ;
        case  message::ContentLengthValueNoZero :
          // the msg contains content-length field and his value is greater than 0
          break ;
        default :
          // the msg does not contain content-length field
          // control the crlf
          L_content_length = xp_get_content_length(msg_buffer) ;
          if( L_content_length == 0) {
            L_flag_crlf = 1;
          } else if (L_content_length == -1 ) {
            // Error to treat?
          } 
          break;
      }
    } 

    if(L_flag_crlf) {
      // Add crlf 
      msg_buffer[len] ='\r';
      msg_buffer[len+1] ='\n';
      msg_buffer[len+2] =0;
    }

    if (length_marker) {
      key = strstr(length_marker,"\r\n\r\n");
      if (key && dest - key > 4 && dest - key < 10004) {
        char tmp = length_marker[4];
        sprintf(length_marker, "%4u", dest - key - 4 + offset);
        length_marker[4] = tmp;
      }
    }
  } else {
    ERROR("Unsupported 'send' message in scenario");
  }
  return(msg_buffer);
}


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

  if (checkInternalCmd(msg) == false) {

    for(search_index = msg_index;
      search_index < scenario_len;
      search_index++) {
      if(scenario[search_index] -> M_type != MSG_TYPE_RECVCMD) {
        if(scenario[search_index] -> optional) {
          continue;
        }
        /* The received message is different from the expected one */
        return rejectCall();
      } else {
        found = true;
        break;
      }
    }
    
    if (found) {
      scenario[search_index]->M_nbCmdRecv ++;
      
      // variable treatment
      // WARNING_P1("---RECVD_TWIN_CMD---%s---", msg); 
      // 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;
      }
      // WARNING_P1("---RECVD_TWIN_CMD AFTER---%s---", msg);
      actionResult = executeAction(msg, search_index);
      
      if(actionResult != call::E_AR_NO_ERROR) {
        // Store last action result if it is an error
        // and go on with the scenario
        call::last_action_result = actionResult;
        if (actionResult == E_AR_STOP_CALL) {
            return rejectCall();
        }
      }
    } else {
      return rejectCall();
    }
    return(next());
    
  } else {
    return (false);
  }
}

bool call::checkInternalCmd(char * cmd)
{

  char * L_ptr1, * L_ptr2, L_backup;
  char *cmd_name       ;

  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();
    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
    return (true);
  }

  *L_ptr2 = L_backup;
  return (false);
}
#endif


bool call::process_incomming(char * msg)
{
  int             reply_code;
  static char     request[65];
  unsigned long   cookie;
  char          * ptr;
  int             search_index;
  bool            found = false;
  T_ActionResult  actionResult;

  int             L_case = 0 ;

#define MATCHES_SCENARIO(index)                                \
      (((reply_code) &&                                        \
        ((scenario[index] -> recv_response) == reply_code)) || \
       ((scenario[index] -> recv_request) &&                   \
        (!strcmp(scenario[index] -> recv_request,              \
                 request))))

  if((transport == T_UDP) && (retrans_enabled)) {

  /* Detects retransmissions from peer and retransmit the 
   * message which was sent just after this one was received */
  cookie = hash(msg);
  if(recv_retrans_hash == cookie) {

    int status;

    if(lost(scenario[recv_retrans_recv_index] -> lost)) {
      TRACE_MSG((s, "%s message (retrans) lost (recv).", 
                 TRANSPORT_TO_STRING(transport)));

      if(comp_state) { comp_free(&comp_state); }
      scenario[recv_retrans_recv_index] -> nb_lost++;
      return true;
    }
    
    send_scene(recv_retrans_send_index, &status);

    if(status == 0) {
      scenario[recv_retrans_recv_index] -> nb_recv_retrans++;
      scenario[recv_retrans_send_index] -> nb_sent_retrans++;
    } else if(status < -1) { 
      return false; 
    }

    return true;
  }

  if(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.
     */
    return true;
    }
  }
  
  
  /* Catch peer tag if necessary */
  if((!strlen(peer_tag)) &&
     (ptr = get_peer_tag(msg))) {
    if(strlen(ptr) > (MAX_HEADER_LEN - 1)) {
      ERROR("Peer tag too long. Change MAX_TAG_LEN and recompile sipp");
    }
    strcpy(peer_tag, ptr);
  }
  
  /* 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
      }
    }
    request[0]=0;
  } 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;
      }

      reply_code = 0;
    } else {
      ERROR_P1("SIP method too long in received message '%s'",
               msg);
    }
  } else {
    ERROR_P1("Invalid sip message received '%s'",
             msg);
  }
    
  /* Try to find it in the expected non mandatory responses
   * until the first mandatory response  in the scenario */
  for(search_index = msg_index;
      search_index < scenario_len;
      search_index++) {
    
    if(!MATCHES_SCENARIO(search_index)) {
      if(scenario[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) {
    for(search_index = msg_index - 1;
        search_index >= 0;
        search_index--) {
      if(MATCHES_SCENARIO(search_index) && 
         ((scenario[search_index] -> optional))) {
        found = true;
        break;
      }
    }
  }

  int test = (!found) ? -1 : scenario[search_index]->test;
  /* test==0: No branching"
   * test==-1 branching without testing"
   * test>0   branching with testing
   */

  // Action treatment
  if (found) {
    //WARNING_P1("---EXECUTE_ACTION_ON_MSG---%s---", msg); 
    
    actionResult = executeAction(msg, search_index);

    if(actionResult != call::E_AR_NO_ERROR) {
      // Store last action result if it is an error
      // and go on with the scenario
      call::last_action_result = actionResult;
      if (actionResult == E_AR_STOP_CALL) {
          return rejectCall();
      }
      // handle the error
      return next();
    }
  }
  
  /* Not found */
  if(!found) {
    if ((L_case = checkAutomaticResponseMode(request)) == 0) {
      if (!process_unexpected(msg)) {
        return false; // Call aborted by unexpected message handling
      }
    } else {
      automaticResponseMode(L_case, msg);
      return false ; // call aborted by automatic response mode
    }
  }

  /* Simulate loss of messages */
  if(lost(scenario[search_index] -> lost)) {
    TRACE_MSG((s, "%s message lost (recv).", 
               TRANSPORT_TO_STRING(transport)));
    if(comp_state) { comp_free(&comp_state); }
    scenario[search_index] -> nb_lost++;
    return true;
  }
  
  /* This is an ACK or a response, and its index is greater than the 
   * current active retransmission message, so we stop the retrans timer. */
  if(((reply_code) ||
      (!strcmp(request, "ACK")))  && 
     (search_index > last_send_index)) {
    next_retrans = 0;
  }
  
  /* 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;
  }
  
  /* If this message can be used to compute RTD, do it now */
  if(!rtd_done) {
    if(scenario[search_index] -> start_rtd) {
      start_time_rtd = clock_tick;
    }

    if(scenario[search_index] -> stop_rtd) {
      rtd_sum += clock_tick - start_time_rtd; 
      CStat::instance()->
        computeStat(CStat::E_ADD_RESPONSE_TIME_DURATION, 
                    clock_tick - start_time_rtd);
      rtd_nb ++;
      rtd_done = true;
    }
  }

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

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

      /* should cache the route set */
      if (reply_code) {

          /* is a reply. */
          char rr[MAX_HEADER_LEN];
          strcpy(rr, get_header_content(msg, (char*)"Record-Route:"));

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

          bool isFirst(true);
          while (1)
          {
              char* pointer = strrchr(rr, ',');
              if (pointer) {
                if (!isFirst) {
                  strcat(actual_rr, pointer + 1);
                } else {
                  isFirst = false;
                }
              } else {
                if (!isFirst) {
                  strcat(actual_rr, rr);
                }
                break;
              }
              *pointer = '\0';
          }
          /* lose the bottom most record route -- that is the SUT */

          char ch[MAX_HEADER_LEN];
          strcpy(ch, get_header_content(msg, (char*)"Contact:"));

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

          /* is a request. */
          char rr[MAX_HEADER_LEN];
          strcpy(rr, get_header_content(msg, (char*)"Record-Route:"));

          /* toss the first RR, it is going to be the SUT */
          char* actual_rr = strchr(rr, ',');

          if (actual_rr) {
              actual_rr += 1;
          }

          char ch[MAX_HEADER_LEN];
          strcpy(ch, get_header_content(msg, (char*)"Contact:"));

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

#ifdef _USE_OPENSSL
  /* store the authentication info */
  if ((scenario[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:"));
      }


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

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

  /* If this was a mandatory message, and keeps its cookie for
   * future retransmissions, and its body for fields inclusion
   * in our messages. Similarly if there is an explicit next label set 
   */
  if (!(scenario[search_index] -> optional) ||
       scenario[search_index]->next && 
      ((test == -1) ||
       (test < SCEN_VARIABLE_SIZE && M_callVariableTable[test] != NULL && M_callVariableTable[test]->isSet()))
     ) {
    msg_index = search_index;

    /* Store last recv msg information */
    last_recv_index = search_index;
    last_recv_hash = cookie;

    last_recv_msg = (char *) realloc(last_recv_msg, strlen(msg) + 1);
    strcpy(last_recv_msg, msg);
    return next();
  }
  return true;
}
  
call::T_ActionResult call::executeAction(char * msg, int scenarioIndex)
{
  CActions*  actions;
  CAction*   currentAction;
  CVariable* scenVariable;
  char       msgPart[MAX_SUB_MESSAGE_LENGTH];
  int        currentId;

  actions = scenario[scenarioIndex]->M_actions;
  // looking for action to do on this message
  if(actions != NULL) {
    for(int i=0; i<actions->getUsedAction(); i++) {
      currentAction = actions->getAction(i);
      if(currentAction != NULL) {
        if(currentAction->getActionType() == CAction::E_AT_ASSIGN_FROM_REGEXP) {
          currentId = currentAction->getVarId();
          scenVariable = scenVariableTable[currentId];
          if(scenVariable != NULL) {
            if(currentAction->getLookingPlace() == CAction::E_LP_HDR) {
              extractSubMessage
                                (msg, 
                                currentAction->getLookingChar(), 
                                msgPart);
        
              if(strlen(msgPart) > 0) {
          
                scenVariable->executeRegExp
                                  (msgPart, 
                                  M_callVariableTable,
				  currentId,
				  currentAction->getNbSubVarId(),
				  currentAction->getSubVarId()
				  );
          
                if( (!(M_callVariableTable[currentId]->isSet())) 
                && (currentAction->getCheckIt() == true) ) {
                  // the message doesn't match and the checkit 
                  // action say it MUST match
                  // Allow easier regexp debugging
                  WARNING_P2("Failed regexp match: looking "
                  "in '%s', with regexp '%s'", 
                  msgPart, 
                  scenVariable->
                  getRegularExpression());
                  // --> Call will be marked as failed
                  return(call::E_AR_REGEXP_DOESNT_MATCH);
                }
              } else {// sub part of message not found
                if( currentAction->getCheckIt() == true ) {
                  // the sub message is not found and the
                  // checking action say it MUST match
                  // --> Call will be marked as failed but 
                  // will go on
                  return(call::E_AR_HDR_NOT_FOUND);
                } 
              }
            } else {// we must look in the entire message
              // WARNING_P1("LOOKING IN MSG -%s-", msg);
                scenVariable->executeRegExp
                              (msg, 
                                  M_callVariableTable,
				  currentId,
				  currentAction->getNbSubVarId(),
				  currentAction->getSubVarId()
				  );

		   
              if((!(M_callVariableTable[currentId]->isSet())) 
              && (currentAction->getCheckIt() == true) ) {
                // the message doesn't match and the checkit 
                // action say it MUST match
                // Allow easier regexp debugging
                WARNING_P2("Failed regexp match: looking in '%s'"
                ", with regexp '%s'", 
                msg, 
                scenVariable->getRegularExpression());
                // --> rejecting the call
                return(call::E_AR_REGEXP_DOESNT_MATCH);
              }
            }
          } // end if scen variable != null
        } else /* end action == E_AT_ASSIGN_FROM_REGEXP */ 
            if (currentAction->getActionType() == CAction::E_AT_LOG_TO_FILE) {
            char* x = createSendingMessage(currentAction->getMessage(), -2 /* do not add crlf*/);
            LOG_MSG((s, "%s\n", x));
        } else /* end action == E_AT_LOG_TO_FILE */ 
            if (currentAction->getActionType() == CAction::E_AT_EXECUTE_CMD) {

            if (currentAction->getCmdLine()) {
                char* x = createSendingMessage(currentAction->getCmdLine(), -2 /* do not add crlf*/);
                // TRACE_MSG((s, "Trying to execute [%s]", x)); 
                pid_t l_pid;
                switch(l_pid = fork())
                {
                    case -1:
                        // error when forking !
                        ERROR("Forking error");
                        break;

                    case 0:
                        // child process - execute the command
                        system(x);
                        exit(EXIT_OTHER);

                    default:
                        // parent process continue
                        break;
                }
            }
        } else /* end action == E_AT_LOG_TO_FILE */ 
            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;
                }
        } else {// end action == E_AT_EXECUTE_CMD
          ERROR("call::executeAction unknown action");
        }
      } // end if current action != null
    } // end for
  }
  return(call::E_AR_NO_ERROR);
}

void call::extractSubMessage(char * msg, char * matchingString, char* result)
{
  char * ptr;
  int sizeOf;
  int i = 0;
  int len;

  ptr = strstr(msg, matchingString); 
  if(ptr != NULL) {
    len = strlen(matchingString);
    strcpy(result, ptr+len);
    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::dumpFileContents(void)
{
    WARNING_P3("Line choosing strategy is [%s]. m_counter [%d] numLinesInFile [%d]",
               m_usage == InputFileSequentialOrder ? "SEQUENTIAL" : "RANDOM",
               m_counter, numLinesInFile);

    for (int i(0); i < numLinesInFile && fileContents[i][0]; ++i) {
        WARNING_P2("%dth line reads [%s]", i, fileContents[i].c_str());
    }
}

/* Read MAX_CHAR_BUFFER_SIZE size lines from the
 * "fileName" and populate it in the fileContents
 * vector. The file should not be more than
 * MAX_LINES_IN_FILE lines long and each line
 * should be terminated with a '\n'
 */

void call::readInputFileContents(const char* fileName)
{
  ifstream *inFile    = new ifstream(fileName);
  ifstream &inFileObj = *inFile;
  char      line[MAX_CHAR_BUFFER_SIZE];
  
  if (!inFile->good()) {
    ERROR_P1("Unable to open file %s", fileName);
    return ;
  }

  numLinesInFile = 0;
  call::m_counter = 0;
  line[0] = '\0';
  inFileObj.getline(line, MAX_CHAR_BUFFER_SIZE);

  if (NULL != strstr(line, "RANDOM")) {
      call::m_usage = InputFileRandomOrder;
  } else if (NULL != strstr(line, "SEQUENTIAL")) {
      call::m_usage = InputFileSequentialOrder;
  } else {
      // default
      call::m_usage = InputFileSequentialOrder;
  }

  while (!inFileObj.eof()) {
    line[0] = '\0';
    inFileObj.getline(line, MAX_CHAR_BUFFER_SIZE);
    if (line[0]) {
      if ('#' != line[0]) {
        fileContents.push_back(line);
        numLinesInFile++; /* this counts number of valid data lines */
      }
    } else {
      break;
    }
  }
  // call::dumpFileContents();
  delete inFile;
}
 
void call::getFieldFromInputFile(const char* keyword, int lineNum, char*& dest)
{
  int nthField    = atoi(keyword+5 /*strlen("field")*/);
  int origNth     = nthField;
  
  if (fileContents.size() > lineNum) {
    const string& line = fileContents[lineNum];
    
    // WARNING_P3("lineNum [%d] nthField [%d] line [%s]",
    //         lineNum, nthField, line.c_str());
    
    int pos(0), oldpos(0);
    do {
      oldpos = pos;
      int localint = line.find(';', oldpos);
      
      if (localint != string::npos) {
        pos = localint + 1;
      } else {
        pos = localint;
        break;
      }
      
      //string x = line.substr(oldpos, pos - oldpos);
      // WARNING_P3("pos [%d] oldpos [%d] is [%s]", pos, oldpos, x.c_str());
      
      if (nthField) {
        --nthField;
      } else {
        break;
      }
      
    } while (oldpos != string::npos);
    
    if (nthField) {
      WARNING_P1("Field %d not found in the file", origNth);
      // field not found in line
    } else {
      if (string::npos != oldpos) {
        if (string::npos != pos) {
          // should not be decremented for fieldN
          pos -= (oldpos + 1);
        }
    
        string x = line.substr(oldpos, pos);
        if (x.length()) {
        dest += sprintf(dest, "%s", x.c_str());
        }
        
        // WARNING_P2("nthField [%d] is [%s]", origNth, x.c_str());
      }
    }
  } else {
    // WARNING_P1("Field %d definition not found", nthField);
  }
}

int  call::checkAutomaticResponseMode(char * P_recv) {

  int L_res = 0 ;

  if (strcmp(P_recv, "BYE")==0) {
    L_res = 1 ;
  } else if (strcmp(P_recv, "CANCEL") == 0) {
    L_res = 2 ;
  } else if (strcmp(P_recv, "PING") == 0) {
    L_res = 3 ;
  }

  return (L_res) ;
  
}


void call::automaticResponseMode(int P_case, char * P_recv)
{

  int res ;

  // usage of last_ keywords
  last_recv_msg = (char *) realloc(last_recv_msg, strlen(P_recv) + 1);
  strcpy(last_recv_msg, P_recv);

  switch (P_case) {
  case 1: // response for an unexpected BYE
    if (default_behavior) {
      WARNING_P1("Aborting call on an unexpected BYE for call: %s", (id==NULL)?"none":id);
    res = sendBuffer(createSendingMessage(
                    (char*)"SIP/2.0 200 OK\n"
                    "[last_Via:]\n"
                    "[last_From:]\n"
                    "[last_To:]\n"
                    "[last_Call-ID:]\n"
                    "[last_CSeq:]\n"
                    "Contact: <sip:[local_ip]:[local_port];transport=[transport]>\n"
                    "Content-Length: 0\n"
                    , -1)) ;

#ifdef __3PCC__
    // if twin socket call => reset the other part here 
    if (twinSippSocket && (msg_index > 0)) {
      res = sendCmdBuffer
      (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n", -1));
    }
#endif /* __3PCC__ */
      CStat::instance()->computeStat(CStat::E_CALL_FAILED);
      CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
      delete_call(id);
    } else {
      WARNING_P1("Continuing call on an unexpected BYE for call: %s", (id==NULL)?"none":id);
    }
      break ;
      
  case 2: // response for an unexpected cancel
    if (default_behavior) {
      WARNING_P1("Aborting call on an unexpected CANCEL for call: %s", (id==NULL)?"none":id);
    res = sendBuffer(createSendingMessage(
                      (char*)"SIP/2.0 200 OK\n"
                      "[last_Via:]\n"
                      "[last_From:]\n"
                      "[last_To:]\n"
                      "[last_Call-ID:]\n"
                      "[last_CSeq:]\n"
                      "Contact: sip:sipp@[local_ip]:[local_port]\n"
                      "Content-Length: 0"
                      , -1)) ;
    
#ifdef __3PCC__
    // if twin socket call => reset the other part here 
    if (twinSippSocket && (msg_index > 0)) {
      res = sendCmdBuffer
      (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n", -1));
    }
#endif /* __3PCC__ */
    
    CStat::instance()->computeStat(CStat::E_CALL_FAILED);
    CStat::instance()->computeStat(CStat::E_FAILED_UNEXPECTED_MSG);
    delete_call(id);
    } else {
      WARNING_P1("Continuing call on unexpected CANCEL for call: %s", (id==NULL)?"none":id);
    }
    break ;
      
  case 3: // response for a random ping
    if (default_behavior) {
    WARNING_P1("Automatic response mode for an unexpected PING for call: %s", (id==NULL)?"none":id);
    count_in_stats = false; // Call must not be counted in statistics
    res = sendBuffer(createSendingMessage(
                    (char*)"SIP/2.0 200 OK\n"
                    "[last_Via:]\n"
                    "[last_Call-ID:]\n"
                    "[last_To:]\n"
                    "[last_From:]\n"
                    "[last_CSeq:]\n"
                    "Contact: sip:sipp@[local_ip]:[local_port]\n"
                    "Content-Length: 0"
                    , -1)) ;
    // Note: the call ends here but it is not marked as bad. PING is a 
    //       normal message.
#ifdef __3PCC__
    // if twin socket call => reset the other part here 
    if (twinSippSocket && (msg_index > 0)) {
      res = sendCmdBuffer
      (createSendingMessage((char*)"call-id: [call_id]\ninternal-cmd: abort_call\n",-1));
    }
#endif /* __3PCC__ */
    
    CStat::instance()->computeStat(CStat::E_AUTO_ANSWERED);
    delete_call(id);
    } else {
      WARNING_P1("Do not answer on an unexpected PING for call: %s", (id==NULL)?"none":id);
    }
    break ;

    default:
    ERROR_P1("Internal error for automaticResponseMode %d", P_case);
    break ;
  }
  
}


