/*
 * SDP parser helpers
 *
 * Copyright (C) 2008-2009 SOMA Networks, INC.
 * Copyright (C) 2010 VoIP Embedded, Inc
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 *  1. Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */


#include <stdlib.h>
#include "../../ut.h"
#include "../msg_parser.h"
#include "../parser_f.h"
#include "../parse_hname2.h"
#include "sdp.h"


static struct {
	const char *s;
	int len;
	int is_rtp;
} sup_ptypes[] = {
	{.s = "rtp/avp",   .len = 7, .is_rtp = 1},
	{.s = "udptl",     .len = 5, .is_rtp = 0},
	{.s = "rtp/avpf",  .len = 8, .is_rtp = 1},
	{.s = "rtp/savp",  .len = 8, .is_rtp = 1},
	{.s = "rtp/savpf", .len = 9, .is_rtp = 1},
	{.s = "udp",       .len = 3, .is_rtp = 0},
	{.s = "udp/bfcp",  .len = 8, .is_rtp = 0},
	{.s = NULL,        .len = 0, .is_rtp = 0}
};


#define READ(val) \
	((unsigned int)(*(val + 0)) \
	 + ((unsigned int)(*(val + 1)) << 8) \
	 + ((unsigned int)(*(val + 2)) << 16) \
	 + ((unsigned int)(*(val + 3)) << 24))
#define advance(_ptr,_n,_str,_error) \
	do{\
		if ((_ptr)+(_n)>(_str).s+(_str).len)\
			goto _error;\
		(_ptr) = (_ptr) + (_n);\
	}while(0);
#define one_of_16( _x , _t ) \
	(_x==_t[0]||_x==_t[15]||_x==_t[8]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
	||_x==_t[5]||_x==_t[6]||_x==_t[7]||_x==_t[1]||_x==_t[9]||_x==_t[10]\
	||_x==_t[11]||_x==_t[12]||_x==_t[13]||_x==_t[14])
#define one_of_8( _x , _t ) \
	(_x==_t[0]||_x==_t[7]||_x==_t[1]||_x==_t[2]||_x==_t[3]||_x==_t[4]\
	||_x==_t[5]||_x==_t[6])

int get_mixed_part_delimiter(str* body, str *mp_delimiter)
{
	static unsigned int boun[16] = {
	        0x6e756f62,0x4e756f62,0x6e556f62,0x4e556f62,
		0x6e754f62,0x4e754f62,0x6e554f62,0x4e554f62,
		0x6e756f42,0x4e756f42,0x6e556f42,0x4e556f42,
		0x6e754f42,0x4e754f42,0x6e554f42,0x4e554f42};
	static unsigned int dary[16] = {
	        0x79726164,0x59726164,0x79526164,0x59526164,
		0x79724164,0x59724164,0x79524164,0x59524164,
		0x79726144,0x59726144,0x79526144,0x59526144,
		0x79724144,0x59724144,0x79524144,0x59524144};
	str str_type;
	unsigned int  x;
	char          *p;


	/* LM_DBG("<%.*s>\n",body->len,body->s); */
	p = str_type.s = body->s;
	str_type.len = body->len;
	while (*p!=';' && p<(body->s+body->len))
		advance(p,1,str_type,error);
	p++;
	str_type.s = p;
	str_type.len = body->len - (p - body->s);
	/* LM_DBG("string to parse: <%.*s>\n",str_type.len,str_type.s); */
	/* skip spaces and tabs if any */
	while (*p==' ' || *p=='\t')
		advance(p,1,str_type,error);
	advance(p,4,str_type,error);
	x = READ(p-4);
	if (!one_of_16(x,boun))
		goto other;
	advance(p,4,str_type,error);
	x = READ(p-4);
	if (!one_of_16(x,dary))
		goto other;
	
	/* skip spaces and tabs if any */
	while (*p==' ' || *p=='\t')
		advance(p,1,str_type,error);
	if (*p!='=') {
		LM_ERR("parse error: no = found after boundary field\n");
		goto error;
	}
	advance(p,1,str_type,error);
	while ((*p==' ' || *p=='\t') && p+1<str_type.s+str_type.len)
		advance(p,1,str_type,error);
	mp_delimiter->len = str_type.len - (int)(p-str_type.s);
	mp_delimiter->s = p;
	/* check if the boundary value is enclosed in quotes */
	if(*p=='"' || *p=='\'') {
		if(mp_delimiter->s[mp_delimiter->len-1]==*p) {
			mp_delimiter->s = p+1;
			mp_delimiter->len -= 2;
			if(mp_delimiter->len<=0) {
				LM_ERR("invalid boundary field value\n");
				goto error;
			}
		} else {
			LM_ERR("missing closing quote in boundary field value\n");
			goto error;
		}
	}
	return 1;

error:  
	return -1;
other:  
	LM_DBG("'boundary' parsing error\n");
	return -1;
}



