/*
 * Copyright (C) 2002-2003 Fhg Fokus
 *
 * This file is part of SEMS, a free SIP media server.
 *
 * SEMS 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 released under
 * the GPL with the additional exemption that compiling, linking,
 * and/or using OpenSSL is allowed.
 *
 * For a license to use the SEMS software under conditions
 * other than those described here, or to purchase support for this
 * software, please contact iptel.org by e-mail at the following addresses:
 *    info@iptel.org
 *
 * SEMS 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
 */

/** @file AmUtils.cpp */

#include "AmUtils.h"
#include "AmThread.h"
#include "AmConfig.h"
#include "log.h"
#include "AmSipMsg.h"
#include "sip/resolver.h"
#include "sip/ip_util.h"

#include <stdarg.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>

#include <sys/socket.h>
#include <sys/un.h>

#include <regex.h>
#include <algorithm>

#include <fstream>


// helper template for compile-time max string length
template<size_t len> size_t maxStrLen;

// max lengths for well known int types
template<> constexpr size_t maxStrLen<4> = 16; // int32: 4294967296 or -2147483648
template<> constexpr size_t maxStrLen<8> = 32; // int64: 18446744073709551616 or -9223372036854775808
template<> constexpr size_t maxStrLen<16> = 64; // int128: 340282366920938463463374607431768211456 or -170141183460469231731687303715884105728


template<typename T>
string num2str(T i) {
    /* support 0 values */
    if (i == 0)
	return "0";

    constexpr size_t maxlen = maxStrLen<sizeof(T)>;

    char buf[maxlen];
    size_t pos = maxlen - 1;

    bool neg = false;
    if (i < 0) {
	neg = true;
	i = -i;
    }

    while (i != 0) {
	buf[pos--] = '0' + (i % 10);
	i /= 10;
    }

    if (neg)
	buf[pos--] = '-';

    pos++;
    return string(&buf[pos], maxlen - pos);
}

string int2str(__uint128_t val) { return num2str<__uint128_t>(val); }
string int2str(unsigned long long val) { return num2str<unsigned long long>(val); }
string int2str(unsigned int val) { return num2str<unsigned int>(val); }
string int2str(int val) { return num2str<int>(val); }
string int2str(unsigned long int val) { return num2str<unsigned long int>(val); }
string int2str(long int val) { return num2str<long int>(val); }
string int2str(long long int val) { return num2str<long long int>(val); }

static char _int2hex_lookup[] = { '0', '1', '2', '3', '4', '5', '6' , '7', '8', '9','A','B','C','D','E','F' };
static char _int2hex_lookup_l[] = { '0', '1', '2', '3', '4', '5', '6' , '7', '8', '9','a','b','c','d','e','f' };

string char2hex(unsigned char val, bool lowercase)
{
  string res;
  if (lowercase) {
    res += _int2hex_lookup_l[val >> 4];
    res += _int2hex_lookup_l[val & 0x0f];
  } else {
    res += _int2hex_lookup[val >> 4];
    res += _int2hex_lookup[val & 0x0f];
  }
  return res;
}

string int2hex(unsigned int val, bool lowercase)
{
  unsigned int digit=0;

  char buffer[2*sizeof(int)+1] = {0};
  int i,j=0;

  for(i=0; i<int(2*sizeof(int)); i++){
    digit = val >> 4*(2*sizeof(int)-1);
    val = val << 4;
    buffer[j++] = lowercase ?
      _int2hex_lookup_l[(unsigned char)digit] : _int2hex_lookup[(unsigned char)digit];
  }

  return string((char*)buffer);
}

string long2hex(unsigned long val)
{
  unsigned int digit=0;

  char buffer[2*sizeof(long)+1] = {0};
  int i,j=0;

  for(i=0; i<int(2*sizeof(long)); i++){
    digit = val >> 4*(2*sizeof(long)-1);
    val = val << 4;
    buffer[j++] = _int2hex_lookup[(unsigned char)digit];
  }

  return string((char*)buffer);
}

