//#include "gen_classes.h"
//#include "Regex.h"
#include "common_sip_msrp.h"
#include <iostream>
#include <fstream>

namespace t3devlib { namespace gen {

// YGA: For debug purpose
/*{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "CaptureSource::PreEncodeField: " << field_id << std::endl;
	logfile.close();
}*/

//
// Implementation of the decoder for SDP
//

// SIP quoted strings messages allow multiple ways of representing 
// some characters (raw unicode or escaped). This function is used
// to normalise the content so that two equivalent representation
// will result in the same TTCN-3 charstring.
//
void normalise_quoted_string (Charstring& cs, bool remove_quotes/* = false*/) throw (DecodeError)
{
	std::string result;

	//FIXME: how LWS shall be normalised ?
	
	const unsigned char* p   = cs.GetValueBin();
	const unsigned char* end = p + (cs.GetLength() / 8);

	if (remove_quotes)
	{
		if ((end - p) < 2)
			goto error_malformed;

		if ((*p++ != '"') | (*--end != '"'))
			goto error_malformed;
	}

	for ( ; p!=end ; p++)
	{
		switch (*p) {
		case '\r': //LWS
		case '\n':

		case ' ': //WSP
		case '\v':
		case '\t':
		case '\f':

		case 0x21: //!
			// plain text
			result += *p;
			break;

		case '\\':
			// escaped character
			p++;
			if ((p == end) || ((*p == '\r') | (*p == '\n'))) {
				// cannot be escaped
				// (should never happen since we checked it wit a regex before)
				DecodeError e (&cs);
				e.Msg() << "Invalid escaped sequence in quoted string: \\\\x" << std::hex << ((int) *p) << std::endl;
				throw e;
			}

			// valid escaped character
			result += *p;
			break;

		default:
			if ((*p >= 0x23) && (*p <= 0x7e))
			{
				// plain text
				result += *p;

			} else if (*p > 127) {
				// UTF-8 character
				//
				// FIXME: how to represent UTF-8 chars ? ('%xx' escape sequences are not used here)
				result += *p;

			} else {
				// non allowed character
				// (should never happen since we checked it wit a regex before)
				DecodeError e (&cs);
				e.Msg() << "Invalid character in quoted string: \\x" << std::hex << ((int) *p) << std::endl;
				throw e;
			}
		}
	}

	// replace the string with the quoted string
	{
		Bytestring& bs = cs;
		bs.SetValue (result);
	}
	return;

error_malformed:
	DecodeError e(&cs);
	e.Msg() << "Malformed quoted string: " << cs.GetValue() << endl;
	throw e;
}

static inline bool asciichar_is_displayable (char c)
{
	if ((c >= 32) && (c<127))
		return true;
	return (c == '\r') | (c == '\n') | (c == '\t') | (c == '%');
}

// SIP tokens allow multiple ways of representing some characters (raw
// unicode or escaped). This function is used to normalise the content so
// that two equivalent representation will result in the same TTCN-3
// charstring.
//
void normalise_escaped_string (Charstring& cs) throw (DecodeError)
{
	std::string result;

	const unsigned char* p   = cs.GetValueBin();
	const unsigned char* end = p + (cs.GetLength() / 8);

	for ( ; p!=end ; p++)
	{
		unsigned char c;

		if (*p == '%') {
			// escaped char %xx

			if ((end - p) < 3)
				goto error_malformed;

			char buff[3] = { p[1], p[2], '\0'};
			p += 2;

			char* next;
			c = strtol(buff, &next, 16);

			if (next != &buff[2])
				goto error_malformed;
			//TODO: check that the result is UTF-8 valid ?
		} else {
			c = *p;
		}

		if (asciichar_is_displayable(c))
		{
			// 7-bit character
			result += c;
		} else {
			// 8-bit character and control characters
			// -> escape it
			char buff[4];
			sprintf (buff, "%%%02x", c);
			result += buff;
		}
	}

	// replace the string with the quoted string
	{
		Bytestring& bs = cs;
		bs.SetValue (result);
	}
	return;

error_malformed:
	DecodeError e(&cs);
	e.Msg() << "Malformed string: " << cs.GetValue() << endl;
	throw e;
}

// move past the possible leading linear whitespaces in the buffer
void remove_whitespace (Buffer & buffer) {
	static Regex reg_ws ("^" SIPREG_LWS);
	if (reg_ws.Match (buffer)) {
		int nPos = buffer.GetPosition() + reg_ws.GetMatchedLength();
		buffer.SetPosition (nPos);
	}
}

void read_sp (Buffer & buffer, Variable* v) {
	static Regex reg_ws ("^[ \t]+");
	reg_ws.AssertMatch (buffer, v);
	int nPos = buffer.GetPosition() + reg_ws.GetMatchedLength();
	buffer.SetPosition (nPos);
}

bool is_sip_scheme (const char * pszScheme) {
	return strncasecmp(pszScheme, "sip", 3) == 0 || strncasecmp(pszScheme, "sips", 4) == 0;
}

bool is_tel_scheme (const char * pszScheme) {
	return strncasecmp(pszScheme, "tel", 3) == 0 || strncasecmp(pszScheme, "fax", 3) == 0 ||
		strncasecmp(pszScheme, "modem", 5) == 0;
}



// Definition of character classes used to be used in the regular expressions
// (within [])
#define SIPCHARS_MARK		"\\-_.!~*'()"
#define SIPCHARS_UNRESERVED	SIPCHARS_ALFANUM SIPCHARS_MARK
#define SIPCHARS_RESERVED	";/?:@&=+$,"
#define SIPCHARS_USER_UNRESERVED "&=+$,;?/"
#define SIPCHARS_TEXT_UTF8CHAR	"\x21-\xFD"


// Definition of common regular expression reused many times within the
// decoder
//
// Note: these definitions shall not contain any group match '(' ')' to avoid
// side effect (the index of the subsequent group match would be shifted)
// -> all parenthesis group needed should be implemented with '(?:' ')' so
//    that they are not indexed
//
#define SIPREG_ESCAPED	"(%[0-9A-Fa-f]{2})"
#define SIPREG_TOKEN "[" SIPCHARS_ALFANUM ".!%*_+`'~\\-]+"
#define SIPREG_TOKEN_NODOT "[" SIPCHARS_ALFANUM "!%*_+`'~\\-]+"
#define SIPREG_WORD "(?:[][" SIPCHARS_ALFANUM "\\-.!%*_+`'~()<>:\\\\\"/?{}])+"
#define SIPREG_ASCII_WITHOUT_COMMA "[\\x21-\\x2B\\x2D-\\x7E]+"
#define SIPREG_TEXT_UTF8_TRIM	"[\x21-\xFD]([\x21-\xFD]|(" SIPREG_LWS "))*"

// sip version
#define SIPREG_SIP_VERSION "SIP/[0-9]\\.[0-9]"

// header name
#define SIPREG_HNAME "(?:[][/?:+$" SIPCHARS_UNRESERVED "]|" SIPREG_ESCAPED ")+"


// without leading and trailing whitespace
#define SIPREG_DISPLAY_NAME "((" SIPREG_TOKEN "(" SIPREG_LWS SIPREG_TOKEN ")*)|(" SIPREG_QUOTED_STRING "))"

#define SIPREG_COMMENT	"[(]((" SIPREG_LWS ")|[\\x21-\\x5B]|[\\x5D-\\xFD]|(" SIPREG_QUOTED_PAIR "))*[)]"

// IPv4
//#define SIPREG_IP4	"([0-9]{1,3}\\.){3}[0-9]{1,3}"

// IPv6
//#define SIPREG_HEX4		"[" SIPCHARS_HEXA "]{1,4}"
//#define SIPREG_HEXSEQ		SIPREG_HEX4 "([:]" SIPREG_HEX4 ")*"
//#define SIPREG_HEXPART	"(((" SIPREG_HEXSEQ ")?[:]{2}(" SIPREG_HEXSEQ ")?)|(" SIPREG_HEXSEQ  "))"
//#define SIPREG_IP6		"[[]" SIPREG_HEXPART "([:]" SIPREG_IP4 ")?[]]"

// host
#define SIPREG_HOST		"((" SIPREG_HOSTNAME ")|(" SIPREG_IP4 ")|(" SIPREG_IP6 "))"
#define SIPREG_ABSOLUTE_URI	"([" SIPCHARS_UNRESERVED "/;?:@&=+$,]|" SIPREG_ESCAPED ")+"

// phone number (global or local)
#define SIPREG_PHONE_NUMBER "(([+][\\-0-9.()]+)|[\\-0-9()*#A-Da-dPpWw]+)"

#define SIPCHARS_PPARAM_UNRESERVED "][/:&+$"
#define SIPREG_PPARAM		"([" SIPCHARS_PPARAM_UNRESERVED SIPCHARS_UNRESERVED "]|" SIPREG_ESCAPED ")"
#define SIPREG_PPARAM_ALLOWED	"[" SIPCHARS_PPARAM_UNRESERVED SIPCHARS_UNRESERVED ";=%]+"

#define SIPCHARS_HPARAM_UNRESERVED "][/?:+$"
#define SIPREG_HPARAM		"([" SIPCHARS_HPARAM_UNRESERVED SIPCHARS_UNRESERVED "]|" SIPREG_ESCAPED ")"
#define SIPREG_HPARAM_ALLOWED	"[" SIPCHARS_HPARAM_UNRESERVED SIPCHARS_UNRESERVED ";=%]+"

#define SIPREG_TELPARAM_NAME	"[!#$%&'*+\\-.0-9A-Z^_`a-z|~]+"
#define SIPREG_TELPARAM_VALUE	"([\\x21\\x23-\\x3A\\x3C-\\x7E]+|(" SIPREG_QUOTED_STRING "))"
#define SIPREG_TELPARAMS		"([;]" SIPREG_TELPARAM_NAME "([=]" SIPREG_TELPARAM_VALUE ")?)+"

#define SIPREG_GPARAM_VALUE "(([" SIPCHARS_ALFANUM ".!%*_+`'~\\-:]+)|(" SIPREG_QUOTED_STRING "))"

// content type parameter
#define SIPREG_M_PARAMETER	SIPREG_TOKEN SIPREG_EQUAL "(?:" SIPREG_TOKEN "|" SIPREG_QUOTED_STRING ")"


bool detect_separator(Regex & reg_separator, Buffer & buffer)
{
	bool bRes;
	if (bRes = reg_separator.Match(buffer))
		reg_separator.MovePast(buffer);
	return bRes;
}
bool detect_comma(Buffer & buffer)
{
	Regex reg_comma ("^" SIPREG_COMMA);
	return detect_separator(reg_comma, buffer);
}

bool detect_semi(Buffer & buffer) throw (DecodeError)
{
	Regex reg_semi ("^" SIPREG_SEMI);
	return detect_separator(reg_semi, buffer);
}

void SipUrl::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_urlParameters && IsPresent(id_urlParameters)) {
		csSemi.Encode(buffer);
	}
}

void SipUrl::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csColon;
	csColon.SetValue(":");

	switch(field_id) {
	case id_scheme:
		csColon.Encode(buffer);
		break;
	default:
		break;
	}
}