/**
 * rfc4566:
 * a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
 */
int extract_rtpmap(str *body,
	str *rtpmap_payload, str *rtpmap_encoding, str *rtpmap_clockrate, str *rtpmap_parmas)
{
	char *cp, *cp1;
	int len;

	if (strncasecmp(body->s, "a=rtpmap:", 9) !=0) {
		/*LM_DBG("We are not pointing to an a=rtpmap: attribute =>`%.*s'\n", body->len, body->s); */
		return -1;
	}

	cp1 = body->s;

	rtpmap_payload->s = cp1 + 9; /* skip `a=rtpmap:' */
	rtpmap_payload->len = eat_line(rtpmap_payload->s, body->s + body->len -
	          rtpmap_payload->s) - rtpmap_payload->s;
	trim_len(rtpmap_payload->len, rtpmap_payload->s, *rtpmap_payload);
	len = rtpmap_payload->len;

	/* */
	cp = eat_token_end(rtpmap_payload->s, rtpmap_payload->s + rtpmap_payload->len);
	rtpmap_payload->len = cp - rtpmap_payload->s;
	if (rtpmap_payload->len <= 0 || cp == rtpmap_payload->s) {
		LM_ERR("no encoding in `a=rtpmap'\n");
		return -1;
	}
	len -= rtpmap_payload->len;
	rtpmap_encoding->s = cp;
	cp = eat_space_end(rtpmap_encoding->s, rtpmap_encoding->s + len);
	len -= cp - rtpmap_encoding->s;
	if (len <= 0 || cp == rtpmap_encoding->s) {
		LM_ERR("no encoding in `a=rtpmap:'\n");
		return -1;
	}

	rtpmap_encoding->s = cp;
	cp1 = (char*)ser_memmem(cp, "/", len, 1);
	if(cp1==NULL) {
		LM_ERR("invalid encoding in `a=rtpmap' at [%.*s]\n", len, cp);
		return -1;
	}
	len -= cp1 - cp;
	rtpmap_encoding->len = cp1 - cp;

	cp = cp1+1;  /* skip '/' */
	len--;
	rtpmap_clockrate->s = cp;
	cp1 = (char*)ser_memmem(cp, "/", len, 1);
	if(cp1==NULL) {
		/* no encoding parameters */
		rtpmap_clockrate->len = len;
		rtpmap_parmas->s = NULL;
		rtpmap_parmas->len = 0;
		return 0;
	}
	rtpmap_clockrate->len = cp1 - cp;
	len -= cp1 - cp;
	rtpmap_parmas->s = cp1 + 1;  /* skip '/' */
	rtpmap_parmas->len = len - 1;
	return 0;
}