/** Convert a double to a string. (from jsoncpp) */
string double2str(double val) {
  char buffer[32];
  sprintf(buffer, "%#.16g", val); 
  char* ch = buffer + strlen(buffer) - 1;
  if (*ch != '0') return buffer; // nothing to truncate, so save time
  while(ch > buffer && *ch == '0'){
    --ch;
  }
  char* last_nonzero = ch;
  while(ch >= buffer){
    switch(*ch){
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
      --ch;
      continue;
    case '.':
      // Truncate zeroes to save bytes in output, but keep one.
      *(last_nonzero+2) = '\0';
      return string(buffer);
    default:
      return string(buffer);
    }
  }
  return string(buffer);
}

/**
 * Convert a reversed hex string to uint.
 * @param str    [in]  string to convert.
 * @param result [out] result integer.
 * @return true on success
 */
bool reverse_hex2int(const string& str, unsigned int& result)
{
  result=0;
  char mychar;

  for (string::const_reverse_iterator pc = str.rbegin();
       pc != str.rend(); ++pc) {

    result <<= 4;
    mychar=*pc;

    if ( mychar >='0' && mychar <='9') 
      result += mychar -'0';
    else if (mychar >='a' && mychar <='f') 
      result += mychar -'a'+10;
    else if (mychar  >='A' && mychar <='F') 
      result += mychar -'A'+10;
    else 
      return true;
  }

  return false;
}

bool str2int(const string& str, unsigned int& result)
{
  char* s = (char*)str.c_str();
  return str2int(s,result);
}

bool str2int(char*& str, unsigned int& result, char sep)
{
  unsigned int ret=0;
  int i=0;
  char* init = str;

  for(; (*str != '\0') && (*str == ' '); str++);

  for(; *str != '\0';str++){
    if ( (*str <= '9' ) && (*str >= '0') ){
      ret=ret*10+*str-'0';
      i++;
      if (i>10) goto error_digits;
    } else {

      bool eol = false;
      switch(*str){
      case 0xd:
      case 0xa:
      case 0x0:
	eol = true;
      }

      if( (*str != sep) && !eol )
	goto error_char;

      break;
    }
  }

  result = ret;
  return false;

 error_digits:
  DBG("str2int: too many letters in [%s]\n", init);
  return true;
 error_char:
  DBG("str2int: unexpected char 0x%x in %s\n", *str, init);
  return true;
}

bool str2int(const string& str, int& result)
{
  char* s = (char*)str.c_str();
  return str2int(s,result);
}

bool str2int(const string& str, double& result)
{
  try
  {
    size_t pos;
    result = std::stod(str, &pos);

    /* check whether the whole stirng was converted */
    if (pos != str.length())
      return false;

    return true;
  }
  catch (const std::invalid_argument&)
  {
    return false;
  }
  catch (const std::out_of_range&)
  {
    return false;
  }

  return false;
}

bool str2int(char*& str, int& result, char sep)
{
  int ret=0;
  int i=0;
  char* init = str;
  int sign = 1;

  for(; (*str != '\0') && (*str == ' '); str++);

  if (*str == '-') {
    sign = -1;
    str++;
    for(; (*str != '\0') && (*str == ' '); str++);
  }

  for(; *str != '\0';str++){
    if ( (*str <= '9' ) && (*str >= '0') ){
      ret=ret*10+*str-'0';
      i++;
      if (i>10) goto error_digits;
    } else {

      bool eol = false;
      switch(*str){
      case 0xd:
      case 0xa:
      case 0x0:
	eol = true;
      }

      if( (*str != sep) && !eol )
	goto error_char;

      break;
    }
  }

  result = ret * sign;
  return true;

 error_digits:
  DBG("str2int: too many digits in [%s]\n", init);
  return false;
 error_char:
  DBG("str2int: unexpected char 0x%x in %s\n", *str, init);
  return false;
}

// long int could probably be the same size as int
bool str2int(const string& str, long& result)
{
  char* s = (char*)str.c_str();
  return str2int(s,result);
}