void SipUrl::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "SipUrl::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_scheme ("^[" SIPCHARS_ALFA "][" SIPCHARS_ALFANUM "+.\\-]*");
	static Regex reg_colon ("^[:]");
	static Regex reg_userinfo ("^(?:[" SIPCHARS_UNRESERVED SIPCHARS_USER_UNRESERVED "]|" SIPREG_ESCAPED ")+(?::(?:[" SIPCHARS_UNRESERVED "&=+$,]|"SIPREG_ESCAPED")*)?[@]");
	static Regex reg_phone ("^" SIPREG_PHONE_NUMBER);
	static Regex reg_hostport ("^[][" SIPCHARS_ALFANUM ":.\\-]+");
	static Regex reg_absolute_uri ("^" SIPREG_ABSOLUTE_URI);
	static Regex reg_urlParams ("^;" SIPREG_PPARAM_ALLOWED);	
	static Regex reg_headers ("^[?]" SIPREG_PPARAM_ALLOWED);
	static Regex reg_telParams ("^" SIPREG_TELPARAMS);

	const char * pszScheme;
	switch (id) {
	case id_scheme:
		reg_scheme.AssertMatch (buffer, this);
		SetHypFieldLength (id, reg_scheme.GetMatchedLength());
		break;

	case id_userInfo:
		reg_colon.AssertMatch(buffer, this);
		buffer.SetPosition(buffer.GetPosition() + 8);
		pszScheme = Get_scheme().GetValue();
		SetHypFieldIsPresent (id,  0);
		// user-info is not decoded in case of absoluteURI
		if (is_sip_scheme(pszScheme)) {
			if (reg_userinfo.Match (buffer)) {
				SetHypFieldIsPresent (id,  1);
				SetHypFieldLength (id, reg_userinfo.GetMatchedLength() - 8);
			}
		} 
		// telephone numbers are decoded to the userInfo field
		else if (is_tel_scheme(pszScheme)){
			reg_phone.AssertMatch(buffer, this);
			SetHypFieldIsPresent (id,  1);
			SetHypFieldLength (id, reg_phone.GetMatchedLength());
		}
		else { // absoluteURI
			if (reg_absolute_uri.Match (buffer)) {
				SetHypFieldIsPresent (id,  1);
				SetHypFieldLength(id, reg_absolute_uri.GetMatchedLength());
			}
		}
		break;

	case id_hostPort:
		pszScheme = Get_scheme().GetValue();
		if (is_sip_scheme(pszScheme)) {
			// remove '@'
			if (IsPresent (id_userInfo)) {
				buffer.SetPosition(buffer.GetPosition() + 8);
			}
			if (reg_hostport.Match (buffer)) {
				SetHypFieldIsPresent (id,  1);
				SetHypFieldLength(id, reg_hostport.GetMatchedLength());
			} else if (IsPresent (id_userInfo)) {
				reg_hostport.Error(this, buffer);
			} else {
				SetHypFieldIsPresent (id,  0);
			}
		} else { // tel or absoluteURI
			SetHypFieldIsPresent(id, 0);
		}
		break;

	case id_urlParameters:
		pszScheme = Get_scheme().GetValue();
		if (is_sip_scheme(pszScheme) &&	reg_urlParams.Match (buffer)){
			SetHypFieldIsPresent (id,  1);
			SetHypFieldLength(id, reg_urlParams.GetMatchedLength());
		} else if (is_tel_scheme(pszScheme)) {
			// "tel" parameters have a different syntax (e.g. & is allowed within both id and values)
			if (reg_telParams.Match (buffer)) {
				SetHypFieldIsPresent (id,  1);
				SetHypFieldLength(id, reg_telParams.GetMatchedLength());
			} else {
				SetHypFieldIsPresent(id, 0);
			}
		}
		else {
			SetHypFieldIsPresent(id, 0);
		}
		break;
	case id_headers:
		pszScheme = Get_scheme().GetValue();
		if (is_sip_scheme(pszScheme) && reg_headers.Match (buffer)){
			SetHypFieldIsPresent (id,  1);
			SetHypFieldLength(id, reg_headers.GetMatchedLength());
		} else {
			SetHypFieldIsPresent(id, 0);
		}
		break;
	}
}

void UserInfo::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csColon;
	csColon.SetValue(":");
	
	switch(field_id) {
	case id_password:
		csColon.Encode(buffer);
		break;
	default:
		break;
	}
}

void UserInfo::PostEncode (Buffer& buffer) throw (EncodeError)
{
	Charstring csAt;
	csAt.SetValue("@");

	csAt.Encode(buffer);
}

void UserInfo::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "UserInfo::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_username ("^([" SIPCHARS_UNRESERVED SIPCHARS_USER_UNRESERVED "]|" SIPREG_ESCAPED ")+");
	static Regex reg_colon ("^[:]");
	static Regex reg_password ("^([&=+$," SIPCHARS_UNRESERVED "]|" SIPREG_ESCAPED ")*");
	static Regex reg_absolute_uri ("^" SIPREG_ABSOLUTE_URI);

	// absoluteURI is mapped into SipUrl.userInfo.userOrTelephoneSubscriber and requires special handling
	Variable* parent = GetParent();
	bool bRequestUri = false;
	if (parent != NULL) {
		const char * pszParName = parent->GetTypeName();
		if (strcmp(pszParName, "SipUrl") == 0) {
			SipUrl * pSipUrl = dynamic_cast<SipUrl*>(parent);
			const char * pszScheme = pSipUrl->Get_scheme().GetValue();
			bRequestUri = !is_sip_scheme(pszScheme);
		}
	}
	Regex * pRegex;
	switch (id) {
	case id_userOrTelephoneSubscriber:
		pRegex = bRequestUri ? &reg_absolute_uri : &reg_username;
		pRegex->AssertMatch (buffer, this);
		SetHypFieldLength(id, pRegex->GetMatchedLength());
		break;
	case id_password:
		if(!bRequestUri && reg_colon.Match(buffer)) {
			buffer.SetPosition(buffer.GetPosition() + 8);
			SetHypFieldIsPresent (id, 1);
			reg_password.AssertMatch (buffer, this);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
		break;
	}
}

void UserInfo::PostDecode (Buffer& buffer) throw (DecodeError)
{
	if (IsPresent (id_userOrTelephoneSubscriber))
		normalise_escaped_string (Get_userOrTelephoneSubscriber());
	if (IsPresent (id_password))
		normalise_escaped_string (Get_password());
}

void HostPort::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csColon;
	csColon.SetValue(":");

	switch(field_id) {
	case id_portField:
		if(IsPresent(field_id)) {
			Get_portField().SetFormat(Integer::AsciiDecimal);
			csColon.Encode(buffer);
		}
		break;
	default:
		break;
	}
}

void HostPort::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "HostPort::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_host ("^" SIPREG_HOST);
	static Regex reg_colon ("^:");

	switch (id) {
	case id_host:
		// host is always present
		SetHypFieldIsPresent(id, 1);
		reg_host.AssertMatch (buffer, this);
		SetHypFieldLength(id, reg_host.GetMatchedLength());
		break;

	case id_portField:
		if(reg_colon.Match (buffer)) {
			buffer.SetPosition(buffer.GetPosition() + 8);
			SetHypFieldIsPresent (id, 1);
			Get_portField().SetFormat(Integer::AsciiDecimal);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
		break;
	}
}

void SemicolonParam_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id != 0) {
		csSemi.Encode(buffer);
	}
}

void SemicolonParam_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "SemicolonParam_List::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_separator ("^" SIPREG_SEMI);
	if (reg_separator.Match(buffer)) // the separator can be in the beginning
		reg_separator.MovePast(buffer);
}

void SemicolonParam_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_semi (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void AmpersandParam_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csAmpersand, csQuestion;
	csAmpersand.SetValue("&");
	csQuestion.SetValue("?");

	if(field_id == 0) {
		csQuestion.Encode(buffer);
	}
	else {		
		csAmpersand.Encode(buffer);
	}
}

void AmpersandParam_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "AmpersandParam_List::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_start ("^[?]");
	static Regex reg_separator ("^[&]");

	if (!buffer.GetBitsLeft())
		return;
	if (GetSize() == 0){
		reg_start.AssertMatch(buffer, this);
		buffer.SetPosition(buffer.GetPosition() + 8);
	}
	else if (reg_separator.Match(buffer))
		buffer.SetPosition(buffer.GetPosition() + 8);
	else
		SetHypSize(-2);
}

void CommaParam_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void CommaParam_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "CommaParam_List::PreDecode" << std::endl;
	logfile.close();

	Variable* parent = GetParent();
	if (parent != NULL) {		
		const char * pszParName = parent->GetTypeName();
		if (strcmp(pszParName, "Credentials") == 0 ||
			strcmp(pszParName, "AuthenticationInfo") == 0){
			SetHypSize (GetSize() + 1);
			SetHypAppend (1);
		}
	}
}

void CommaParam_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "CommaParam_List::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	if (GetSize() > 0 && !reg_content.Match(buffer)) {
		SetHypSize(-2);	
	}
}

void CommaParam_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	if (detect_comma (buffer) && reg_content.Match (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}


void GenericParam::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csEqual;
	csEqual.SetValue("=");

	switch(field_id) {
	case id_paramValue:
		if(IsPresent(field_id)) {
			csEqual.Encode(buffer);
		}
		break;
	default:
		break;
	}
}

void GenericParam::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "GenericParam::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_equal ("^" SIPREG_EQUAL);

	static Regex reg_pparname ("^" SIPREG_PPARAM "+");
	static Regex reg_pparvalue ("^" SIPREG_PPARAM "*");

	static Regex reg_hparname ("^" SIPREG_HPARAM "+");
	static Regex reg_hparvalue ("^" SIPREG_HPARAM "*");

	static Regex reg_telparname ("^" SIPREG_TELPARAM_NAME);
	static Regex reg_telparvalue ("^" SIPREG_TELPARAM_VALUE);

	static Regex reg_gparname ("^" SIPREG_TOKEN);
	static Regex reg_gparvalue ("^" SIPREG_GPARAM_VALUE);

	Regex * preg_name = NULL;
	Regex * preg_value;
	bool bMandatoryParam = false;

	Variable* parent = GetParent();
	if (parent == NULL)
		throw DecodeError (this, "Parent type cannot be null\n");
	const char * pszParName = parent->GetTypeName();
	if (strcmp(pszParName, "SemicolonParam_List") == 0){
		parent = parent->GetParent();
		if (parent != NULL && strcmp (parent->GetTypeName(), "SipUrl") == 0) {
			SipUrl * pSipUrl = dynamic_cast<SipUrl*>(parent);
			const char * pszScheme = pSipUrl->Get_scheme().GetValue();
			if (is_sip_scheme(pszScheme)) {
				preg_name = &reg_pparname;
				preg_value = &reg_pparvalue;
			}
			else if (is_tel_scheme(pszScheme)) {
				preg_name = &reg_telparname;
				preg_value = &reg_telparvalue;
			}
		}
		if (preg_name == NULL) {
			preg_name = &reg_gparname;
			preg_value = &reg_gparvalue;
		}
	}
	else if (strcmp(pszParName, "AmpersandParam_List") == 0){
		preg_name = &reg_hparname;
		preg_value = &reg_hparvalue;
		bMandatoryParam = true;
	}
	else if (strcmp(pszParName, "CommaParam_List") == 0){
		preg_name = &reg_gparname;
		preg_value = &reg_gparvalue;
	}
	else {
		std::string message ("Unexpected parent type of parameter record: '");
		message += pszParName;
		message += '\n';
		throw DecodeError (this, message);
	}
	
	char c;
	switch (id) {
	case id_id:
		preg_name->AssertMatch (buffer, this);
		SetHypFieldLength (id, preg_name->GetMatchedLength());
		break;
	case id_paramValue:
		if (bMandatoryParam)
			reg_equal.AssertMatch(buffer, this);
		if(bMandatoryParam || (buffer.GetBitsLeft() && reg_equal.Match(buffer))) {
			buffer.SetPosition(buffer.GetPosition() + reg_equal.GetMatchedLength());
			preg_value->AssertMatch (buffer, this);
			SetHypFieldIsPresent (id, 1);
			SetHypFieldLength (id, preg_value->GetMatchedLength());
		} else {
			SetHypFieldIsPresent (id, 0);
		}
		break;
	}
}


void RequestLine::PostEncode (Buffer& buffer) throw (EncodeError)
{
	Charstring cs;

	cs.SetValue("\r\n");
	cs.Encode(buffer);
}

void RequestLine::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring cs;

	cs.SetValue(" ");

	switch(field_id) {
	case id_method:
	case id_requestUri:
		cs.Encode(buffer);
		break;
	default:
		break;
	}
}

void GenericParam::PostDecode (Buffer& buffer) throw (DecodeError)
{
	Variable* param_list = GetParent();
	if (!param_list)
		return;
	
	Variable* parent = param_list->GetParent();
	if (!parent)
		return;
	const char* parent_type = parent->GetTypeName();

	if (strcmp (parent_type, "SipUrl") == 0) {
		normalise_escaped_string (Get_id());
	}

	if (IsPresent (id_paramValue))
	{
		Charstring& value = Get_paramValue();

		if (value.GetLength() &&
		    (*value.GetValueBin() == '"')) {
			normalise_quoted_string (value, true);
		} else {
			const char* par_name = Get_id().GetValue();
			
			//TODO: add other unescaped params
			if (strcmp (parent_type, "ViaBody") == 0) {
				if ((strcmp (par_name, "branch") == 0)
				 || (strcmp (par_name, "ttl") == 0)
				 || (strcmp (par_name, "maddr") == 0)
				 || (strcmp (par_name, "received") == 0))
					goto skip_escape;
			} else if (strcmp (parent_type, "From") == 0) {
				if (strcmp (par_name, "tag") == 0)
					goto skip_escape;
			}
			else if (strcmp (parent_type, "PChargingVector") == 0) {
			  if (strcmp (par_name, "icid-value") == 0)
					goto skip_escape;
			}
do_escape:
			normalise_escaped_string (value);
skip_escape: ;

		}
	}
}