int extract_fmtp( str *body, str *fmtp_payload, str *fmtp_string )
{
	char *cp, *cp1;
	int len;

	if (strncasecmp(body->s, "a=fmtp:", 7) !=0) {
		/*LM_DBG("We are not pointing to an a=fmtp: attribute =>`%.*s'\n", body->len, body->s); */
		return -1;
	}

	cp1 = body->s;

	fmtp_payload->s = cp1 + 7; /* skip `a=fmtp:' */
	fmtp_payload->len = eat_line(fmtp_payload->s, body->s + body->len -
		fmtp_payload->s) - fmtp_payload->s;
	trim_len(fmtp_payload->len, fmtp_payload->s, *fmtp_payload);
	len = fmtp_payload->len;

	/* */
	cp = eat_token_end(fmtp_payload->s, fmtp_payload->s + fmtp_payload->len);
	fmtp_payload->len = cp - fmtp_payload->s;
	if (fmtp_payload->len <= 0 || cp == fmtp_payload->s) {
		LM_ERR("no encoding in `a=fmtp:'\n");
		return -1;
	}
	len -= fmtp_payload->len;
	fmtp_string->s = cp;
	cp = eat_space_end(fmtp_string->s, fmtp_string->s + len);
	len -= cp - fmtp_string->s;
	if (len <= 0 || cp == fmtp_string->s) {
		LM_ERR("no encoding in `a=fmtp:'\n");
		return -1;
	}

	fmtp_string->s = cp;

	fmtp_string->len = eat_line(fmtp_string->s, body->s + body->len -
		fmtp_string->s) - fmtp_string->s;
	trim_len(fmtp_string->len, fmtp_string->s, *fmtp_string);

	return 0;
}


/**
 * Allocate a new ice attribute
 */
static inline sdp_ice_attr_t *add_sdp_ice(sdp_stream_cell_t* _stream)
{
	sdp_ice_attr_t *ice_attr;
	int len;

	len = sizeof(sdp_ice_attr_t);
	ice_attr = (sdp_ice_attr_t *)pkg_malloc(len);
	if (ice_attr == NULL) {
	    PKG_MEM_ERROR;
	    return NULL;
	}
	memset( ice_attr, 0, len);

	/* Insert the new ice attribute */
	ice_attr->next = _stream->ice_attr;
	_stream->ice_attr = ice_attr;
	_stream->ice_attrs_num++;

	return ice_attr;
}

static inline sdp_ice_opt_t *add_sdp_ice_opt(sdp_stream_cell_t* _stream)
{
	sdp_ice_opt_t *ice_opt;
	int len;

	len = sizeof(sdp_ice_opt_t);
	ice_opt = (sdp_ice_opt_t *)pkg_malloc(len);
	if (ice_opt == NULL) {
	    PKG_MEM_ERROR;
	    return NULL;
	}
	memset( ice_opt, 0, len);

	/* Insert the new ice option */
	ice_opt->next = _stream->ice_opt;
	_stream->ice_opt = ice_opt;
	_stream->ice_opt_num++;

	return ice_opt;
}

int extract_candidate(str *body, sdp_stream_cell_t *stream)
{
    char *space, *start;
    int len, fl;
    sdp_ice_attr_t *ice_attr;

    if ((body->len < 12) || (strncasecmp(body->s, "a=candidate:", 12) != 0)) {
	/*LM_DBG("We are not pointing to an a=candidate: attribute =>`%.*s'\n", body->len, body->s); */
	return -1;
    }
    
    start = body->s + 12;
    len = body->len - 12;

    space = memchr(start, 32, len);
    if ((space == NULL) || (space - start + 3 > len) || !isdigit(*(space + 1)))  {
	LM_ERR("no component in `a=candidate'\n");
	return -1;
    }

    fl = space - start;
    
    start = space + 1;
    len = len - (space - start + 1);
    space = memchr(start, 32, len);
    if (space == NULL) {
	LM_ERR("no component in `a=candidate'\n");
	return -1;
    }

    ice_attr = add_sdp_ice(stream);
    if (ice_attr == NULL) {
	LM_ERR("failed to add ice attribute\n");
	return -1;
    }

    /* currently only foundation and component-id are parsed */
    /* if needed, parse more */
    ice_attr->foundation.s = body->s + 12;
    ice_attr->foundation.len = fl;
    ice_attr->component_id = strtol(start, (char **)NULL, 10);

    return 0;
}


/* generic method for attribute extraction
 * field must has format "a=attrname:" */
int extract_field(str *body, str *value, str field)
{
	if (strncmp(body->s, field.s, field.len < body->len ? field.len : body->len) !=0) {
		/*LM_DBG("We are not pointing to an %.* attribute =>`%.*s'\n", field.len, field.s, body->len, body->s); */
		return -1;
	}

	value->s = body->s + field.len; /* skip `a=attrname:' */
	value->len = eat_line(value->s, body->s + body->len -
	          value->s) - value->s;
	trim_len(value->len, value->s, *value);

	return 0;
}