bool str2int(char*& str, long& result, char sep)
{
  long ret=0;
  int i=0;
  char* init = str;
  long sign = 1;

  for(; (*str != '\0') && (*str == ' '); str++);

  if (*str == '-') {
    sign = -1;
    str++;
    for(; (*str != '\0') && (*str == ' '); str++);
  }

  for(; *str != '\0';str++){
    if ( (*str <= '9' ) && (*str >= '0') ){
      ret=ret*10+*str-'0';
      i++;
      if (i>20) goto error_digits;
    } else {

      bool eol = false;
      switch(*str){
      case 0xd:
      case 0xa:
      case 0x0:
	eol = true;
      }

      if( (*str != sep) && !eol )
	goto error_char;

      break;
    }
  }

  result = ret * sign;
  return true;

 error_digits:
  DBG("str2int: too many digits in [%s]\n", init);
  return false;
 error_char:
  DBG("str2int: unexpected char 0x%x in %s\n", *str, init);
  return false;
}

bool str2int(const string& str, __uint128_t & result)
{
  char* s = (char*)str.c_str();
  result = str2int(s);
  return (result ? true : false);
}

__uint128_t str2int(const char* str) {
    __uint128_t result = 0;
    while (*str) {
        if (*str < '0' || *str > '9') {
            DBG("Invalid character in string '%s'\n", str);
            result = 0;
            break;
        }
        result = result * 10 + (*str - '0');
        str++;
    }
    return result;
}

// long int could probably be the same size as int
bool str2int(const string& str, unsigned long long& result)
{
  char* s = (char*)str.c_str();
  return str2int(s,result);
}

bool str2int(char*& str, unsigned long long& result, char sep)
{
  unsigned long long ret=0;
  int i=0;
  char* init = str;
  long sign = 1;

  for(; (*str != '\0') && (*str == ' '); str++);

  if (*str == '-') {
    sign = -1;
    str++;
    for(; (*str != '\0') && (*str == ' '); str++);
  }

  for(; *str != '\0';str++){
    if ( (*str <= '9' ) && (*str >= '0') ){
      ret=ret*10+*str-'0';
      i++;
      if (i>20) goto error_digits;
    } else {

      bool eol = false;
      switch(*str){
      case 0xd:
      case 0xa:
      case 0x0:
	eol = true;
      }

      if( (*str != sep) && !eol )
	goto error_char;

      break;
    }
  }

  result = ret * sign;
  return true;

 error_digits:
  DBG("str2int: too many digits in [%s]\n", init);
  return false;
 error_char:
  DBG("str2int: unexpected char 0x%x in %s\n", *str, init);
  return false;
}

bool str2bool(const string &s, bool &dst)
{
  // TODO: optimize
  if ((s == "yes") || (s == "true") || (s == "1")) {
    dst = true;
    return true;
  }
  if ((s == "no") || (s == "false") || (s == "0")) {
    dst = false;
    return true;
  }
  return false;
}

std::string URL_decode(const std::string& s) {
  enum {
    uSNormal=       0, // start
    uSH1,
    uSH2
  };

  int st = uSNormal;
  string res;
  for (size_t pos = 0; pos < s.length(); pos++) {
    switch (st) {
    case uSNormal: {
      if (s[pos] == '%')
	st = uSH1;
      else
	res+=s[pos];

    }; break;

    case uSH1: {
      if (s[pos] == '%') {
	res+='%';
	st = uSNormal;
      } else {
      st = uSH2;
      }
    }; break;

    case uSH2: {
      char c = 0;

      if ( s[pos] >='0' && s[pos] <='9')
	c += s[pos] -'0';
      else if (s[pos] >='a' && s[pos] <='f')
	c += s[pos] -'a'+10;
      else if (s[pos]  >='A' && s[pos] <='F')
	c += s[pos] -'A'+10;
      else {
	st = uSNormal;
	DBG("error in escaped string: %%%c%c\n", s[pos-1], s[pos]);
	continue;
      }

      if ( s[pos-1] >='0' && s[pos-1] <='9')
	c += (s[pos-1] -'0') << 4;
      else if (s[pos-1] >='a' && s[pos-1] <='f')
	c += (s[pos-1] -'a'+10) << 4;
      else if (s[pos-1]  >='A' && s[pos-1] <='F')
	c += (s[pos-1] -'A'+10 ) << 4;
      else {
	st = uSNormal;
	DBG("error in escaped string: %%%c%c\n", s[pos-1], s[pos]);
	continue;
      }
      res +=c;
      st = uSNormal;
    } break;
    }
  }

  return res;
}