void RequestLine::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "RequestLine::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_method ("^" SIPREG_TOKEN);
	static Regex reg_request_uri ("[^ \t\n\r]+");
	static Regex reg_sip_version (SIPREG_SIP_VERSION);
	switch (id) {
		case id_method:
			reg_method.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_method.GetMatchedLength());
			break;
		case id_requestUri:
			read_sp (buffer, this);
			reg_request_uri.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_request_uri.GetMatchedLength());
			break;
		case id_sipVersion:
			read_sp (buffer, this);
			reg_sip_version.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_sip_version.GetMatchedLength());
			break;
	}
}

void RequestLine::PostDecode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_crlf ("^\r\n");

	reg_crlf.AssertMatch (buffer, this);
	buffer.SetPosition(buffer.GetPosition() + reg_crlf.GetMatchedLength());
}

const char* Method::msSipMethods[] = { 
	"ACK_E",
	"BYE_E",
	"CANCEL_E",
	"INVITE_E",
	"OPTIONS_E",
	"REGISTER_E",
	"PRACK_E",
	"SUBSCRIBE_E", 
	"NOTIFY_E",
	"PUBLISH_E",
	"REFER_E",
	"UPDATE_E",
	"MESSAGE_E",
	"INFO_E",
	"UNKNOWN_METHOD_E"
	, "" };

const char* Method::msMethodValues[] = { 
	"ACK",
	"BYE",
	"CANCEL",
	"INVITE",
	"OPTIONS",
	"REGISTER",
	"PRACK",
	"SUBSCRIBE", 
	"NOTIFY",
	"PUBLISH",
	"REFER",
	"UPDATE",
	"MESSAGE",
	"INFO"
	"UNKNOWN_METHOD"
	, "" };

void Method::Encode (Buffer& buffer) throw (EncodeError)
{
	Charstring c;
	const char ** ppMethod = msSipMethods;
	const std::string & val = GetValueString();

	int i = 0;
	while (*(ppMethod[i]) && strcmp(ppMethod[i], val.c_str()) != 0)
		i++;

	if (*(ppMethod[i]) == 0) {
		std::string message ("unsupported enum value '");
		message += val;
		message += '\n';
		throw EncodeError (this, message);
	}

	c.SetValue(msMethodValues[i]);
	c.Encode(buffer);
}

void Method::Decode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_method ("^" SIPREG_TOKEN);

	reg_method.AssertMatch (buffer, this);

	const char ** ppValue = msMethodValues;
	const std::string & val = reg_method.GetMatchedString();

	int i = 0;
	while (*(ppValue[i]) && strcmp(ppValue[i], val.c_str()) != 0) // case sensitive!!!
		i++;

	if (*(ppValue[i]) == 0) {
		SetValueString ("UNKNOWN_METHOD_E");
	} else {
		SetValueString (msSipMethods[i]);
	}
	buffer.SetPosition(buffer.GetPosition() + reg_method.GetMatchedLength());
}

void StatusLine::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS;
	csWS.SetValue(" ");

	switch(field_id) {
	case id_statusCode:
		Get_statusCode().SetFormat(Integer::AsciiDecimal);
	case id_reasonPhrase:
		csWS.Encode(buffer);
		break;
	default:
		break;
	}
}

void StatusLine::PostEncode (Buffer& buffer) throw (EncodeError)
{
	Charstring csCRLF;
	csCRLF.SetValue("\r\n");

	csCRLF.Encode(buffer);
}

void StatusLine::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "StatusLine::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_sip_version (SIPREG_SIP_VERSION);
	static Regex reg_status_code ("^[0-9]{3}");
	static Regex reg_phrase ("([" SIPCHARS_RESERVED SIPCHARS_UNRESERVED SIPCHARS_UTF8_NONASCII " \t]|" SIPREG_ESCAPED ")*");

	switch (id) {
		case id_sipVersion:
			reg_sip_version.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_sip_version.GetMatchedLength());
			break;
		case id_statusCode:
			read_sp (buffer, this);
			reg_status_code.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_status_code.GetMatchedLength());
			Get_statusCode().SetFormat(Integer::AsciiDecimal);
			break;
		case id_reasonPhrase:
			read_sp (buffer, this);
			reg_phrase.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_phrase.GetMatchedLength());			
			break;
	}
}

void StatusLine::PostDecode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_crlf ("^\r\n");

	reg_crlf.AssertMatch(buffer, this);
	reg_crlf.MovePast(buffer);

	normalise_escaped_string (Get_reasonPhrase());
}

class SipHeaderMap {
public:
	struct Entry {
		Entry (const char* name, const char* abbrev, int id_msg_hdr, const char* id_fdn)
		 : mName (name), mAbbrev (abbrev), mIdMessageHeader (id_msg_hdr), mIdFieldName (id_fdn)
		{}
		const std::string	mName;
		const std::string	mAbbrev;
		const int		mIdMessageHeader;
		const std::string	mIdFieldName;
	};

	static const Entry& GetByName (const std::string& key)
	{
		const mMapName_t& m = msInstance.mMapName;
		mMapName_t::const_iterator it = m.find (key);
		if (it != m.end()) {
			return *it->second;
		} else {
			return *msInstance.mUndef;
		}
	}
	
	static const Entry& GetByIdFieldName (const std::string& key)
	{
		const std::map<std::string, Entry*>& m = msInstance.mMapIdFieldName;
		std::map <std::string, Entry*>::const_iterator it = m.find (key);
		if (it != m.end()) {
			return *it->second;
		} else {
			return *msInstance.mUndef;
		}
	}
	
	static const Entry& GetByIdMessageHeader (int key)
	{
		const std::map<int, Entry*>& m = msInstance.mMapIdMessageHeader;
		std::map <int, Entry*>::const_iterator it = m.find (key);
		if (it != m.end()) {
			return *it->second;
		} else {
			return *msInstance.mUndef;
		}
	}


private:
	void AddEntry (const Entry& entry) {
		mEntries.push_back(entry);
		Entry& e = *mEntries.rbegin();
		
		//TODO: check unicity
		mMapName[e.mName] = &e;
		mMapName[e.mAbbrev] = &e;
		mMapIdMessageHeader[e.mIdMessageHeader] = &e;
		mMapIdFieldName[e.mIdFieldName] = &e;
	}

	SipHeaderMap() {

#define SIP_HEADER_ADD(name, abbr, msghdr, fdname)	AddEntry (Entry (#name, #abbr, MessageHeader::id_ ## msghdr, #fdname));

		//		Name		Abbrev	MessageHeader	FieldName
		//					field id	field id
		SIP_HEADER_ADD (From,		f,	fromField,	FROM_E);
		SIP_HEADER_ADD (Via,		v,	via,		VIA_E);
		SIP_HEADER_ADD (Accept,		 ,	accept,		ACCEPT_E);
		SIP_HEADER_ADD (Call-ID,	i,	callId,		CALL_ID_E);
		SIP_HEADER_ADD (CSeq,		 ,	cSeq,		CSEQ_E);
		SIP_HEADER_ADD (Content-Length,	l,	contentLength,	CONTENT_LENGTH_E);
		SIP_HEADER_ADD (Content-Type,	c,	contentType,	CONTENT_TYPE_E);
		SIP_HEADER_ADD (Contact,	m,	contact,		CONTACT_E);
		SIP_HEADER_ADD (To,			t,	toField,		TO_E);
		SIP_HEADER_ADD (Accept-Encoding,	,	acceptEncoding,	ACCEPT_ENCODING_E);
		SIP_HEADER_ADD (Accept-Language,	,	acceptLanguage,	ACCEPT_LANGUAGE_E);
		SIP_HEADER_ADD (Max-Forwards,	,	maxForwards,	MAX_FORWARDS_E);
		SIP_HEADER_ADD (Alert-Info,		,	alertInfo,	ALERT_INFO_E);
		SIP_HEADER_ADD (Require,	,	require,	REQUIRE_E);
		SIP_HEADER_ADD (Proxy-Require,	,	proxyRequire,	PROXY_REQUIRE_E);
		SIP_HEADER_ADD (Record-Route,	,	recordRoute,	RECORD_ROUTE_E);
		SIP_HEADER_ADD (Allow,		 ,	allow,		ALLOW_E);
		SIP_HEADER_ADD (Authentication-Info,	,	authenticationInfo,	AUTHENTICATION_INFO_E);
		SIP_HEADER_ADD (Authorization,	,	authorization,	AUTHORIZATION_E);
		SIP_HEADER_ADD (Call-Info,		,	callInfo,		CALL_INFO_E);
		SIP_HEADER_ADD (Content-Disposition,	,	contentDisposition,	CONTENT_DISPOSITION_E);
		SIP_HEADER_ADD (Content-Encoding,	e,	contentEncoding,	CONTENT_ENCODING_E);
		SIP_HEADER_ADD (Content-Language,	,	contentLanguage,	CONTENT_LANGUAGE_E);
		SIP_HEADER_ADD (Date,		 ,	date,		DATE_E);
		SIP_HEADER_ADD (Error-Info,		,	errorInfo,	ERROR_INFO_E);
		SIP_HEADER_ADD (Expires,	,	expires,	EXPIRES_E);
		SIP_HEADER_ADD (In-Reply-To,	,	inReplyTo,	IN_REPLY_TO_E);
		SIP_HEADER_ADD (MIME-Version,	,	mimeVersion,	MIME_VERSION_E);
		SIP_HEADER_ADD (Min-Expires,	,	minExpires,	MIN_EXPIRES_E);
		SIP_HEADER_ADD (Organization,	,	organization,	ORGANIZATION_E);
		SIP_HEADER_ADD (Priority,	,	priority,	PRIORITY_E);
		SIP_HEADER_ADD (Proxy-Authenticate,		,	proxyAuthenticate,	PROXY_AUTHENTICATE_E);
		SIP_HEADER_ADD (Proxy-Authorization,	,	proxyAuthorization,	PROXY_AUTHORIZATION_E);
		SIP_HEADER_ADD (Reply-To,	,	replyTo,	REPLY_TO_E);
		SIP_HEADER_ADD (Retry-After,	,	retryAfter,	RETRY_AFTER_E);
		SIP_HEADER_ADD (Route,		,	route,		ROUTE_E);
		SIP_HEADER_ADD (Server,		,	server,		SERVER_E);
		SIP_HEADER_ADD (Supported,	,	supported,	SUPPORTED_E);
		SIP_HEADER_ADD (Subject,	s,	subject,	SUBJECT_E);
		SIP_HEADER_ADD (Timestamp,	,	timestamp,	TIMESTAMP_E);
		SIP_HEADER_ADD (Unsupported,	,	unsupported,	UNSUPPORTED_E);
		SIP_HEADER_ADD (User-Agent,	,	userAgent,	USER_AGENT_E);
		SIP_HEADER_ADD (Warning,	,	warning,	WARNING_E);
		SIP_HEADER_ADD (WWW-Authenticate,		,	wwwAuthenticate,	WWW_AUTHENTICATE_E);
		SIP_HEADER_ADD (RSeq,		,	rSeq,		RSEQ_E);
		SIP_HEADER_ADD (RAck,		,	rAck,		RACK_E);
		SIP_HEADER_ADD (Allow-Events,	u,	allowEvents,	ALLOW_EVENTS_E);
		SIP_HEADER_ADD (Event,		o,	event,		EVENT_E);
		SIP_HEADER_ADD (Subscription-State,		,	subscriptionState,	SUBSCRIPTION_STATE_E);
		SIP_HEADER_ADD (P-Media-Authorization,	,	pMediaAuthorization,	P_MEDIA_AUTHORIZATION_E);
		SIP_HEADER_ADD (Privacy,	,	privacy,	PRIVACY_E);
		SIP_HEADER_ADD (P-Asserted-Identity,	,	pAssertedID,	P_ASSERTED_ID_E);
		SIP_HEADER_ADD (P-Preferred-Identity,	,	pPreferredID,	P_PREFERRED_ID_E);
		SIP_HEADER_ADD (Reason,		,	reason,		REASON_E);
		SIP_HEADER_ADD (Path,		,	path,		PATH_E);
		SIP_HEADER_ADD (Security-Client,		,	securityClient,		SECURITY_CLIENT_E);
		SIP_HEADER_ADD (Security-Server,		,	securityServer,		SECURITY_SERVER_E);
		SIP_HEADER_ADD (Security-Verify,		,	securityVerify,		SECURITY_VERIFY_E);
		SIP_HEADER_ADD (P-Associated-URI,		,	pAssociatedURI,		P_ASSOCIATED_URI_E);
		SIP_HEADER_ADD (P-Called-Party-ID,		,	pCalledPartyID,		P_CALLED_PARTY_E);
		SIP_HEADER_ADD (P-Visited-Network-ID,	,	pVisitedNetworkID,	P_VISITED_NETWORK_E);
		SIP_HEADER_ADD (P-Access-Network-Info,	,	pAccessNetworkInfo,	P_ACCESS_NETWORK_INFO_E);
		SIP_HEADER_ADD (P-Charging-Function-Addresses,	,	pChargingFunctionAddresses,		P_CHARGING_FUNCTION_ADDRESSES_E);
		SIP_HEADER_ADD (P-Charging-Vector,		,	pChargingVector,	P_CHARGING_VECTOR_E);
		SIP_HEADER_ADD (Refer-To,	r,	referTo,	REFER_TO_E);
		SIP_HEADER_ADD (Refer-Sub,	r,	referSub,	REFER_SUB_E);
		SIP_HEADER_ADD (Replaces,		,	replaces,	REPLACES_E);
		SIP_HEADER_ADD (Service-Route,			,	serviceRoute,		SERVICE_ROUTE_E);
		SIP_HEADER_ADD (Accept-Contact,			a,	acceptContact,		ACCEPT_CONTACT_E);
		SIP_HEADER_ADD (Referred-By,	b,	referredBy,	REFERRED_BY_E);
		SIP_HEADER_ADD (Session-Expires,		,	sessionExpires,		SESSION_EXPIRES_E);
		SIP_HEADER_ADD (Min-SE,		,	minSE,		MIN_SE_E);
		SIP_HEADER_ADD (History-Info,			,	historyInfo,		HISTORY_INFO_E);
		SIP_HEADER_ADD (P-Early-Media,			,	pEarlyMedia,		P_EARLY_MEDIA_E);
		SIP_HEADER_ADD (UserToUser,		,	userToUser,	USER_TO_USER_E);
		SIP_HEADER_ADD (P-Asserted-Service,		,	pAssertedService,		P_ASSERTED_SERVICE_E);		
		{
			mEntries.push_back(Entry("", "", MessageHeader::id_undefinedHeader_List, ""));
			Entry& e = *mEntries.rbegin();
			mMapIdMessageHeader[e.mIdMessageHeader] = &e;
			mUndef = &e;
		}
	}