int extract_ice_option(str *body, sdp_stream_cell_t *stream)
{
	sdp_ice_opt_t *ice_opt;

	char * ptr_src;
	int max_options = 10; /* protection - max options can be listed in one line */
	int length = 0;       /* each option length */

	/* a=ice-options: */
	if ((body->len < 14) || (strncasecmp(body->s, ICE_OPTIONS, 14) != 0))
		return -1;

	ptr_src = body->s + 14;
	if (*ptr_src == 32) ptr_src++; /* if starts with a space, skip it */

	/* identify all existing ICE options, if they are listed in one row */
	while (*ptr_src && *ptr_src != '\r' && *ptr_src != '\n' && max_options-->0)
	{
		while (*ptr_src != 32 && *ptr_src && *ptr_src != '\r' && *ptr_src != '\n')
		{
			length++;
			ptr_src++;
		}

		ice_opt = add_sdp_ice_opt(stream);
		if (ice_opt == NULL) {
			LM_ERR("failed to add ice option\n");
			return -1;
		}

		ice_opt->option.s = ptr_src-length;
		ice_opt->option.len = length;
		trim_len(ice_opt->option.len, ice_opt->option.s, ice_opt->option);

		length = 0;
		if (*ptr_src == 32) ptr_src++; /* skip space */
	}

	return 0;
}

int extract_ptime(str *body, str *ptime)
{
	static const str field = str_init("a=ptime:");
	return extract_field(body, ptime, field);
}

int extract_accept_types(str *body, str *accept_types)
{
	static const str field = str_init("a=accept-types:");
	return extract_field(body, accept_types, field);
}

int extract_accept_wrapped_types(str *body, str *accept_wrapped_types)
{
	static const str field = str_init("a=accept-wrapped-types:");
	return extract_field(body, accept_wrapped_types, field);
}

int extract_max_size(str *body, str *max_size)
{
	static const str field = str_init("a=max-size:");
	return extract_field(body, max_size, field);
}

int extract_path(str *body, str *path)
{
	static const str field = str_init("a=path:");
	return extract_field(body, path, field);
}

int extract_rtcp(str *body, str *rtcp)
{
	static const str field = str_init("a=rtcp:");
	return extract_field(body, rtcp, field);
}

int extract_sendrecv_mode(str *body, str *sendrecv_mode, int *is_on_hold)
{
	char *cp1;

	cp1 = body->s;
	if ( !( (strncasecmp(cp1, "a=sendrecv", 10) == 0) ||
		(strncasecmp(cp1, "a=recvonly", 10) == 0))) {
		if ((strncasecmp(cp1, "a=inactive", 10) == 0) ||
		    (strncasecmp(cp1, "a=sendonly", 10) == 0) ) {
			*is_on_hold = RFC3264_HOLD;
		} else {
			return -1;
		}
	}

	sendrecv_mode->s = body->s + 2; /* skip `a=' */
	sendrecv_mode->len = 8; /* we know the length and therefore we don't need to overkill */
	/*
	sendrecv_mode->len = eat_line(sendrecv_mode->s, body->s + body->len -
		sendrecv_mode->s) - sendrecv_mode->s;
	trim_len(sendrecv_mode->len, sendrecv_mode->s, *sendrecv_mode);
	*/

	return 0;
}

int extract_bwidth(str *body, str *bwtype, str *bwwitdth)
{
	char *cp, *cp1;
	int len;

	cp1 = NULL;
	for (cp = body->s; (len = body->s + body->len - cp) > 0;) {
		cp1 = (char*)ser_memmem(cp, "b=", len, 2);
		if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r')
			break;
		cp = cp1 + 2;
	}
	if (cp1 == NULL)
		return -1;

	bwtype->s = cp1 + 2;
	bwtype->len = eat_line(bwtype->s, body->s + body->len - bwtype->s) - bwtype->s;
	trim_len(bwtype->len, bwtype->s, *bwtype);

	cp = bwtype->s;
	len = bwtype->len;
	cp1 = (char*)ser_memmem(cp, ":", len, 1);
	len -= cp1 - cp;
	if (len <= 0) {
		LM_ERR("invalid encoding in `b=%.*s'\n", bwtype->len, bwtype->s);
		return -1;
	}
	bwtype->len = cp1 - cp;

	/* skip ':' */
	bwwitdth->s = cp1 + 1;
	bwwitdth->len = len - 1;
	
	return 0;
}