std::string URL_encode(const std::string &s)
{
    const std::string unreserved = "-_.~";

    std::string escaped="";
    for(size_t i=0; i<s.length(); i++)
    {

      //RFC 3986 section 2.3 Unreserved Characters (January 2005)
      if ((s[i] >= 'A' && s[i] <= 'Z')
	  || (s[i] >= 'a' && s[i] <= 'z')
	  || (s[i] >= '0' && s[i] <= '9')
	  || (s[i] == '-') || (s[i] == '_') || (s[i] == '.') || (s[i] == '~') )
        {
            escaped.push_back(s[i]);
        }
        else
        {
            escaped.append("%");
            char buf[3];
            sprintf(buf, "%.2X", s[i]);
            escaped.append(buf);
        }
    }
    return escaped;
}

int parse_return_code(const char* lbuf, unsigned int& res_code, string& res_msg )
{
  char res_code_str[4] = {'\0'};
  const char* cur=lbuf;

  // parse xxx code
  for( int i=0; i<3; i++ ){
    if( (*cur >= '0') && (*cur <= '9') )
      res_code_str[i] = *cur;
    else
      goto error;
    cur++;
  }

  if( (*cur != ' ') && (*cur != '\t') && (*cur !='-') ){
    ERROR("expected 0x%x or 0x%x or 0x%x, found 0x%x\n",' ','\t','-',*cur);
    goto error;
  }

  if(sscanf(res_code_str,"%u",&res_code) != 1){
    ERROR("wrong code (%s)\n",res_code_str);
    goto error;
  }

  // wrap spaces and tabs
  while( (*cur == ' ') || (*cur == '\t') || (*cur =='-')) 
    cur++;

  res_msg = cur;
  return 0;

 error:
  ERROR("while parsing response\n");
  return -1;
}

bool file_exists(const string& name)
{
  FILE* test_fp = fopen(name.c_str(),"r");
  if(test_fp){
    fclose(test_fp);
    return true;
  }
  return false;
}

string filename_from_fullpath(const string& path)
{
  string::size_type pos = path.rfind('/');
  if(pos != string::npos)
    return path.substr(pos+1);
  return "";
}

string get_addr_str(const sockaddr_storage* addr)
{
  char host[NI_MAXHOST] = "";
  return am_inet_ntop(addr,host,NI_MAXHOST);
}

string get_addr_str_sip(const sockaddr_storage* addr)
{
  char host[NI_MAXHOST] = "";
  return am_inet_ntop_sip(addr,host,NI_MAXHOST);
}

string file_extension(const string& path)
{
  string::size_type pos = path.rfind('.');
  if(pos == string::npos){
    pos = path.rfind("%2E");

    if(pos == string::npos)
      return "";
    return path.substr(pos+3,string::npos);
  }

  return path.substr(pos+1,string::npos);
}

string add2path( const string& path, int n_suffix, ...)
{
  va_list ap;
  string outpath = path;
    
  va_start(ap,n_suffix);

  for(int i=0; i<n_suffix; i++){

    const char* s = va_arg(ap,const char*);

    if(!outpath.empty() && (outpath[outpath.length()-1] != '/'))
      outpath += '/';

    outpath += s;
  }

  va_end(ap);

  return outpath;
}

int get_local_addr_for_dest(sockaddr_storage* remote_ip, sockaddr_storage* local)
{
  int temp_sock = socket(remote_ip->ss_family, SOCK_DGRAM, 0 );
  if (temp_sock == -1) {
    ERROR("socket() failed: %s\n",
	  strerror(errno));
    return -1;
  }

  socklen_t len=sizeof(sockaddr_storage);
  if (connect(temp_sock, (sockaddr*)remote_ip, 
	      remote_ip->ss_family == AF_INET ? 
	      sizeof(sockaddr_in) : sizeof(sockaddr_in6))==-1) {

      ERROR("connect failed: %s\n",
	    strerror(errno));
      goto error;
  }

  if (getsockname(temp_sock, (sockaddr*)local, &len)==-1) {
      ERROR("getsockname failed: %s\n",
	    strerror(errno));
      goto error;
  }

  close(temp_sock);
  return 0;

 error:
  close(temp_sock);
  return -1;
}