	static SipHeaderMap		msInstance;

	std::list<Entry>		mEntries;
	Entry*				mUndef;

	class StringCaseInsensitiveComparator
	{
	public:
		bool operator() (const std::string& a, const std::string& b) const
		{
			return (strcasecmp (a.c_str(), b.c_str()) < 0);
		}
	};

	typedef std::map <std::string, Entry*, StringCaseInsensitiveComparator>	mMapName_t;
	mMapName_t			mMapName;
	std::map <std::string, Entry*>	mMapIdFieldName;
	std::map <int, Entry*>		mMapIdMessageHeader;

};

SipHeaderMap SipHeaderMap::msInstance;

void MessageHeader::PostEncode (Buffer& buffer) throw (EncodeError) 
{
 	Charstring csCRLF;
	csCRLF.SetValue("\r\n");

	csCRLF.Encode(buffer);	
}

void MessageHeader::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError) 
{
 	Charstring csCRLF;
	csCRLF.SetValue("\r\n");

	if(IsPresent(field_id) && field_id != id_undefinedHeader_List) {
		csCRLF.Encode(buffer);
	}
}

void MessageHeader::PreDecodeField (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_header_name ("^(" SIPREG_TOKEN ")" SIPREG_HCOLON);
	static Regex reg_crlf ("^\r\n");


// YGA: For debug purpose
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "MessageHeader::PreDecodeField" << std::endl;
	logfile.close();
}

	if (reg_crlf.Match (buffer)) {
		reg_crlf.MovePast (buffer);
		SetHypNextField (-2); // end of the headers
	} else {
		reg_header_name.AssertMatch (buffer, this);

		const SipHeaderMap::Entry& hdr = SipHeaderMap::GetByName(reg_header_name.GetMatchedString (1));
		int id = hdr.mIdMessageHeader;

		std::cerr << "biiiiiip " << reg_header_name.GetMatchedString(1) << std::endl;

	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "MessageHeader::PreDecodeField: " << id << std::endl;
	logfile << "MessageHeader::PreDecodeField:MatchedName: " << reg_header_name.GetMatchedString(1) << std::endl;
	logfile << "MessageHeader::PreDecodeField:hdrId: " << id << std::endl;
	logfile.close();



		// check that this field is not duplicated
		if (IsPresent(id)) {
			switch (id) {
			case id_accept:
			case id_acceptEncoding:
			case id_acceptLanguage:
			case id_alertInfo:
			case id_allow:
			case id_authorization:
			case id_contact:
			case id_contentEncoding:
			case id_contentLanguage:
			case id_errorInfo:
			case id_inReplyTo:
			case id_proxyAuthorization:
			case id_proxyRequire:
			case id_recordRoute:
			case id_require:
			case id_route:
			case id_supported:
			case id_unsupported:
			case id_via:
			case id_warning:
			case id_allowEvents:
			case id_pMediaAuthorization:
			case id_pAssertedID:
			case id_pPreferredID:
			case id_reason:
			case id_path:
			case id_securityClient:
			case id_securityServer:
			case id_securityVerify:
			case id_pAssociatedURI:
			case id_pCalledPartyID:
			case id_pVisitedNetworkID:
			case id_acceptContact:
			case id_historyInfo:
			case id_pEarlyMedia:
			case id_undefinedHeader_List:
				// these fields can appear multiple times
				break;
			default:
				DecodeError e (this);
				e.Msg() << "Duplicated field in the message: "
					<< hdr.mName << std::endl; 
				throw (e);
			}
		}
		SetHypNextField (id);
	}
}

void MessageHeader::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_crlf ("^\r\n");
	reg_crlf.AssertMatch (buffer, this);
	buffer.SetPosition(buffer.GetPosition() + reg_crlf.GetMatchedLength());
}


const char* FieldName::msFields[] = { 
        "ACCEPT_E",
        "ACCEPT_ENCODING_E",
        "ACCEPT_LANGUAGE_E",
        "ALERT_INFO_E",
        "ALLOW_E",
        "AUTHENTICATION_INFO_E",
        "AUTHORIZATION_E",
        "CALL_ID_E",
        "CALL_INFO_E",
        "CONTACT_E",
        "CONTENT_DISPOSITION_E",
        "CONTENT_ENCODING_E",
        "CONTENT_LANGUAGE_E",
        "CONTENT_LENGTH_E",
        "CONTENT_TYPE_E",
        "CSEQ_E",
        "DATE_E",
        "ERROR_INFO_E",
        "EXPIRES_E",
        "FROM_E",
        "IN_REPLY_TO_E",
        "MAX_FORWARDS_E",
        "MIME_VERSION_E",
        "MIN_EXPIRES_E",
        "ORGANIZATION_E",
        "PRIORITY_E",
        "PROXY_AUTHENTICATE_E",
        "PROXY_AUTHORIZATION_E",
        "PROXY_REQUIRE_E",
        "RECORD_ROUTE_E",
        "REPLY_TO_E",
        "REQUIRE_E",
        "RETRY_AFTER_E",
        "ROUTE_E",
        "SERVER_E",
        "SUBJECT_E",
        "SUPPORTED_E",
        "TIMESTAMP_E",
        "TO_E",
        "UNSUPPORTED_E",
        "USER_AGENT_E",
        "VIA_E",
        "WARNING_E",
        "WWW_AUTHENTICATE_E",
	"RACK_E",
	"RSEQ_E",
	"ALLOW_EVENTS_E",
	"EVENT_E",
	"SUBSCRIPTION_STATE_E",
	"P_MEDIA_AUTHORIZATION_E",
	"PRIVACY_E",
	"P_ASSERTED_ID_E",
	"REFER_SUB_E",
	"P_PREFERRED_ID_E", 
	"REASON_E",
	"REFER_TO_E",
	"REFERRED_BY_E",
	"HISTORY_INFO_E",
	"P_MEDIA_AUTH_E",
	"PATH_E",	
	"SECURITY_CLIENT_E",	
	"SECURITY_SERVER_E",
	"SECURITY_VERIFY_E",  
	"P_ACCESS_NETWORK_INFO_E",
	"P_ASSOCIATED_URI_E",
	"P_CALLED_PARTY_E",
	"P_CHARGING_FUNCTION_ADDRESSES_E",
	"P_CHARGING_VECTOR_E",
	"P_VISITED_NETWORK_E",
	"SERVICE_ROUTE_E",
	"ACCEPT_CONTACT_E",
	"MIN_SE_E",
	"SESSION_EXPIRES_E",
	"P_ASSERTED_SERVICE_E",
	"P_EARLY_MEDIA_E"
	, "" };

void FieldName::Encode (Buffer& buffer) throw (EncodeError)
{
	Charstring c;

	c.SetValue ((SipHeaderMap::GetByIdFieldName(GetValueString()).mName + ": ").c_str());
	c.Encode(buffer);
}

void FieldName::Decode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_header_name ("^([A-Za-z\\-]+)" SIPREG_HCOLON);

	reg_header_name.AssertMatch (buffer, this);
	SetValueString (SipHeaderMap::GetByName(reg_header_name.GetMatchedString (1)).mIdFieldName.c_str());
	
	buffer.SetPosition(buffer.GetPosition() + reg_header_name.GetMatchedLength());
}

void Addr_Union::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "Addr_Union::PreDecode" << std::endl;
	logfile.close();

	static Regex reg_name_addr ("^" SIPREG_DISPLAY_NAME "?" SIPREG_SWS "<[^\\r\\n]*>");
	
	mPosition = buffer.GetPosition();

	SetHypChosenId (
		reg_name_addr.Match (buffer)
			? id_nameAddr
			: id_addrSpecUnion
	);
}

void Addr_Union::PostDecode (Buffer& buffer) throw (DecodeError)
{
	if (GetChosenId() == id_addrSpecUnion) {
		Variable* parent = GetParent();
		if (parent && (
			(strcmp (parent->GetTypeName(), "From") == 0) ||
			(strcmp (parent->GetTypeName(), "ContactAddress") == 0) ||
			(strcmp (parent->GetTypeName(), "ReplyTo") == 0) ||
			(strcmp (parent->GetTypeName(), "To") == 0) ))
		{
			// in the case we decoded an address not enclosed in <> in a
			// From, Contact, Reply-To or To header, then we must ensure
			// that it does not contain comma, semicolon or question mark

			const unsigned char* start = buffer.GetValueBin() + (mPosition/8);
			const unsigned char* end   = buffer.GetValueBin() + (buffer.GetPosition()/8);
			
			for (const unsigned char* p=start ; p!=end ; p++) {
				switch (*p) {
				case ';':
					Get_addrSpecUnion().SetField (SipUrl::id_urlParameters, new Undef);
				case '?':
					Get_addrSpecUnion().SetField (SipUrl::id_headers, new Undef);
					buffer.SetPosition ((p-start) * 8 + mPosition);
					goto finished;
				case ',':
					throw DecodeError (this, "Url must not contain unescaped comma, semicolor or question mark if it is not enclosed with <>\n");
				default: ;
				}
			}
			finished: ;
		}
	}
}

void ContactBody::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "ContactBody::PreDecode" << std::endl;
	logfile.close();

	static Regex reg_asterisk ("^[*]");
	if (reg_asterisk.Match (buffer)) {
		if (GetChosenId() == id_contactAddresses)
			throw DecodeError (this, "cannot process wildcard; contactAddresses option is already selected\n");
		SetHypChosenId (id_wildcard);
		SetHypFieldLength(id_wildcard, 8);
	} else {
		if (GetChosenId() == id_wildcard)
			throw DecodeError (this, "cannot process address list; wildcart option is already selected\n");
		SetHypChosenId (id_contactAddresses);
	}
}