int extract_mediaip(str *body, str *mediaip, int *pf, char *line)
{
	char *cp, *cp1;
	int len;

	cp1 = NULL;
	for (cp = body->s; (len = body->s + body->len - cp) > 0;) {
		cp1 = (char*)ser_memmem(cp, line, len, 2);
		if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r')
			break;
		cp = cp1 + 2;
	}
	if (cp1 == NULL)
		return -1;

	mediaip->s = cp1 + 2;
	mediaip->len = eat_line(mediaip->s, body->s + body->len - mediaip->s) - mediaip->s;
	trim_len(mediaip->len, mediaip->s, *mediaip);
	if (mediaip->len == 0) {
		LM_ERR("no [%s] line in SDP\n",line);
		return -1;
	}

	/* search reverse for IP[4|6] in c=/o= line */
	cp = (char*)ser_memrmem(mediaip->s, " IP", mediaip->len, 3);
	if (cp == NULL) {
		LM_ERR("no `IP[4|6]' in `%s' field\n",line);
		return -1;
	}
	/* safety checks:
	 * - for length, at least 6: ' IP[4|6] x...'
	 * - white space after
	 */
	if(cp + 6 > mediaip->s + mediaip->len && cp[4]!=' ') {
		LM_ERR("invalid content for `%s' line\n",line);
		return -1;
	}
	switch(cp[3]) {
		case '4':
			*pf = AF_INET;
		break;
		case '6':
			*pf = AF_INET6;
		break;
		default:
			LM_ERR("invalid addrtype IPx for `%s' line\n",line);
			return -1;
	}
	cp += 5;

	/* next token is the IP address */
	cp = eat_space_end(cp, mediaip->s + mediaip->len);
	len = eat_token_end(cp, mediaip->s + mediaip->len) - cp;
	mediaip->s = cp;
	mediaip->len = len;

	if (mediaip->len == 0) {
		LM_ERR("no `IP[4|6]' address in `%s' field\n",line);
		return -1;
	}

	LM_DBG("located IP address [%.*s] in `%s' field\n",
			mediaip->len, mediaip->s, line);
	return 1;
}

