/******************************************************************************
* Copyright (c) 2000-2019 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
******************************************************************************/
///////////////////////////////////////////////////////////////////////////////
//
//  File:     TCCMessageHandling.cc
//  Rev:      R36B
//  Prodnr:   CNL 113 472
//
///////////////////////////////////////////////////////////////////////////////

#include "TTCN3.hh"
#include "TCCMessageHandling_Functions.hh"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>

int get_content_length(char *buff, int length)
{
  int current_state=0;
  int ret_val=-1;
  for(int a=0;a<length;a++){
    switch(current_state){
      case 0:
        if(buff[a]=='\n') current_state=1;
        break;
      case 1:
        if(buff[a]=='l' || buff[a]=='L') current_state=2;
        else if( (a+14 < length)  && (!strncasecmp(buff+a,"content-length",14))) {current_state=2;a+=13;}
        else current_state=0;
        break;
      case 2:
        if(buff[a]==':') current_state=3;
        else if(buff[a]==' ' || buff[a]=='\t' || buff[a]=='\r' || buff[a]=='\n') current_state=2;
        else current_state=0;
        break;
      case 3:
        if(buff[a]>='0' && buff[a]<='9'){
          ret_val=buff[a]-'0';
          current_state=4;
        }
        else if(buff[a]==' ' || buff[a]=='\t' || buff[a]=='\r' || buff[a]=='\n') current_state=3;
        else current_state=0;
        break;
      case 4:
        if(buff[a]>='0' && buff[a]<='9'){
          ret_val*=10;
          ret_val+=buff[a]-'0';
        }
        else return ret_val;
        break;
    }
  }
  return ret_val;
}

char* get_header_end(char *buff, int length)
{
  int current_state = 0;
  char* ret_val = NULL;
  for (int a=0;a<length;a++)
  {
    switch(current_state){
      case 0:
        if(buff[a]=='\n') current_state=1;
        break;
      case 1:
        if(buff[a]=='\r') current_state=2;
        else current_state=0;
        break;
      case 2:
        if(buff[a]=='\n')
          return buff+a-2;
        else current_state=0;
        break;
    }
  }
  return ret_val;
}

INTEGER TCCMessageHandling__Functions::f__TCCMessageHandling__getMessageLength
(
  const OCTETSTRING& stream
)
{
  char *msg_in_buffer_loc = (char*) ((const unsigned char*) stream);
  const int   buf_length = stream.lengthof();
  const char *header_end;
  char *atm = msg_in_buffer_loc;
  int num_chars;

  //check for STUN response
  if (2 == buf_length && '\r' == msg_in_buffer_loc[0] && '\n' == msg_in_buffer_loc[1])
    return 2;
  
  while((*atm<=' ') && (*atm>0) && (atm-msg_in_buffer_loc < buf_length-1)) {atm++;}  // skip leading blanks

  //header_end=strstr(atm,"\n\r\n");  // search for alone crlf
  header_end = get_header_end(atm, buf_length-(atm-msg_in_buffer_loc));

  if(header_end)
    num_chars=(header_end-msg_in_buffer_loc)+3;
  else
    return -1;

  int cont_len = get_content_length(msg_in_buffer_loc,num_chars);
  if (cont_len == -1) 
    return -1;

  return INTEGER(num_chars + cont_len);
}

INTEGER TCCMessageHandling__Functions::f__TCCMessageHandling__getMessageLength4Diameter
(
  const OCTETSTRING& stream
)
{
  if (stream.lengthof() < 4){ return -1; }
  const unsigned char *msg_in_buffer_loc =  ((const unsigned char*) stream);
  
  int ret_value = (msg_in_buffer_loc[1] << 16) + (msg_in_buffer_loc[2] << 8) + (msg_in_buffer_loc[3]);
  return ret_value;
}

INTEGER TCCMessageHandling__Functions::f__TCCMessageHandling__getMessageLength4Radius
(
  const OCTETSTRING& stream
)
{
  if (stream.lengthof() < 4){ return -1; }
  const unsigned char *msg_in_buffer_loc =  ((const unsigned char*) stream);
  
  int ret_value = (msg_in_buffer_loc[2] << 8) + (msg_in_buffer_loc[3]);
  return ret_value;
}

INTEGER TCCMessageHandling__Functions::f__TCCMessageHandling__getMessageLength4BER
(
  const OCTETSTRING& stream
)
{   
    const unsigned char* data =  (const unsigned char*)stream;  
    size_t length = stream.lengthof();
  
    enum position {TAG,LENGTH,INDEFINITE};

    position pos = TAG;
    size_t number_of_L_bytes = 0;
    size_t leng = 0;
  
    for (size_t i = 0; i < length; ){
	  switch(pos){
		  case TAG:{
			  if ((data[i] & 0x1F) > 30){
				  ++i;
				  for ( ;(i < length) && (data[i] & 0x80); ++i);
			  }

			  ++i;
			  pos = LENGTH;
			  break;

		  }
		  case LENGTH:{
			  if (data[i] & 0x80){
				  if (! (data[i] & 0x7F)) //indefinite form
					  pos = INDEFINITE;
				  else{ //long form
					  number_of_L_bytes = data[i] & 0x7F;
					  ++i;
					  for(;(i < length) && number_of_L_bytes; ++i, --number_of_L_bytes){
					    leng<<=8;
					    leng+=data[i];
					  }					  

					  return leng + i;
					    
				  }
			  } else{ //short form
				 
					  return i + data[i] + 1;
			  }
			  
			  ++i;
			  break;
		  }
		  case INDEFINITE:{
	
			ASN_BER_TLV_t tmp_tlv;   
                        if(ASN_BER_str2TLV(stream.lengthof(),stream,tmp_tlv, BER_ACCEPT_ALL)){
                          return tmp_tlv.get_len();
                        } 
                        else
                          return -1; 
		  }
		  default:
			  TTCN_error("Unknown state while parsing TLV.");
	  }
  }

  return -1;  
  
   
     
}