int get_local_addr_for_dest(const string& remote_ip, string& local)
{
  sockaddr_storage remote_ip_ss;
  sockaddr_storage local_ss;

  int err = inet_pton(AF_INET,remote_ip.c_str(),&((sockaddr_in*)&remote_ip_ss)->sin_addr);
  if(err == 1){
    remote_ip_ss.ss_family = AF_INET;
  }
  else if(err == 0){
    err = inet_pton(AF_INET6,remote_ip.c_str(),&((sockaddr_in6*)&remote_ip_ss)->sin6_addr);
    if(err == 1){
      remote_ip_ss.ss_family = AF_INET6;
    }
  }

  if(err == 0){
    // not an IP... try a name.
    dns_handle dh;
    err = resolver::instance()->resolve_name(remote_ip.c_str(),&dh,&remote_ip_ss,IPv4);
  }

  if(err == -1){
    ERROR("While converting address: '%s'",remote_ip.c_str());
    return -1;
  }

  if(remote_ip_ss.ss_family==AF_INET){
#if defined(BSD44SOCKETS)
    ((sockaddr_in*)&remote_ip_ss)->sin_len = sizeof(sockaddr_in);
#endif
    ((sockaddr_in*)&remote_ip_ss)->sin_port = htons(5060); // fake port number
  }
  else {
#if defined(BSD44SOCKETS)
    ((sockaddr_in6*)&remote_ip_ss)->sin6_len = sizeof(sockaddr_in6);
#endif
    ((sockaddr_in6*)&remote_ip_ss)->sin6_port = htons(5060); // fake port number
  }

  err = get_local_addr_for_dest(&remote_ip_ss, &local_ss);
  if(err < 0){
    return -1;
  }

  char tmp_addr[NI_MAXHOST];
  if(am_inet_ntop(&local_ss,tmp_addr,NI_MAXHOST) != NULL){
    local = tmp_addr;
    return 0;
  }
  
  return -1;
}

string extract_tag(const string& addr)
{
  string::size_type p = addr.find(";tag=");
  if(p == string::npos)
    return "";

  p += 5/*sizeof(";tag=")*/;
  string::size_type p_end = p;
  while(p_end < addr.length()){
    if( addr[p_end] == '>'
	|| addr[p_end] == ';' )
      break;
    p_end++;
  }
  return addr.substr(p,p_end-p);
}

bool key_in_list(const string& s_list, const string& key, 
		 char list_delim) 
{
  size_t pos = 0;
  size_t pos2 = 0;
  size_t pos_n = 0;
  while (pos < s_list.length()) {
    pos_n = pos2 = s_list.find(list_delim, pos);
    if (pos2==string::npos)
      pos2 = s_list.length()-1;
    while ((pos2>0)&&
	   ((s_list[pos2] == ' ')||(s_list[pos2] == list_delim)
	    ||(s_list[pos2] == '\n')))
      pos2--;
    if (s_list.substr(pos, pos2-pos+1)==key)
      return true;
    if (pos_n == string::npos)
      return false;
    while ((pos_n<s_list.length()) && 
	   ((s_list[pos_n] == ' ')||(s_list[pos_n] == list_delim)||
	    (s_list[pos_n] == '\n')))
      pos_n++;
    if (pos_n == s_list.length())
      return false;
    pos = pos_n;
  }
  return false;
}

string strip_header_params(const string& hdr_string) 
{
  size_t val_begin = 0; // skip trailing ' '
  for (;(val_begin<hdr_string.length()) && 
	 hdr_string[val_begin]==' ';val_begin++);
  // strip parameters
  size_t val_end = hdr_string.find(';', val_begin);
  if (val_end == string::npos) 
    val_end=hdr_string.length();
  return hdr_string.substr(val_begin, val_end-val_begin);
}

string get_header_param(const string& hdr_string, 
			const string& param_name) 
{
  size_t pos = 0;
  while (pos<hdr_string.length()) {
    pos = hdr_string.find(';',pos);
    if (pos == string::npos) 
      return "";
    if ((hdr_string.length()>pos+param_name.length()+1)
	&& hdr_string.substr(++pos, param_name.length())==param_name 
	&& hdr_string[pos+param_name.length()] == '=') {
      size_t pos2 = pos+param_name.length()+1;
      while(pos2<hdr_string.length()){

	  switch(hdr_string[pos2]) {
	  case ';':
	  case '\n':
	  case '\r':
	      break;

	  default:
	      pos2++;
	      continue;
	  }

	  break;
      }
      return hdr_string.substr(pos + param_name.length() + 1, // skip 'param=' 
			       pos2 - pos - param_name.length() - 1);
    }
    pos +=param_name.length();
  }
  return "";
}

