type SIP_TOKEN  = RE/[a-zA-Z0-9_.!%*+`'~-]+/;
type NOT_SIP_TOKEN  = RE/[^a-zA-Z0-9_.!%*+`'~-]*/;
type SIP_WS     = RE/[ \t]*/;
type SIP_URI    = RE/[[:alnum:]@[:punct:]]+/;

type SIP_PDU(is_orig: bool) = case is_orig of {
	true  ->	request:	SIP_Request;
	false ->	reply:		SIP_Reply;
};

type SIP_Request = record {
	request:	SIP_RequestLine &oneline;
	msg:		SIP_Message;
};

type SIP_Reply = record {
	reply:		SIP_ReplyLine &oneline;
	msg:		SIP_Message;
};

type SIP_RequestLine = record {
	junk:		NOT_SIP_TOKEN;
	method:		SIP_TOKEN;
	:		SIP_WS;
	uri:		SIP_URI;
	:		SIP_WS;
	version:	SIP_Version &restofdata;
} &oneline;

type SIP_ReplyLine = record {
	version:	SIP_Version;
	:		SIP_WS;
	status:		SIP_Status;
	:		SIP_WS;
	reason:		bytestring &restofdata;
} &oneline;

type SIP_Status = record {
	stat_str:	RE/[0-9]{3}/;
} &let {
	stat_num: int = bytestring_to_int(stat_str, 10);
};

type SIP_Version = record {
	:		"SIP/";
	vers_str:	RE/[0-9]+\.[0-9]+/;
} &let {
	vers_num:	double = bytestring_to_double(vers_str);
};

type SIP_Headers = SIP_Header[] &until($input.length() == 0);

type SIP_Message = record {
	headers:	SIP_Headers;
	body:		SIP_Body;
};

type SIP_HEADER_NAME = RE/[^: \t]+/;
type SIP_Header = record {
	name:	SIP_HEADER_NAME;
	:	SIP_WS;
	:	":";
	:	SIP_WS;
	value:	bytestring &restofdata;
} &oneline;

type SIP_Body = record {
	 body:	bytestring &length = $context.flow.get_content_length();
};