int extract_media_attr(str *body, str *mediamedia, str *mediaport, str *mediatransport, str *mediapayload, int *is_rtp)
{
	char *cp, *cp1;
	int len, i;

	cp1 = NULL;
	for (cp = body->s; (len = body->s + body->len - cp) > 0;) {
		cp1 = (char*)ser_memmem(cp, "m=", len, 2);
		if (cp1 == NULL || cp1[-1] == '\n' || cp1[-1] == '\r')
			break;
		cp = cp1 + 2;
	}
	if (cp1 == NULL) {
		LM_ERR("no `m=' in SDP\n");
		return -1;
	}
	mediaport->s = cp1 + 2; /* skip `m=' */
	mediaport->len = eat_line(mediaport->s, body->s + body->len -
	  mediaport->s) - mediaport->s;
	trim_len(mediaport->len, mediaport->s, *mediaport);

	mediapayload->len = mediaport->len;
	mediamedia->s = mediaport->s;


	/* Skip media supertype and spaces after it */
	cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len);
	mediaport->len -= cp - mediaport->s;
	mediamedia->len = mediapayload->len - mediaport->len;
	if (mediaport->len <= 0 || cp == mediaport->s) {
		LM_ERR("no port in `m='\n");
		return -1;
	}
	mediaport->s = cp;

	cp = eat_space_end(mediaport->s, mediaport->s + mediaport->len);
	mediaport->len -= cp - mediaport->s;
	if (mediaport->len <= 0 || cp == mediaport->s) {
		LM_ERR("no port in `m='\n");
		return -1;
	}

	/* Extract port */
	mediaport->s = cp;
	cp = eat_token_end(mediaport->s, mediaport->s + mediaport->len);
	mediatransport->len = mediaport->len - (cp - mediaport->s);
	if (mediatransport->len <= 0 || cp == mediaport->s) {
		LM_ERR("no port in `m='\n");
		return -1;
	}
	mediatransport->s = cp;
	mediaport->len = cp - mediaport->s;

	/* Skip spaces after port */
	cp = eat_space_end(mediatransport->s, mediatransport->s + mediatransport->len);
	mediatransport->len -= cp - mediatransport->s;
	if (mediatransport->len <= 0 || cp == mediatransport->s) {
		LM_ERR("no protocol type in `m='\n");
		return -1;
	}
	/* Extract protocol type */
	mediatransport->s = cp;
	cp = eat_token_end(mediatransport->s, mediatransport->s + mediatransport->len);
	if (cp == mediatransport->s) {
		LM_ERR("no protocol type in `m='\n");
		return -1;
	}
	mediatransport->len = cp - mediatransport->s;

	mediapayload->s = mediatransport->s + mediatransport->len;
	mediapayload->len -= mediapayload->s - mediamedia->s;
	cp = eat_space_end(mediapayload->s, mediapayload->s + mediapayload->len);
	mediapayload->len -= cp - mediapayload->s;
	mediapayload->s = cp;

	for (i = 0; sup_ptypes[i].s != NULL; i++)
		if (mediatransport->len == sup_ptypes[i].len &&
		    strncasecmp(mediatransport->s, sup_ptypes[i].s, mediatransport->len) == 0) {
			*is_rtp = sup_ptypes[i].is_rtp;
			return 0;
		}
	/* Unproxyable protocol type. Generally it isn't error. */
	return 0;
}

int extract_sess_version(str* oline, str* sess_version) {

	char* cp0 = NULL;
	char* cp = NULL;
	int len = 0;
	char ws = ' ';
	int i=0;

	/*
	"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5" CR LF
	"o=W 1 2 IN IP4 0.0.0.0" CR LF -> 24
	"o=W 1 2 IN IP6 ::1" CR LF -> 20
	"o=W 1 2 X Y Z" CR LF -> 15
	*/
	if ( oline->s == NULL || oline->len < 15 )
	{
		LM_ERR("invalid o -line\n");
		return -1;
	}

	if ( sess_version == NULL )
	{
		LM_ERR("invalid result pointer\n");
		return -1;
	}

	LM_DBG("oline(%d): >%.*s<\n", oline->len, oline->len, oline->s);

	// jump over o=
	cp = oline->s + 2;
	len = oline->len - 2;

	// find whitespace 3 times
	do
	{
		cp0=cp;
		//LM_DBG("loop %d: >%.*s<\n", len, len, cp0);

		cp = (char*)ser_memmem(cp0, &ws, len, 1);
		if ( cp == NULL) { break; }

		//LM_DBG("cp0: %p cp: %p (%ld)\n", cp0, cp, cp-cp0);
		len-=cp-cp0;

		// step over whitespace
		if ( len > 0 )
		{
			cp++;
			len--;
		}

		i++;
	} while ( len < oline->len && i < 3 );

	len = cp-cp0-1;
	LM_DBG("end %d: >%.*s<\n", len, len, cp0);

	sess_version->s = cp0;
	sess_version->len = len;

	if (!isdigit(*cp0))
	{
		LM_WARN("not digit >%.*s<\n", len, cp0);
	}

	return 1;
}


/*
 * Auxiliary for some functions.
 * Returns pointer to first character of found line, or NULL if no such line.
 */