/** get the value of key @param short_name or @param name or from the list param_hdr*/
string get_header_keyvalue(const string& param_hdr, const string& short_name, const string& name) {
  string res = get_header_keyvalue(param_hdr, short_name);
  if (res.length())
    return res;

  return get_header_keyvalue(param_hdr, name);
}

/** 
 * get value from parameter header with the name @param name
 * while skipping escaped values
 */
string get_header_keyvalue(const string& param_hdr, const string& name) {
  vector <string> parts = explode(param_hdr, ",");
  vector<string>::iterator vit;
  string part;
  for ( vit=parts.begin() ; vit < parts.end(); vit++ )
  {
    part = get_header_keyvalue_single(*vit, name);
    if(!part.empty()) break;
  }
  return part;
}

string get_header_keyvalue_single(const string& param_hdr, const string& name) {
  // ugly, but we need escaping
#define ST_FINDBGN  0
#define ST_FB_ESC   1
#define ST_BEGINKEY 2
#define ST_CMPKEY   3
#define ST_FINDEQ   4
#define ST_FINDVAL  5
#define ST_VALUE    6
#define ST_VAL_ESC  7

  size_t p=0, k_begin=0, corr=0, 
    v_begin=0, v_end=0;

  char last = ' ';
  char esc_char = ' ';

  unsigned int st = ST_BEGINKEY;
  
  while (p<param_hdr.length() && !v_end) {
    char curr = param_hdr[p];
    // DBG("curr %c, st=%d, corr=%d\n", curr, st, corr);
    switch(st) {
    case ST_FINDBGN: {
      switch(curr) {
      case '"':
      case '\'':
	{
	  st = ST_FB_ESC;
	  esc_char = curr;
	} break;
      case ';':
	st = ST_BEGINKEY;
	break;
      default: break;
      } break;
    } break;

    case ST_FB_ESC: {
      if (curr == esc_char && last != '\\') {
	st = ST_FINDBGN;
      }
    } break;

    case ST_BEGINKEY: {
      switch (curr) {
      case ' ': // spaces before the key
      case '\t':
      case ';': // semicolons before the key
	break;
      default:
	if (curr==tolower(name[0]) || curr==toupper(name[0])) {
	  if (name.length() == 1)
	    st = ST_FINDEQ;
	  else
	    st = ST_CMPKEY;
	  k_begin = p;
	  corr = 1;
	} else {
	  st = ST_FINDBGN;
	}
      }
    } break;

    case ST_CMPKEY: {
	if (curr==tolower(name[corr]) || curr==toupper(name[corr])) {
	  corr++;
	  if (corr == name.length()) {
	    st = ST_FINDEQ;
	  }
	} else {
	  st = ST_FINDBGN;
	  corr=0;
	  p=k_begin; // will continue searching one after k_begin
	}
    } break;

    case ST_FINDEQ: {
      switch (curr) {
      case ' ':
      case '\t':
	break;
      case '=': 
	st = ST_FINDVAL; break;
      default: { 
	  st = ST_FINDBGN;
	  corr=0;
	  p=k_begin; // will continue searching one after k_begin
      } break;
      }
    } break;

    case ST_FINDVAL: {
      switch (curr) {
      case ' ':
      case '\t':
	break;

      case '"':
      case '\'': {
	st = ST_VAL_ESC;
	esc_char = curr;
	v_begin=p;
      } break;
      default: 
	st = ST_VALUE;
	v_begin=p;
      }
    } break;

    case ST_VALUE: {
      switch (curr) {
      case '"':
      case '\'': {
	st = ST_VAL_ESC;
	esc_char = curr;
      } break;
      case ';':
	v_end = p;
	break;
      default: break;
      }
    } break;


    case ST_VAL_ESC: {
      if (curr == esc_char && last != '\\') {
	st = ST_VALUE;
      }
    } break;
    }

    p++;
    last = curr;
  }

  if (!v_end && (st == ST_VALUE || st == ST_VAL_ESC))
    v_end = p;
  
  if (v_begin && v_end) {
    if ((v_end - v_begin > 1) &&
	(param_hdr[v_begin] == param_hdr[v_end-1]) &&
	((param_hdr[v_begin] == '\'') || (param_hdr[v_begin] == '"')))
	return param_hdr.substr(v_begin+1, v_end-v_begin-2); // whole value quoted

    return param_hdr.substr(v_begin, v_end-v_begin);
  }  else 
    return "";
}