void ContactAddress_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void ContactAddress_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "ContactAddress_List::PreDecode" << std::endl;
	logfile.close();

	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void ContactAddress_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void ContactAddress::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_contactParams && IsPresent(id_contactParams)) {
		csSemi.Encode(buffer);
	}
}

void ContactAddress::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "ContactAddress::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_semicolon ("^" SIPREG_SEMI);
	if (id == id_contactParams) {
		if(reg_semicolon.Match(buffer)) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void From::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_fromParams && IsPresent(id_fromParams)) {
		csSemi.Encode(buffer);
	}
}

void From::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "From::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_semicolon ("^;");
	if (id == id_fromParams) {
		if(reg_semicolon.Match(buffer)) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void To::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_toParams && IsPresent(id_toParams)) {
		csSemi.Encode(buffer);
	}
}

void To::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "To::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_semicolon ("^;");
	if (id == id_toParams) {
		if(reg_semicolon.Match(buffer)) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void ReplyTo::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_replyToParams && IsPresent(id_replyToParams)) {
		csSemi.Encode(buffer);
	}
}

void ReplyTo::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "ReplyTo::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_semicolon ("^;");
	if (id == id_replyToParams) {
		if(reg_semicolon.Match(buffer)) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void Accept::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "Accept::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_accept_args ("^[^;,\\r\\n]");
	if (id == id_acceptArgs){
		if(reg_accept_args.Match(buffer) || Get_acceptArgs().GetSize() > 0) {
			SetHypFieldIsPresent (id, 1);
		} else if (Get_acceptArgs().GetSize() == 0){
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void AcceptBody::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_acceptParam && IsPresent(id_acceptParam)) {
		csSemi.Encode(buffer);
	}
}

void AcceptBody::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "AcceptBody::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_media_range ("^[^" SIPCHARS_WSP ";,\\r\\n]+");
	static Regex reg_semicolon ("^" SIPREG_SEMI);
	switch (id){
		case id_mediaRange:
			reg_media_range.AssertMatch (buffer, this);
			SetHypFieldLength(id, reg_media_range.GetMatchedLength());
			break;
		case id_acceptParam:
			if (reg_semicolon.Match (buffer) ) {
				SetHypFieldIsPresent(id, 1);
			} else {
				SetHypFieldIsPresent(id, 0);
			}
	}
}

void AcceptBody_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void AcceptBody_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "AcceptBody_List::PreDecode" << std::endl;
	logfile.close();

	// we assume that we are decoding one field at onece 
	// multiple fields are handled by successively decoding
	// the via field several times in MessageHeader
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void AcceptBody_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "AcceptBody_List::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	if (GetSize() > 0 && !reg_content.Match(buffer)) {
		SetHypSize(-2);	
	}
}

void AcceptBody_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	if (detect_comma (buffer) && reg_content.Match (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void AcceptEncoding::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "AcceptEncoding::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_content_coding ("^[^,\\r\\n]");
	if (id == id_contentCoding){
		if(reg_content_coding.Match(buffer) || 
			Get_contentCoding().GetSize() > 0) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void ContentCoding_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void ContentCoding_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "ContentCoding_List::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	if (GetSize() == 0)
		reg_content.AssertMatch(buffer, this);
	else if (!reg_content.Match(buffer)) {
		SetHypSize(-2);
		return;
	}
	SetHypFieldLength(reg_content.GetMatchedLength());
}


void ContentCoding_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	if (detect_comma (buffer) && reg_content.Match (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void AcceptLanguage::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "AcceptLanguage::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_language_body ("^[^;,\\r\\n]");
	if (id == id_languageBody){
		if(reg_language_body.Match(buffer) ||
			Get_languageBody().GetSize() > 0) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void LanguageBody::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_acceptParam && IsPresent(id_acceptParam)) {
		csSemi.Encode(buffer);
	}
}

void LanguageBody::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "LanguageBody::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_language_range ("^[^" SIPCHARS_WSP ";,\\r\\n]+");
	static Regex reg_semicolon ("^" SIPREG_SEMI);
	switch (id){
		case id_languageRange:
			reg_language_range.AssertMatch (buffer, this);
			SetHypFieldLength(id, reg_language_range.GetMatchedLength());
			break;
		case id_acceptParam:
			if (reg_semicolon.Match (buffer)) {
				SetHypFieldIsPresent(id, 1);
			} else {
				SetHypFieldIsPresent(id, 0);
			}
	}
}

void LanguageBody_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError) 
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void LanguageBody_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "LanguageBody_List::PreDecode" << std::endl;
	logfile.close();

	// we assume that we are decoding one field at onece 
	// multiple fields are handled by successively decoding
	// the via field several times in MessageHeader
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void LanguageBody_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError) {
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "LanguageBody_List::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_language ("^[^" SIPCHARS_WSP ";,\\r\\n]+");
	if (!reg_language.Match(buffer)) {
		SetHypSize(-2);
		return;
	}
}

void LanguageBody_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_language ("^[^" SIPCHARS_WSP ";,\\r\\n]+");
	if (detect_comma (buffer) && reg_language.Match (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void MaxForwards::PreEncode (Buffer& buffer) throw (EncodeError)
{
	Get_forwards().SetFormat(Integer::AsciiDecimal);
}

void MaxForwards::PreDecode (Buffer& buffer) throw (DecodeError)
{
	Get_forwards().SetFormat(Integer::AsciiDecimal);
}

void AlertInfo::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	static Regex reg_alert_info ("^[^;,\\r\\n]");
	if (id == id_alertInfoBody){
		if(reg_alert_info.Match(buffer) || Get_alertInfoBody().GetSize() > 0) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void AlertInfoBody::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csLeftAngle, csSemi;
	csLeftAngle.SetValue("<");
	csSemi.SetValue(";");

 	if(field_id == id_url) {
		csLeftAngle.Encode(buffer);
	}

	if(field_id == id_genericParams && IsPresent(id_genericParams)) {
		csSemi.Encode(buffer);
	}
}

void AlertInfoBody::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csRightAngle;
	csRightAngle.SetValue(">");

 	if(field_id == id_url) {
		csRightAngle.Encode(buffer);
	}
}

void AlertInfoBody::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_url ("^<" SIPREG_ABSOLUTE_URI ">");
	static Regex reg_semicolon ("^" SIPREG_SEMI);
	switch (id){
		case id_url:
			reg_url.AssertMatch (buffer, this);
			buffer.SetPosition(buffer.GetPosition() + 8);
			SetHypFieldLength(id, reg_url.GetMatchedLength() - 16);
			break;
		case id_genericParams:
			if (reg_semicolon.Match (buffer)) {
				SetHypFieldIsPresent(id, 1);
			} else {
				SetHypFieldIsPresent(id, 0);
			}
	}
}

void AlertInfoBody::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	switch (id){
		case id_url:
			buffer.SetPosition(buffer.GetPosition() + 8);
			break;
	}
}

void AlertInfoBody_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void AlertInfoBody_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "AlertInfoBody_List::PreDecode" << std::endl;
	logfile.close();

	// we assume that we are decoding one field at onece 
	// multiple fields are handled by successively decoding
	// the via field several times in MessageHeader
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void AlertInfoBody_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void Allow::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "Allow::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_allow ("^[^,\\r\\n]");
	if (id == id_methods){
		if(reg_allow.Match(buffer) || Get_methods().GetSize() > 0) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void Method_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void Method_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "Method_List::PreDecode" << std::endl;
	logfile.close();

	// we assume that we are decoding one field at onece 
	// multiple fields are handled by successively decoding
	// the via field several times in MessageHeader
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void Method_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError) {
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "Method_List::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_content ("^" SIPREG_TOKEN);
	
	if (reg_content.Match (buffer)) {
		SetHypFieldLength(reg_content.GetMatchedLength());
	} else {
		SetHypSize(-2);
		return;
	} 
}

void Method_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_TOKEN);

	if (detect_comma (buffer) && reg_content.Match (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}


void CredentialsList::PreDecode (Buffer& buffer) throw (DecodeError) {
  SetHypSize (GetSize() + 1);
  SetHypAppend (1);
}

void CredentialsList::PreDecodeField (int id, Buffer& buffer) throw (DecodeError) {
  
}

// TODO: Add CredentialsList PreEncode

/*      type record Authorization
      {
        FieldName		fieldName (AUTHORIZATION_E),
		CredentialsList	body // changed from Credentials to allow multiple Authorization headers
      }*/
/*      type record ProxyAuthorization
      {
        FieldName		fieldName (PROXY_AUTHORIZATION_E),
        CredentialsList		credentials // changed from Credentials to allow multiple Authorization headers
      }*/


void Credentials::PreEncode (Buffer& buffer) throw (EncodeError)
{
	Charstring csDigestWS;
	csDigestWS.SetValue("Digest ");
	
	if(GetChosenId() == id_digestResponse) {
		csDigestWS.Encode(buffer);
	}
}

void Credentials::PreDecode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_digest ("^[Dd][Ii][Gg][Ee][Ss][Tt]" SIPREG_LWS);
	if (reg_digest.Match (buffer)) {
		if (GetChosenId() == id_otherResponse)
			throw DecodeError (this, "cannot process digest credentials; otherResponse option is already selected\n");
		buffer.SetPosition(buffer.GetPosition() + reg_digest.GetMatchedLength());
		SetHypChosenId (id_digestResponse);		
	} else {
		if (GetChosenId() == id_digestResponse)
			throw DecodeError (this, "cannot process custom credentials; digestResponse option is already selected\n");
		SetHypChosenId (id_otherResponse);
	}
}

void Challenge::PreEncode (Buffer& buffer) throw (EncodeError)
{
	Charstring csDigestWS;
	csDigestWS.SetValue("Digest ");
	
	if(GetChosenId() == id_digestCln) {
		csDigestWS.Encode(buffer);
	}
}

void Challenge::PreDecode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_digest ("^[Dd][Ii][Gg][Ee][Ss][Tt]" SIPREG_LWS);
	if (reg_digest.Match (buffer)) {
		buffer.SetPosition(buffer.GetPosition() + reg_digest.GetMatchedLength());
		SetHypChosenId (id_digestCln);
	} else {
		SetHypChosenId (id_otherChallenge);
	}
}

void OtherAuth::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS;
	csWS.SetValue(" ");

	switch(field_id) {
	case id_authScheme:
		csWS.Encode(buffer);
		break;
	default:
		break;
	}
}

void OtherAuth::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_auth_scheme ("^" SIPREG_TOKEN);
	static Regex reg_separator ("^" SIPREG_LWS);

	switch (id){
		case id_authScheme:
			reg_auth_scheme.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_auth_scheme.GetMatchedLength());
			break;
		case id_authParams:
			reg_separator.AssertMatch(buffer, this);
			buffer.SetPosition(buffer.GetPosition() + reg_separator.GetMatchedLength());
			Get_authParams().SetHypSize (GetSize() + 1);
			Get_authParams().SetHypAppend (1);
			break;
	}
}

void CallInfo::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	static Regex reg_call_info ("^[^;,\\r\\n]");
	if (id == id_callInfoBody){
		if(reg_call_info.Match(buffer)) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void CallInfoBody::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csLeftAngle, csSemi;
	csLeftAngle.SetValue("<");
	csSemi.SetValue(";");

 	if(field_id == id_url) {
		csLeftAngle.Encode(buffer);
	}

	if(field_id == id_infoParams && IsPresent(id_infoParams)) {
		csSemi.Encode(buffer);
	}
}

void CallInfoBody::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csRightAngle;
	csRightAngle.SetValue(">");

 	if(field_id == id_url) {
		csRightAngle.Encode(buffer);
	}
}

void CallInfoBody::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_url ("^<" SIPREG_ABSOLUTE_URI ">");
	static Regex reg_semicolon ("^" SIPREG_SEMI);
	switch (id){
		case id_url:
			reg_url.AssertMatch (buffer, this);
			buffer.SetPosition(buffer.GetPosition() + 8);
			SetHypFieldLength(id, reg_url.GetMatchedLength() - 16);
			break;
		case id_infoParams:
			if (reg_semicolon.Match (buffer)) {
				SetHypFieldIsPresent(id, 1);
			} else {
				SetHypFieldIsPresent(id, 0);
			}
	}
}

void CallInfoBody::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	switch (id){
		case id_url:
			buffer.SetPosition(buffer.GetPosition() + 8);
			break;
	}
}

void CallInfoBody_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void CallInfoBody_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void ContentDisposition::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_dispositionParams && IsPresent(id_dispositionParams)) {
		csSemi.Encode(buffer);
	}
}