char *find_sdp_line(char* p, char* plimit, char linechar)
{
	static char linehead[3] = "x=";
	char *cp, *cp1;
	linehead[0] = linechar;
	/* Iterate through body */
	cp = p;
	for (;;) {
		if (cp >= plimit)
			return NULL;
		cp1 = ser_memmem(cp, linehead, plimit-cp, 2);
		if (cp1 == NULL)
			return NULL;
		/*
		 * As it is body, we assume it has previous line and we can
		 * lookup previous character.
		 */
		if (cp1[-1] == '\n' || cp1[-1] == '\r')
			return cp1;
		/*
		 * Having such data, but not at line beginning.
		 * Skip them and reiterate. ser_memmem() will find next
		 * occurence.
		 */
		if (plimit - cp1 < 2)
			return NULL;
		cp = cp1 + 2;
	}
}


/* This function assumes p points to a line of requested type. */
char * find_next_sdp_line(char* p, char* plimit, char linechar, char* defptr)
{
	char *t;
	if (p >= plimit || plimit - p < 3)
		return defptr;
	t = find_sdp_line(p + 2, plimit, linechar);
	return t ? t : defptr;
}


/* Find first SDP line starting with linechar. Return defptr if not found */
char * find_first_sdp_line(char* pstart, char* plimit, char linechar,
		char* defptr)
{
	char *t;
	if (pstart >= plimit || plimit - pstart < 3)
		return defptr;
	t = find_sdp_line(pstart, plimit, linechar);
	return t ? t : defptr;
}


/* returns pointer to next header line, and fill hdr_f ;
 * if at end of header returns pointer to the last crlf  (always buf)*/
char* get_sdp_hdr_field(char* buf, char* end, struct hdr_field* hdr)
{

	char* tmp;
	char *match;

	if ((*buf)=='\n' || (*buf)=='\r'){
		/* double crlf or lflf or crcr */
		hdr->type=HDR_EOH_T;
		return buf;
	}

	tmp=parse_hname2(buf, end, hdr);
	if (hdr->type==HDR_ERROR_T){
		LM_ERR("bad header\n");
		goto error;
	}

	/* eliminate leading whitespace */
	tmp=eat_lws_end(tmp, end);
	if (tmp>=end) {
		LM_ERR("hf empty\n");
		goto error;
	}

	/* just skip over it */
	hdr->body.s=tmp;
	/* find end of header */
	/* find lf */
	do{
		match=memchr(tmp, '\n', end-tmp);
		if (match){
			match++;
		}else {
			LM_ERR("bad body for <%.*s>(%d)\n", hdr->name.len, hdr->name.s,
					hdr->type);
			tmp=end;
			goto error;
		}
		tmp=match;
	}while( match<end &&( (*match==' ')||(*match=='\t') ) );
	tmp=match;
	hdr->body.len=match-hdr->body.s;

	/* jku: if \r covered by current length, shrink it */
	trim_r( hdr->body );
	hdr->len=tmp-hdr->name.s;
	return tmp;
error:
	LM_DBG("error exit\n");
	hdr->type=HDR_ERROR_T;
	hdr->len=tmp-hdr->name.s;
	return tmp;
}



char *find_sdp_line_delimiter(char* p, char* plimit, str delimiter)
{
	static char delimiterhead[3] = "--";
	char *cp, *cp1;
	/* Iterate through body */
	cp = p;
	for (;;) {
		if (cp >= plimit)
			return NULL;
		for(;;) {
			cp1 = ser_memmem(cp, delimiterhead, plimit-cp, 2);
			if (cp1 == NULL)
				return NULL;
			/* We matched '--',
			 * now let's match the boundary delimiter */
			if(cp1+2+delimiter.len >= plimit)
				return NULL;
			if (strncmp(cp1+2, delimiter.s, delimiter.len) == 0)
				break;
			else
				cp = cp1 + 2 + delimiter.len;
		}
		if (cp1[-1] == '\n' || cp1[-1] == '\r')
			return cp1;
		if (plimit - cp1 < 2 + delimiter.len)
			return NULL;
		cp = cp1 + 2 + delimiter.len;
	}
}


/*
 * This function assumes p points to a delimiter type line.
 */
char *find_next_sdp_line_delimiter(char* p, char* plimit, str delimiter, char* defptr)
{
	char *t;
	if (p >= plimit || plimit - p < 3)
		return defptr;
	t = find_sdp_line_delimiter(p + 2, plimit, delimiter);
	return t ? t : defptr;
}