/** get the value of key @param name from \ref PARAM_HDR header in hdrs */
string get_session_param(const string& hdrs, const string& name) {
  string iptel_app_param = getHeader(hdrs, PARAM_HDR, true);
  if (!iptel_app_param.length()) {
    //      DBG("call parameters header PARAM_HDR not found "
    // 	 "(need to configure ser's tw_append?).\n");
    return "";
  }

  return get_header_keyvalue(iptel_app_param, name);
}

void parse_app_params(const string& hdrs, map<string,string>& app_params)
{
  // TODO: use real parser with quoting and optimize
  vector<string> items = explode(getHeader(hdrs, PARAM_HDR, true), ";");
  for (vector<string>::iterator it=items.begin(); 
       it != items.end(); it++) {
    vector<string> kv = explode(*it, "=");
    if (kv.size() == 2) {
      app_params.insert(make_pair(kv[0], kv[1]));
    } else {
      if (kv.size() == 1) {
	app_params.insert(make_pair(*it, string()));
      }
    }
  }
}


// support for thread-safe pseudo-random numbers
static unsigned int _s_rand=0;
static AmMutex _s_rand_mut;

void init_random()
{
  int seed=0;
  FILE* fp_rand = fopen("/dev/urandom","r");
  if(fp_rand){
    if (fread(&seed,sizeof(int),1,fp_rand) != 1) {
      DBG("/dev/urandom could not be read, rng probably not initialized.\n");
    }
    fclose(fp_rand);
  }
  seed += getpid();
  seed += time(0);
  _s_rand = seed;
}

unsigned int get_random()
{
  _s_rand_mut.lock();
  unsigned int r = rand_r(&_s_rand);
  _s_rand_mut.unlock();
    
  return r;
}

// Explode string by a separator to a vector
// see http://stackoverflow.com/questions/236129/c-how-to-split-a-string
std::vector<string> explode(const string& s, const string& delim, 
			    const bool keep_empty) {
  vector<string> result;
  if (delim.empty()) {
    result.push_back(s);
    return result;
  }
  string::const_iterator substart = s.begin(), subend;
  while (true) {
    subend = search(substart, s.end(), delim.begin(), delim.end());
    string temp(substart, subend);
    if (keep_empty || !temp.empty()) {
      result.push_back(std::move(temp));
    }
    if (subend == s.end()) {
      break;
    }
    substart = subend + delim.size();
  }
  return result;
}



// Warning: static var is not mutexed
// Call this func only in init code.
//
void add_env_path(const char* name, const string& path)
{
  string var(path);
  char*  old_path=0;

  regex_t path_reg;

  assert(name);
  if((old_path = getenv((char*)name)) != 0) {
    if(strlen(old_path)){
	    
      if(regcomp(&path_reg,("[:|^]" + path + "[:|$]").c_str(),REG_NOSUB)){
	ERROR("could not compile regex\n");
	return;
      }
	    
      if(!regexec(&path_reg,old_path,0,0,0)) { // match
	regfree(&path_reg);
	return; // do nothing
      }
      regfree(&path_reg);
      var += ":" + string(old_path);
    }
  }

  DBG("setting %s to: '%s'\n",name,var.c_str());
#ifndef BSD_COMP
  setenv(name,var.c_str(),1);
#else
  string sol_putenv = name + string("=") + var;
  putenv(sol_putenv.c_str());
#endif
}

/** skip to the end of a string enclosed in round brackets, skipping more 
    bracketed items, too */