void ContentDisposition::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_disposition_type ("^" SIPREG_TOKEN);
	static Regex reg_semicolon ("^" SIPREG_SEMI);
	switch (id){
		case id_dispositionType:
			reg_disposition_type.AssertMatch (buffer, this);
			SetHypFieldLength(id, reg_disposition_type.GetMatchedLength());
			break;
		case id_dispositionParams:
			if (reg_semicolon.Match (buffer)) {
				SetHypFieldIsPresent(id, 1);
			} else {
				SetHypFieldIsPresent(id, 0);
			}
	}
}

void LanguageTag_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void LanguageTag_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void LanguageTag_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	reg_content.AssertMatch(buffer, this);
	SetHypFieldLength(reg_content.GetMatchedLength());	
}

void LanguageTag_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void Date::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	static Regex reg_date ("^[^\\r\\n]+");
	if (id == id_sipDate){
		reg_date.AssertMatch(buffer, this);
		SetHypFieldLength(id, reg_date.GetMatchedLength());
	}
}

void ErrorInfo::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	static Regex reg_error_info ("^[^;,\\r\\n]");
	if (id == id_errorInfo){
		if(reg_error_info.Match(buffer) || Get_errorInfo().GetSize() > 0) {
			SetHypFieldIsPresent (id, 1);
		} else {
			SetHypFieldIsPresent (id, 0);
		}
	}
}

void ErrorInfoBody::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csLeftAngle, csSemi;
	csLeftAngle.SetValue("<");
	csSemi.SetValue(";");

 	if(field_id == id_uri) {
		csLeftAngle.Encode(buffer);
	}

	if(field_id == id_genericParams && IsPresent(id_genericParams)) {
		csSemi.Encode(buffer);
	}
}

void ErrorInfoBody::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csRightAngle;
	csRightAngle.SetValue(">");

 	if(field_id == id_uri) {
		csRightAngle.Encode(buffer);
	}
}

void ErrorInfoBody::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_uri ("^<" SIPREG_ABSOLUTE_URI ">");
	static Regex reg_semicolon ("^" SIPREG_SEMI);
	switch (id){
		case id_uri:
			reg_uri.AssertMatch (buffer, this);
			buffer.SetPosition(buffer.GetPosition() + 8);
			SetHypFieldLength(id, reg_uri.GetMatchedLength() - 16);
			break;
		case id_genericParams:
			if (reg_semicolon.Match (buffer)) {
				SetHypFieldIsPresent(id, 1);
			} else {
				SetHypFieldIsPresent(id, 0);
			}
	}
}

void ErrorInfoBody::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	switch (id){
		case id_uri:
			buffer.SetPosition(buffer.GetPosition() + 8);
			break;
	}
}

void ErrorInfoBody_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void ErrorInfoBody_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void ErrorInfoBody_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void Expires::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_delta_sec ("^[0-9]+");

	switch (id) {
	case id_deltaSec: 
		reg_delta_sec.AssertMatch (buffer, this);
		SetHypFieldLength (id, reg_delta_sec.GetMatchedLength());
		break;
	}
}

void CallidString_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void CallidString_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void CallidString_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_ASCII_WITHOUT_COMMA);
	reg_content.AssertMatch(buffer, this);
	SetHypFieldLength(reg_content.GetMatchedLength());
}


void CallidString_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void MimeVersion::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csDot;
	csDot.SetValue(".");

	switch(field_id) {
	case id_majorNumber:
		Get_majorNumber().SetFormat(Integer::AsciiDecimal);
		break;
	case id_minorNumber:
		Get_minorNumber().SetFormat(Integer::AsciiDecimal);
		csDot.Encode(buffer);
		break;
	default:
		break;
	}
}

void MimeVersion::PreDecode (Buffer& buffer) throw (DecodeError)
{
	Get_majorNumber().SetFormat(Integer::AsciiDecimal);
	Get_minorNumber().SetFormat(Integer::AsciiDecimal);
}
void MimeVersion::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^[.]");
	if (id == id_minorNumber) {
		reg_separator.AssertMatch (buffer, this);
		buffer.SetPosition(buffer.GetPosition() + 8);
	}
}
void MinExpires::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_delta_sec ("^[0-9]+");

	switch (id) {
	case id_deltaSec: 
		reg_delta_sec.AssertMatch (buffer, this);
		SetHypFieldLength (id, reg_delta_sec.GetMatchedLength());
		break;
	}
}

void Organization::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_organization ("^(" SIPREG_TEXT_UTF8_TRIM ")*");

	switch (id) {
	case id_organization: 
		reg_organization.AssertMatch (buffer, this);
		SetHypFieldLength (id, reg_organization.GetMatchedLength());
		break;
	}
}

void Priority::PreDecodeField (int id, Buffer& buffer) throw (DecodeError){
	static Regex reg_priority ("^" SIPREG_TOKEN);
	if (id == id_priorityValue){
		reg_priority.AssertMatch(buffer, this);
		SetHypFieldLength(id, reg_priority.GetMatchedLength());
	}
}

void RetryAfter::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csLeftPar, csSemi;
	csLeftPar.SetValue("(");
	csSemi.SetValue(";");

	if(field_id == id_comment && IsPresent(id_comment)) {
		csLeftPar.Encode(buffer);
	}

	if(field_id == id_retryParams && IsPresent(id_retryParams)) {
		csSemi.Encode(buffer);
	}
}

void RetryAfter::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csRightPar;
	csRightPar.SetValue(")");

	if(field_id == id_comment && IsPresent(id_comment)) {
		csRightPar.Encode(buffer);
	}
}

void RetryAfter::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_delta_sec ("^[0-9]+");
	static Regex reg_comment ("^" SIPREG_COMMENT);
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_deltaSec: 
			reg_delta_sec.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_delta_sec.GetMatchedLength());
			break;
		case id_comment:
			remove_whitespace(buffer);
			if (reg_comment.Match (buffer)) {
				SetHypFieldIsPresent(id, 1);
				SetHypFieldLength (id, reg_comment.GetMatchedLength() - 16);
				buffer.SetPosition(buffer.GetPosition() + 8);
			}
			else
				SetHypFieldIsPresent(id, 0);
			break;
		case id_retryParams:
			if (reg_separator.Match (buffer))
				SetHypFieldIsPresent(id, 1);
			else
				SetHypFieldIsPresent(id, 0);
			break;
	}
}

void RetryAfter::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_parenthesis ("^[)]");
	if (id == id_comment && IsPresent(id))
	{
		reg_parenthesis.AssertMatch (buffer, this);
		buffer.SetPosition(buffer.GetPosition() + 8);
		remove_whitespace(buffer);
	}
}

void Subject::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_summary ("^(" SIPREG_TEXT_UTF8_TRIM ")*");

	switch (id) {
	case id_summary: 
		reg_summary.AssertMatch (buffer, this);
		SetHypFieldLength (id, reg_summary.GetMatchedLength());
		break;
	}
}


void ServerVal_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS;
	csWS.SetValue(" ");

	if(field_id != 0) {
		csWS.Encode(buffer);
	}
}

void ServerVal_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^((?:" SIPREG_TOKEN "(?:" SIPREG_SLASH SIPREG_TOKEN ")?)|" SIPREG_COMMENT ")");
	reg_content.AssertMatch(buffer, this);
	SetHypFieldLength(reg_content.GetMatchedLength());
}

void ServerVal_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_LWS);
	if (reg_separator.Match (buffer)) {
		reg_separator.MovePast (buffer);
		SetHypSize (GetSize() + 1);
	}
	else
		SetHypSize (-2);
}

void Supported::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_TOKEN);

	switch (id){
		case id_optionsTags:
			if (reg_content.Match (buffer) || Get_optionsTags().GetSize() > 0)
				SetHypFieldIsPresent(id, 1);
			else
				SetHypFieldIsPresent(id, 0);
			break;
	}
}

void NameAddr::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csLeftAngle;
	csLeftAngle.SetValue("<");

 	if(field_id == id_addrSpec) {
		csLeftAngle.Encode(buffer);
	}
}

void NameAddr::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS, csRightAngle;
	csWS.SetValue(" ");
	csRightAngle.SetValue(">");

	switch(field_id) {
	case id_displayName:
		if(IsPresent(id_displayName)) {
			csWS.Encode(buffer);
		}
		break;
	case id_addrSpec:
		csRightAngle.Encode(buffer);
		break;
	default:
		break;
	}
}

void NameAddr::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_display_name ("^" SIPREG_DISPLAY_NAME);
	static Regex reg_laquot = ("^<");
	static Regex reg_uri = ("^[^\\r\\n]+");

	remove_whitespace(buffer);
	switch (id){
		case id_displayName:
			if (reg_display_name.Match(buffer)) {
				SetHypFieldIsPresent (id, 1);
				SetHypFieldLength (id, reg_display_name.GetMatchedLength());
			} else {
				SetHypFieldIsPresent (id, 0);
			}
			break;
		case id_addrSpec:
			reg_laquot.AssertMatch (buffer, this);
			buffer.SetPosition(buffer.GetPosition() + 8);
			reg_uri.AssertMatch(buffer, this);
			SetHypFieldLength (id, reg_uri.GetMatchedLength());
			break;
	}
}

void NameAddr::PostDecode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_raquot ("^>");

	reg_raquot.AssertMatch (buffer, this);
	buffer.SetPosition(buffer.GetPosition() + 8);
	remove_whitespace(buffer);
	
	if (IsPresent (id_displayName)) {
		if (Get_displayName().GetLength() &&
		    (*Get_displayName().GetValueBin() == '"'))
			normalise_quoted_string (Get_displayName(), true);
	}
}

void SentProtocol::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSlash;
	csSlash.SetValue("/");

	if(field_id != id_protocolName) {
		csSlash.Encode(buffer);
	}
}

void SentProtocol::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{

	if (id) {
		static Regex reg_slash ("^/");
		reg_slash.AssertMatch (buffer, this);
		reg_slash.MovePast (buffer);
	}

	static Regex reg_sp ("^" SIPREG_TOKEN);

	reg_sp.AssertMatch (buffer, this);

	SetHypFieldLength (id, reg_sp.GetMatchedLength());
}

void ViaBody::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_viaParams && IsPresent(id_viaParams)) {
		csSemi.Encode(buffer);
	}
}

void ViaBody::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS;
	csWS.SetValue(" ");

	if(field_id == id_sentProtocol) {
		csWS.Encode(buffer);
	}
}

void ViaBody::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "ViaBody::PreDecodeField: " << id << std::endl;
	logfile.close();

	static Regex reg_lws ("^" SIPREG_LWS);
	static Regex reg_semi ("^;");

	switch (id) {
	case id_sentBy:
		reg_lws.AssertMatch (buffer, this);
		reg_lws.MovePast (buffer);
		break;
	case id_viaParams:
		SetHypFieldIsPresent (id, reg_semi.Match (buffer) ? 1 : 0);
		break;
	default:
		;
	}
}

void ViaBody_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void ViaBody_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
	logfile << "ViaBody_List::PreDecode" << std::endl;
	logfile.close();

	// we assume that we are decoding one field at once 
	// multiple fields are handled by successively decoding
	// the via field several times in MessageHeader
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void ViaBody_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	Regex reg_comma ("^" SIPREG_COMMA);

	if (reg_comma.Match(buffer)) {
		reg_comma.MovePast(buffer);

		SetHypSize (GetSize() + 1);
	}
}

void UndefinedHeader_List::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csCRLF;
	csCRLF.SetValue("\r\n");

	csCRLF.Encode(buffer);
}

void UndefinedHeader_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	// we assume that we are decoding one field at once 
	// multiple fields are handled by successively decoding
	// the via field several times in MessageHeader
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void UndefinedHeader::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csColon;
	csColon.SetValue(": ");

	if(field_id == id_headerName) {
		csColon.Encode(buffer);
	}
}

void UndefinedHeader::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_header_name ("^" SIPREG_TOKEN);

	// TODO: match properly UTF-8 characters
	// TODO: normalise the value ?...
	static Regex reg_header_value ("^" SIPREG_HCOLON "(([^\\r\\n]|" SIPREG_SWS ")*)");

	switch (id) {
	case id_headerName:
		reg_header_name.AssertMatch (buffer, this);
		SetHypFieldLength (id, reg_header_name.GetMatchedLength());
		break;
	case id_headerValue:
		reg_header_value.AssertMatch (buffer, this);
		reg_header_value.MoveAt (buffer, 1);
		SetHypFieldLength (id, reg_header_value.GetMatchedLength(1));
		break;
	default:
		;
	}
}

void UndefinedHeader::PostDecode (Buffer& buffer) throw (DecodeError)
{
	normalise_escaped_string (Get_headerValue());
}

void CallId::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (id == id_callid) {
		static Regex reg_cid ("^" SIPREG_WORD "(@" SIPREG_WORD ")*" );

		reg_cid.AssertMatch(buffer, this);
		SetHypFieldLength (id, reg_cid.GetMatchedLength());
	}
}

void CSeq::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS;
	csWS.SetValue(" ");
	
	switch(field_id) {
	case id_method:
		csWS.Encode(buffer);
		break;
	case id_seqNumber:
		Get_seqNumber().SetFormat(Integer::AsciiDecimal);
		break;
	default:
		break;
	}
}

void CSeq::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_method ("^" SIPREG_LWS "(" SIPREG_TOKEN ")");

	switch (id) {
	case id_seqNumber: 
		Get_seqNumber().SetFormat(Integer::AsciiDecimal);
		break;
	case id_method:
		reg_method.AssertMatch (buffer, this);
		reg_method.MoveAt (buffer, 1);
		SetHypFieldLength (id, reg_method.GetMatchedLength(1));
		break;
	}
}

void ContentLength::PreEncode (Buffer& buffer) throw (EncodeError)
{
	Get_len().SetFormat(Integer::AsciiDecimal);
}

void ContentLength::PreDecode (Buffer& buffer) throw (DecodeError)
{
	Get_len().SetFormat(Integer::AsciiDecimal);
}

void ContentType::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_ctype ("^" SIPREG_TOKEN "/" SIPREG_TOKEN "(" SIPREG_SEMI SIPREG_M_PARAMETER ")*");
	if (id == id_mediaType)
	{
		std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app);
		logfile << "ContentType::PreDecodeField: id:" << id << std::endl;
		logfile << "ContentType::PreDecodeField: matching at:" << buffer.GetPosition() << std::endl;
		
		reg_ctype.AssertMatch (buffer, this);
		SetHypFieldLength (id, reg_ctype.GetMatchedLength());
		logfile << "ContentType::PreDecodeField: matched:" << reg_ctype.GetMatchedString() << std::endl;
		logfile.close();
	}
	
}

#define SIP_MESSAGE_CODET(msgname) \
void msgname::PostDecodeField (int id, Buffer& buffer) throw (DecodeError) \
{ \
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app); \
	logfile << "msgname::PostDecodeField: id:" << id << std::endl; \
	logfile.close(); \
	switch (id) { \
	case id_msgHeader: \
	  { \
		/* decide if we have to decode the message body and how to decode it */ \
		MessageHeader& hdr = Get_msgHeader(); \
		int content_length = hdr.IsPresent (MessageHeader::id_contentLength) ? hdr.Get_contentLength().Get_len().GetValue() : 0; \
		if (content_length == 0) { \
			/* no message body */ \
			if (hdr.IsPresent (MessageHeader::id_contentType)) \
				throw DecodeError (this, "the Content-Type field must not be present if the content length is null\n"); \
 \
			SetHypFieldIsPresent (id_messageBody, 0); \
		} else if (content_length > 0) { \
			/* message body present */ \
			if (!hdr.IsPresent (MessageHeader::id_contentType)) \
				throw DecodeError (this, "the Content-Type field must be present if the content length is not null\n"); \
 \
			SetHypFieldIsPresent (id_messageBody, 1); \
			MessageBody::SetHypLength (content_length * 8); \
			const char* content_type = hdr.Get_contentType().Get_mediaType().GetValue(); \
			if (strcmp (content_type, "application/sdp") == 0) { \
				MessageBody::SetHypChosenId (MessageBody::id_sdpMessageBody); \
			} else { \
				/* decode as plain text by default */ \
				MessageBody::SetHypChosenId (MessageBody::id_textplain); \
			} \
		} else { \
			throw DecodeError (this, "Content-Length must not be a negative number\n"); \
		} \
		SetHypFieldIsPresent (id_payload, 0); \
		break; \
	  } \
	case id_payload: \
		if (buffer.GetBitsLeft()) { \
			DecodeError ex(this); \
			ex.Msg() << "buffer not fully decoded (" << buffer.GetBitsLeft()/8 << " remaining bytes)" << std::endl; \
			throw ex; \
		} \
		Get_payload().Get_payloadlength().SetValue(buffer.GetLength() / 8); \
		Get_payload().Get_payloadvalue().SetValueBin(buffer.GetValueBin(), buffer.GetLength()); \
 \
		/* replace undisplayable characters with '?' */ \
		Charstring& payload = Get_payload().Get_payloadvalue(); \
		int byte_length = buffer.GetLength() / 8; \
		for (int  i=0 ; i<byte_length ; i++) { \
			if (!asciichar_is_displayable (payload[i])) \
				payload[i] = '?'; \
		} \
		break; \
	} \
}

#define SIP_MESSAGE_CODET_ERROR(msgname) \
void msgname::PostDecode (Buffer& buffer, DecodeError& e) throw (DecodeError) \
{ \
	std::cerr << "###################################################################################" << std::endl; \
	std::cerr << "###  INVALID SIP MESSAGE RECEIVED                                               ###" << std::endl; \
	std::cerr << "###                                                                             ###" << std::endl; \
	e.Dump(std::cerr); \
	std::cerr << "###-----------------------------------------------------------------------------###" << std::endl; \
	std::cerr.write (reinterpret_cast<const char*>(buffer.GetValueBin()), buffer.GetLength()/8); \
	std::cerr << "###################################################################################" << std::endl; \
	/* tell t3devkit to ignore silently the message */ \
	std::ofstream logfile("C:\\Temp\\LibSip.log", ios::app); \
	logfile << "###################################################################################" << std::endl; \
	logfile << "###  INVALID SIP MESSAGE RECEIVED                                               ###" << std::endl; \
	logfile << "###################################################################################" << std::endl; \
	logfile.write(reinterpret_cast<const char*>(buffer.GetValueBin()), buffer.GetLength()/8); \
	logfile.close(); \
	throw DecodeIgnoreMessage(e.mVar); \
}

SIP_MESSAGE_CODET (Response)
SIP_MESSAGE_CODET_ERROR (Response)
SIP_MESSAGE_CODET (Request)
SIP_MESSAGE_CODET_ERROR (Request)
SIP_MESSAGE_CODET (REGISTER_Request)
SIP_MESSAGE_CODET_ERROR (REGISTER_Request)
SIP_MESSAGE_CODET (INVITE_Request)
SIP_MESSAGE_CODET_ERROR (INVITE_Request)
SIP_MESSAGE_CODET (OPTIONS_Request)
SIP_MESSAGE_CODET_ERROR (OPTIONS_Request)
SIP_MESSAGE_CODET (BYE_Request)
SIP_MESSAGE_CODET_ERROR (BYE_Request)
SIP_MESSAGE_CODET (CANCEL_Request)
SIP_MESSAGE_CODET_ERROR (CANCEL_Request)
SIP_MESSAGE_CODET (ACK_Request)
SIP_MESSAGE_CODET_ERROR (ACK_Request)
SIP_MESSAGE_CODET (PRACK_Request)
SIP_MESSAGE_CODET_ERROR (PRACK_Request)
SIP_MESSAGE_CODET (NOTIFY_Request)
SIP_MESSAGE_CODET_ERROR (NOTIFY_Request)
SIP_MESSAGE_CODET (SUBSCRIBE_Request)
SIP_MESSAGE_CODET_ERROR (SUBSCRIBE_Request)
SIP_MESSAGE_CODET (PUBLISH_Request)
SIP_MESSAGE_CODET_ERROR (PUBLISH_Request)
SIP_MESSAGE_CODET (UPDATE_Request)
SIP_MESSAGE_CODET_ERROR (UPDATE_Request)
SIP_MESSAGE_CODET (REFER_Request)
SIP_MESSAGE_CODET_ERROR (REFER_Request)
SIP_MESSAGE_CODET (MESSAGE_Request)
SIP_MESSAGE_CODET_ERROR (MESSAGE_Request)
SIP_MESSAGE_CODET (INFO_Request)
SIP_MESSAGE_CODET_ERROR (INFO_Request)

void OptionTag_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void OptionTag_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void OptionTag_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_TOKEN);
	
	bool bMandatory = true;
	Variable* parent = GetParent();
	if (parent != NULL) {
		const char * pszParName = parent->GetTypeName();
		if (strcmp(pszParName, "Supported") == 0) 
			bMandatory = false;
	}

	if (bMandatory || GetSize() == 0)
		reg_content.AssertMatch(buffer, this);
	else if (!reg_content.Match (buffer)) {
		SetHypSize (-2);
		return;
	}
	SetHypFieldLength(reg_content.GetMatchedLength());	
}

void OptionTag_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_content ("^" SIPREG_TOKEN);
	if (detect_comma (buffer) && reg_content.Match (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void RouteBody_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void RouteBody_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void RouteBody_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void RouteBody::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_rrParam && IsPresent(id_rrParam)) {
		csSemi.Encode(buffer);
	}
}

void RouteBody::PreDecodeField(int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_semi ("^;");

	if (id == id_rrParam) {
		SetHypFieldIsPresent (id, reg_semi.Match(buffer) ? 1 : 0);
	}
}

void Timestamp::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS;
	csWS.SetValue(" ");
	
	switch(field_id) {
	case id_delay:
		if(IsPresent(id_delay)) {
			csWS.Encode(buffer);
		}
		break;
	default:
		break;
	}
}

void Timestamp::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_LWS);

	switch (id) {
	case id_timeValue:
		SetHypFieldIsPresent (id, 1); //always present (mandatory in BNF)
		break;
	case id_delay:
		if (reg_separator.Match (buffer)) {
			reg_separator.MovePast (buffer);
			SetHypFieldIsPresent (id, 1);
		} else
			SetHypFieldIsPresent (id, 0);
		break;
	}
}

void TimeValue::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csDot;
	csDot.SetValue(".");

	switch(field_id) {
	case id_majorDigit:
		Get_majorDigit().SetFormat(Integer::AsciiDecimal);
		break;
	case id_minorDigit:
		Get_minorDigit().SetFormat(Integer::AsciiDecimal);
		csDot.Encode(buffer);
		break;
	default:
		break;
	}
}

void TimeValue::PreDecode (Buffer& buffer) throw (DecodeError)
{
	Get_majorDigit().SetFormat(Integer::AsciiDecimal);
	Get_minorDigit().SetFormat(Integer::AsciiDecimal);
}

void TimeValue::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^[.]");
	static Regex reg_digits ("^[0-9]+");
	switch (id) { 
		case id_minorDigit:
			SetHypFieldIsPresent (id,  0);
			if (reg_separator.Match (buffer)) {
				reg_separator.MovePast( buffer);
				if (reg_digits.Match (buffer))
					SetHypFieldIsPresent(id, 1);
			}			
			break;
	}
}

void WarningValue_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void WarningValue_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void WarningValue_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void WarningValue::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS, csDoubleQuote;
	csWS.SetValue(" ");
	csDoubleQuote.SetValue("\"");

	switch(field_id) {
	case id_warnCode:
		Get_warnCode().SetFormat(Integer::AsciiDecimal);
		break;
	case id_warnText:
		csWS.Encode(buffer);
		csDoubleQuote.Encode(buffer);
		break;
	case id_warnAgent:
		csWS.Encode(buffer);
		break;
	}
}

void WarningValue::PostEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csDoubleQuote;
	csDoubleQuote.SetValue("\"");

	if(field_id == id_warnText) {
		csDoubleQuote.Encode(buffer);
	}
}

void WarningValue::PreDecode (Buffer& buffer) throw (DecodeError)
{
	Get_warnCode().SetFormat(Integer::AsciiDecimal);
}

void WarningValue::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^[ ]");
	static Regex reg_text ("^" SIPREG_QUOTED_STRING);
	switch (id) { 
		case id_warnAgent:
			reg_separator.AssertMatch (buffer, this);
			reg_separator.MovePast (buffer);
			break;
		case id_warnText:
			reg_separator.AssertMatch (buffer, this);
			reg_separator.MovePast (buffer);
			reg_text.AssertMatch (buffer, this);
			buffer.SetPosition(buffer.GetPosition() + 8); // remove starting quota
			SetHypFieldLength (id, reg_text.GetMatchedLength() - 16);
			break;
	}
}