size_t skip_to_end_of_brackets(const string& s, size_t start) {
  size_t res = start;
  char last_c = ' ';
  int num_brackets = 0;
  while (res < s.size() &&
	 (s[res] != ')' || num_brackets || last_c == '\\')) {
    if (last_c != '\\') {
      if (s[res]==')' && num_brackets)
	num_brackets--;
      else if (s[res]=='(')
	num_brackets++;
    }
    last_c = s[res];
    res++;
  }
  return res;
}

bool read_regex_mapping(const string& fname, const char* sep,
			const char* dbg_type,
			RegexMappingVector& result) {
  std::ifstream appcfg(fname.c_str());
  if (!appcfg.good()) {
    ERROR("could not load %s file at '%s'\n",
	  dbg_type, fname.c_str());
    return false;
  } else {
    while (!appcfg.eof()) {
      string entry;
      getline (appcfg,entry);
      if (!entry.length())
	continue;
      size_t non_wsp_pos = entry.find_first_not_of(" \t");
      if (non_wsp_pos != string::npos && entry[non_wsp_pos] == '#')
	continue;

      vector<string> re_v = explode(entry, sep);
      if (re_v.size() != 2) {
	ERROR("Incorrect line '%s' in %s: expected format 'regexp%sstring'\n",
	      entry.c_str(), fname.c_str(), sep);
	return false;
      }
      regex_t app_re;
      if (regcomp(&app_re, re_v[0].c_str(), REG_EXTENDED)) {
	ERROR("compiling regex '%s' in %s.\n", 
	      re_v[0].c_str(), fname.c_str());
	return false;
      }
      DBG("adding %s '%s' => '%s'\n",
	  dbg_type, re_v[0].c_str(),re_v[1].c_str());
      result.push_back(make_pair(app_re, re_v[1]));
    }
  }
  return true;
}

void ReplaceStringInPlace(std::string& subject, const std::string& search,
                          const std::string& replace) {
  size_t pos = 0;
  while ((pos = subject.find(search, pos)) != std::string::npos) {
    subject.replace(pos, search.length(), replace);
    pos += replace.length();
  }
}

#define MAX_GROUPS 9

bool run_regex_mapping(const RegexMappingVector& mapping, const char* test_s,
                       string& result) {
  regmatch_t groups[MAX_GROUPS];
  for (RegexMappingVector::const_iterator it = mapping.begin();
       it != mapping.end(); it++) {
    if (!regexec(&it->first, test_s, MAX_GROUPS, groups, 0)) {
      result = it->second;
      string soh(1, char(1));
      ReplaceStringInPlace(result, "\\\\", soh);
      unsigned int g = 0;
      for (g = 1; g < MAX_GROUPS; g++) {
        if (groups[g].rm_so == (int)(size_t)-1) break;
        DBG("group %u: [%2u-%2u]: %.*s\n",
            g, groups[g].rm_so, groups[g].rm_eo,
            groups[g].rm_eo - groups[g].rm_so, test_s + groups[g].rm_so);
	std::string match(test_s + groups[g].rm_so,
			  groups[g].rm_eo - groups[g].rm_so);
        ReplaceStringInPlace(result, "\\" + int2str(g), match);
      }
      ReplaceStringInPlace(result, soh, "\\");
      return true;
    }
  }
  return false;
}

// These function comes basically from ser's uac module 
void cvt_hex(HASH bin, HASHHEX hex)
{
  unsigned short i;
  unsigned char j;

  for (i = 0; i<HASHLEN; i++)
    {
      j = (bin[i] >> 4) & 0xf;
      if (j <= 9)
	{
	  hex[i * 2] = (j + '0');
	} else {
	  hex[i * 2] = (j + 'a' - 10);
	}

      j = bin[i] & 0xf;

      if (j <= 9)
	{
	  hex[i * 2 + 1] = (j + '0');
	} else {
	  hex[i * 2 + 1] = (j + 'a' - 10);
	}
    };

  hex[HASHHEXLEN] = '\0';
}

/** get an MD5 hash of a string */
string calculateMD5(const string& input) {
  MD5_CTX Md5Ctx;
  HASH H;
  HASHHEX HH;

  MD5Init(&Md5Ctx);
  MD5Update(&Md5Ctx, (unsigned char*)input.c_str(), input.length());
  MD5Final(H, &Md5Ctx);
  cvt_hex(H, HH);
  return string((const char*)HH);
}