void WarningValue::PostDecode (Buffer& buffer) throw (DecodeError)
{
	buffer.SetPosition(buffer.GetPosition() + 8); // remove ending quota

	normalise_quoted_string (Get_warnText());
}

void WarnAgent::PreDecode (Buffer& buffer) throw (DecodeError)
{
	static Regex reg_host ("^" SIPREG_HOST "([:][0-9]+)?");
	static Regex reg_pseudonym ("^" SIPREG_TOKEN);
	int nLen1 = -1;
	int nLen2 = -1;
	if (reg_host.Match (buffer))
		nLen1 = reg_host.GetMatchedLength();
	if (reg_pseudonym.Match (buffer))
		nLen2 = reg_pseudonym.GetMatchedLength();
	if (nLen2 > nLen1) {
		SetHypChosenId (id_pseudonym);
		SetHypFieldLength (id_pseudonym, nLen2);
	} else
		SetHypChosenId (id_hostPort);
}

void RSeq::PreEncode (Buffer& buffer) throw (EncodeError)
{
	Get_responseNum().SetFormat(Integer::AsciiDecimal);
}

void RSeq::PreDecode (Buffer& buffer) throw (DecodeError)
{
	Get_responseNum().SetFormat(Integer::AsciiDecimal);
}

void RAck::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csWS;
	csWS.SetValue(" ");


	switch(field_id) {
	case id_responseNum:
		Get_responseNum().SetFormat(Integer::AsciiDecimal);
		break;
	case id_seqNumber:
		Get_seqNumber().SetFormat(Integer::AsciiDecimal);
	case id_method:
		csWS.Encode(buffer);
		break;
	default:
		break;
	}
}

void RAck::PreDecode (Buffer& buffer) throw (DecodeError)
{
	Get_responseNum().SetFormat(Integer::AsciiDecimal);
	Get_seqNumber().SetFormat(Integer::AsciiDecimal);
}

void RAck::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^[ ]");
	static Regex reg_method ("^" SIPREG_TOKEN);
	switch (id) { 
		case id_seqNumber:
			reg_separator.AssertMatch (buffer, this);
			reg_separator.MovePast (buffer);
			break;
		case id_method:
			reg_separator.AssertMatch (buffer, this);
			reg_separator.MovePast (buffer);
			reg_method.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_method.GetMatchedLength());
			break;
	}
}

void EventType_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void EventType_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void EventType_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_event ("^" SIPREG_TOKEN);
	reg_event.AssertMatch (buffer, this);
	SetHypFieldLength (reg_event.GetMatchedLength());
}

void EventType_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void Event::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_eventParams && IsPresent(id_eventParams)) {
		csSemi.Encode(buffer);
	}
}

void Event::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_event ("^" SIPREG_TOKEN);
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_eventType:
			reg_event.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_event.GetMatchedLength());
			break;
		case id_eventParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void SubscriptionState::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_substateParams && IsPresent(id_substateParams)) {
		csSemi.Encode(buffer);
	}
}

void SubscriptionState::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_substate ("^" SIPREG_TOKEN);
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_subState:
			reg_substate.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_substate.GetMatchedLength());
			break;
		case id_substateParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void PMediaAuthorization_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void PMediaAuthorization_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void PMediaAuthorization_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_media_authorization ("^[" SIPCHARS_HEXA "]+");
	reg_media_authorization.AssertMatch (buffer, this);
	SetHypFieldLength (reg_media_authorization.GetMatchedLength());
}

void PMediaAuthorization_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void PrivacyValue_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id != 0) {
		csSemi.Encode(buffer);
	}
}

void PrivacyValue_List::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_privacy ("^" SIPREG_TOKEN);
	reg_privacy.AssertMatch (buffer, this);
	SetHypFieldLength (reg_privacy.GetMatchedLength());
}

void PrivacyValue_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_semi (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void PAssertedIDValue_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void PAssertedIDValue_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void PAssertedIDValue_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void PPreferredIDValue_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void PPreferredIDValue_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void ReasonValue::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_reasonParams && IsPresent(id_reasonParams)) {
		csSemi.Encode(buffer);
	}
}

void ReasonValue::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^" SIPREG_TOKEN);
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_token:
			reg_token.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_token.GetMatchedLength());
			break;
		case id_reasonParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void ReasonValues::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void PathValues::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void ReasonValues::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void ReasonValues::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void PathValue::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_rrParam && IsPresent(id_rrParam)) {
		csSemi.Encode(buffer);
	}
}

void PathValue::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_rrParam:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void PathValues::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void PathValues::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void SecurityMechanism::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_mechParams && IsPresent(id_mechParams)) {
		csSemi.Encode(buffer);
	}
}

void SecurityMechanism::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^" SIPREG_TOKEN);
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_mechName:
			reg_token.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_token.GetMatchedLength());
			break;
		case id_mechParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void SecurityMechanism_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void SecurityMechanism_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void SecurityMechanism_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void NameAddrParam::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_genericParams && IsPresent(id_genericParams)) {
		csSemi.Encode(buffer);
	}
}

void NameAddrParam::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_genericParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void NameAddrParam_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void NameAddrParam_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void NameAddrParam_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void VnetworkSpec::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_genericParams && IsPresent(id_genericParams)) {
		csSemi.Encode(buffer);
	}
}

void VnetworkSpec::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^(" SIPREG_TOKEN ")|(" SIPREG_QUOTED_STRING ")");
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_vNetworkSpecToken:
			reg_token.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_token.GetMatchedLength());
			break;
		case id_genericParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void VnetworkSpec_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void VnetworkSpec_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void VnetworkSpec_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void PAccessNetworkInfo::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_genericParams && IsPresent(id_genericParams)) {
		csSemi.Encode(buffer);
	}
}

void PAccessNetworkInfo::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^" SIPREG_TOKEN);
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_accessType:
			reg_token.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_token.GetMatchedLength());
			break;
		case id_genericParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void PChargingFunctionAddresses::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^" SIPREG_TOKEN);
	switch (id) { 
		case id_chargeAddrParams:
			SetHypFieldIsPresent (id, reg_token.Match (buffer) ? 1 : 0);
			break;
	}
}

void PChargingVector::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^" SIPREG_TOKEN);
	switch (id) { 
		case id_chargeParams:
			SetHypFieldIsPresent (id, reg_token.Match (buffer) ? 1 : 0);
			break;
	}
}

void ReferTo::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_referToParams && IsPresent(id_referToParams)) {
		csSemi.Encode(buffer);
	}
}

void ReferTo::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_referToParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void ReferSub::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_referSubParams && IsPresent(id_referSubParams)) {
		csSemi.Encode(buffer);
	}
}

void ReferSub::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_referSubParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void AcRcValue_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void AcRcValue_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void AcRcValue_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void AcRcValue::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_acRcParams && IsPresent(id_acRcParams)) {
		csSemi.Encode(buffer);
	}
}

void AcRcValue::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_wildcard ("^[*]");
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) { 
		case id_wildcard:
			reg_wildcard.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_wildcard.GetMatchedLength());
			break;
		case id_acRcParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void Replaces::PreEncodeField	(int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id	== id_fieldName &&	IsPresent(id_fieldName))	{
		csSemi.Encode(buffer);
	}
}

void Replaces::PreDecodeField	(int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_fieldName:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void ReferredBy::PreEncodeField	(int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id	== id_referredbyIdParams &&	IsPresent(id_referredbyIdParams))	{
		csSemi.Encode(buffer);
	}
}

void ReferredBy::PreDecodeField	(int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_referredbyIdParams:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

/* Nothing to do
void UserToUser::PreEncodeField	(int field_id, Buffer& buffer) throw (EncodeError)
{
}

void UserToUser::PreDecodeField	(int id, Buffer& buffer) throw (DecodeError)
{
}*/

void MinSE::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_minSeParam && IsPresent(id_minSeParam)) {
		csSemi.Encode(buffer);
	}
}


void MinSE::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_delta_sec ("^[0-9]+");
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_deltaSec: 
			reg_delta_sec.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_delta_sec.GetMatchedLength());
			break;
		case id_minSeParam:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void StringList::PreEncode (Buffer& buffer) throw (EncodeError)
{
	Charstring csIndexEqual;
	csIndexEqual.SetValue("index=");

	csIndexEqual.Encode(buffer);
}

void StringList::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csDot;
	csDot.SetValue(".");

	if(field_id != 0) {
		csDot.Encode(buffer);
	}
}

void StringList::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_num ("^[0-9]+");
	SetSize (GetSize() + 1);

	reg_num.AssertMatch(buffer, this);
	SetHypFieldLength(reg_num.GetMatchedLength());
}

void StringList::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	Regex reg_dot ("^[\\x2E]");

	if (detect_separator(reg_dot, buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void HistoryInfoEntry::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csSemi;
	csSemi.SetValue(";");

	if(field_id == id_hiIndex && IsPresent(id_hiIndex)) {
		csSemi.Encode(buffer);
	}

	if(field_id == id_hiExtention && IsPresent(id_hiExtention)) {
		csSemi.Encode(buffer);
	}
}

void HistoryInfoEntry::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_index ("^" SIPREG_SEMI "[Ii][Nn][Dd][Ee][Xx][=]");
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_hiIndex: 
			if (reg_index.Match (buffer)) {				
				reg_index.MovePast (buffer);
				SetHypFieldIsPresent (id, 1);
			} else
				SetHypFieldIsPresent (id, 0);
			break;
		case id_hiExtention:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}

void HistoryInfo_List::PreEncodeField (int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void HistoryInfo_List::PreDecode (Buffer& buffer) throw (DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void HistoryInfo_List::PostDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void EM_List::PreEncodeField	(int field_id, Buffer& buffer) throw (EncodeError)
{
	Charstring csComma;
	csComma.SetValue(",");

	if(field_id != 0) {
		csComma.Encode(buffer);
	}
}

void EM_List::PreDecodeField	(int id, Buffer& buffer) throw (DecodeError) {
	static Regex reg_token ("^" SIPREG_TOKEN);
	if (GetSize() == 0)
		reg_token.AssertMatch(buffer, this);
	else if (!reg_token.Match(buffer)) {
		SetHypSize(-2);
		return;
	}
	SetHypFieldLength(reg_token.GetMatchedLength());
}

void EM_List::PreDecode (Buffer&	buffer)	throw	(DecodeError)
{
	SetHypSize (GetSize() + 1);
	SetHypAppend (1);
}

void EM_List::PostDecodeField (int	id,	Buffer&	buffer)	throw	(DecodeError)
{
	if (detect_comma (buffer))
		SetHypSize (GetSize() + 1);
	else
		SetHypSize (-2);
}

void PEarlyMedia::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^" SIPREG_TOKEN);

	switch (id) {
		case id_em_param: 
			if (reg_token.Match (buffer) || Get_em_param().GetSize() > 0) {				
				SetHypFieldIsPresent (id, 1);
			} else
				SetHypFieldIsPresent (id, 0);
			break;
	}
}

/* UserToUser: Nothing to do
void UserToUser::PreEncodeField	(int field_id, Buffer& buffer) throw (EncodeError)
{

}

void UserToUser::PreDecodeField	(int id, Buffer& buffer) throw (DecodeError)
{

}*/

void PAssertedService::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_token ("^" SIPREG_TOKEN);

	switch (id) {
		case id_pAssertedServiceValue: 
			reg_token.AssertMatch (buffer, this);
			SetHypFieldLength(id, reg_token.GetMatchedLength());
			break;
	}
}


void MessageBody::PreEncode	(Buffer& buffer) throw (EncodeError)
{
  switch (GetChosenId())
  {
    case id_xmlBody:
    case id_mimeMessageBody:
    case id_sipfrag:
      {
        std::string message ("unsupported field '");
        message += GetChosenFieldName();
        message += '\n';
        throw EncodeError (this, message);
      }
      break;
  }
}


void SessionExpires::PreDecodeField (int id, Buffer& buffer) throw (DecodeError)
{
	static Regex reg_delta_sec ("^[0-9]+");
	static Regex reg_separator ("^" SIPREG_SEMI);

	switch (id) {
		case id_deltaSec: 
			reg_delta_sec.AssertMatch (buffer, this);
			SetHypFieldLength (id, reg_delta_sec.GetMatchedLength());
			break;
		case id_seParam:
			SetHypFieldIsPresent (id, reg_separator.Match (buffer) ? 1 : 0);
			break;
	}
}


}} // namespaces
