2
0
Эх сурвалжийг харах

SDP document parser

Signed-off-by: Jan Janak <[email protected]>
Ovidiu Sas 16 жил өмнө
parent
commit
450962d056

+ 1201 - 0
parser/sdp/sdp.c

@@ -0,0 +1,1201 @@
+/*
+ * $Id: sdp.c 5542 2009-01-29 18:57:09Z osas $
+ *
+ * SDP parser interface
+ *
+ * Copyright (C) 2008-2009 SOMA Networks, 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 "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
+#include "../parser_f.h"
+#include "../parse_content.h"
+#include "sdp.h"
+#include "sdp_helpr_funcs.h"
+
+#define USE_PKG_MEM 0
+#define USE_SHM_MEM 1
+
+/**
+ * Creates and initialize a new sdp_info structure
+ */
+static inline int new_sdp(struct sip_msg* _m)
+{
+	sdp_info_t* sdp;
+
+	sdp = (sdp_info_t*)pkg_malloc(sizeof(sdp_info_t));
+	if (sdp == 0) {
+		LM_ERR("No memory left\n");
+		return -1;
+	}
+	memset( sdp, 0, sizeof(sdp_info_t));
+		
+	_m->sdp = sdp;
+
+	return 0;
+}
+
+/**
+ * Alocate a new session cell.
+ */
+static inline sdp_session_cell_t *add_sdp_session(sdp_info_t* _sdp, int session_num, str* cnt_disp)
+{
+	sdp_session_cell_t *session;
+	int len;
+
+	len = sizeof(sdp_session_cell_t);
+	session = (sdp_session_cell_t*)pkg_malloc(len);
+	if (session == 0) {
+		LM_ERR("No memory left\n");
+		return 0;
+	}
+	memset( session, 0, len);
+
+	session->session_num = session_num;
+	if (cnt_disp != NULL) {
+		session->cnt_disp.s = cnt_disp->s;
+		session->cnt_disp.len = cnt_disp->len;
+	}
+
+	/* Insert the new session */
+	session->next = _sdp->sessions;
+	_sdp->sessions = session;
+	_sdp->sessions_num++;
+
+	return session;
+}
+
+/**
+ * Allocate a new stream cell.
+ */
+static inline sdp_stream_cell_t *add_sdp_stream(sdp_session_cell_t* _session, int stream_num,
+		str* media, str* port, str* transport, str* payloads, int pf, str* sdp_ip)
+{
+	sdp_stream_cell_t *stream;
+	int len;
+
+	len = sizeof(sdp_stream_cell_t);
+	stream = (sdp_stream_cell_t*)pkg_malloc(len);
+	if (stream == 0) {
+		LM_ERR("No memory left\n");
+		return 0;
+	}
+	memset( stream, 0, len);
+
+	stream->stream_num = stream_num;
+
+	stream->media.s = media->s;
+	stream->media.len = media->len;
+	stream->port.s = port->s;
+	stream->port.len = port->len;
+	stream->transport.s = transport->s;
+	stream->transport.len = transport->len;
+	stream->payloads.s = payloads->s;
+	stream->payloads.len = payloads->len;
+
+	stream->pf = pf;
+	stream->ip_addr.s = sdp_ip->s;
+	stream->ip_addr.len = sdp_ip->len;
+
+	/* Insert the new stream */
+	stream->next = _session->streams;
+	_session->streams = stream;
+	_session->streams_num++;
+
+	return stream;
+}
+
+/**
+ * Allocate a new payload.
+ */
+static inline sdp_payload_attr_t *add_sdp_payload(sdp_stream_cell_t* _stream, int payload_num, str* payload)
+{
+	sdp_payload_attr_t *payload_attr;
+	int len;
+
+	len = sizeof(sdp_payload_attr_t);
+	payload_attr = (sdp_payload_attr_t*)pkg_malloc(len);
+	if (payload_attr == 0) {
+		LM_ERR("No memory left\n");
+		return 0;
+	}
+	memset( payload_attr, 0, len);
+
+	payload_attr->payload_num = payload_num;
+	payload_attr->rtp_payload.s = payload->s;
+	payload_attr->rtp_payload.len = payload->len;
+
+	/* Insert the new payload */
+	payload_attr->next = _stream->payload_attr;
+	_stream->payload_attr = payload_attr;
+	_stream->payloads_num++;
+
+	return payload_attr;
+}
+
+/**
+ * Initialize fast access pointers.
+ */
+static inline sdp_payload_attr_t** init_p_payload_attr(sdp_stream_cell_t* _stream, int pkg)
+{
+	int payloads_num, i;
+	sdp_payload_attr_t *payload;
+
+	if (_stream == 0) {
+		LM_ERR("Invalid stream\n");
+		return 0;
+	}
+	payloads_num = _stream->payloads_num;
+	if (payloads_num == 0) {
+		LM_ERR("Invalid number of payloads\n");
+		return 0;
+	}
+	if (pkg == USE_PKG_MEM) {
+		_stream->p_payload_attr = (sdp_payload_attr_t**)pkg_malloc(payloads_num * sizeof(sdp_payload_attr_t*));
+	} else if (pkg == USE_SHM_MEM) {
+		_stream->p_payload_attr = (sdp_payload_attr_t**)shm_malloc(payloads_num * sizeof(sdp_payload_attr_t*));
+	} else {
+		LM_ERR("undefined memory type\n");
+		return 0;
+	}
+	if (_stream->p_payload_attr == 0) {
+		LM_ERR("No memory left\n");
+		return 0;
+	}
+
+	--payloads_num;
+	payload = _stream->payload_attr;
+	for (i=0;i<=payloads_num;i++) {
+		_stream->p_payload_attr[payloads_num-i] = payload;
+		payload = payload->next;
+	}
+
+	return _stream->p_payload_attr;
+}
+
+/*
+ * Setters ...
+ */
+
+void set_sdp_payload_attr(sdp_payload_attr_t *payload_attr, str *rtp_enc, str *rtp_clock, str *rtp_params)
+{
+	if (payload_attr == 0) {
+		LM_ERR("Invalid payload location\n");
+		return;
+	}
+	payload_attr->rtp_enc.s = rtp_enc->s;
+	payload_attr->rtp_enc.len = rtp_enc->len;
+	payload_attr->rtp_clock.s = rtp_clock->s;
+	payload_attr->rtp_clock.len = rtp_clock->len;
+	payload_attr->rtp_params.s = rtp_params->s;
+	payload_attr->rtp_params.len = rtp_params->len;
+
+	return;
+}
+
+void set_sdp_ptime_attr(sdp_stream_cell_t *stream, sdp_payload_attr_t *payload_attr, str *ptime)
+{
+	sdp_payload_attr_t *payload;
+	int i;
+
+	if (payload_attr == 0) {
+		/* This is a generic attribute
+		 * Let's update all payloads */
+		for (i=0;i<stream->payloads_num;i++) {
+			payload = stream->p_payload_attr[i];
+			payload->ptime.s = ptime->s;
+			payload->ptime.len = ptime->len;
+		}
+	} else {
+		payload_attr->ptime.s = ptime->s;
+		payload_attr->ptime.len = ptime->len;
+	}
+	return;
+}
+
+void set_sdp_sendrecv_mode_attr(sdp_stream_cell_t *stream, sdp_payload_attr_t *payload_attr, str *sendrecv_mode)
+{
+	sdp_payload_attr_t *payload;
+	int i;
+
+	if (payload_attr == 0) {
+		/* This is a generic attribute
+		 * Let's update all payloads */
+		for (i=0;i<stream->payloads_num;i++) {
+			payload = stream->p_payload_attr[i];
+			payload->sendrecv_mode.s = sendrecv_mode->s;
+			payload->sendrecv_mode.len = sendrecv_mode->len;
+		}
+	} else {
+		payload_attr->sendrecv_mode.s = sendrecv_mode->s;
+		payload_attr->sendrecv_mode.len = sendrecv_mode->len;
+	}
+
+	return;
+}
+
+/*
+ * Getters ....
+ */
+
+sdp_session_cell_t* get_sdp_session_sdp(struct sdp_info* sdp, int session_num)
+{
+	sdp_session_cell_t *session;
+
+	session = sdp->sessions;
+	if (session_num > sdp->sessions_num)
+		return NULL;
+	while (session) {
+		if (session->session_num == session_num) {
+			return session;
+		} else {
+			session = session->next;
+		}
+	}
+	return NULL;
+}
+
+sdp_session_cell_t* get_sdp_session(struct sip_msg* _m, int session_num)
+{
+	if (_m->sdp == NULL) return NULL;
+	return get_sdp_session_sdp(_m->sdp, session_num);
+}
+
+
+sdp_stream_cell_t* get_sdp_stream_sdp(struct sdp_info* sdp, int session_num, int stream_num)
+{
+	sdp_session_cell_t *session;
+	sdp_stream_cell_t *stream;
+
+	if (sdp==NULL) 
+		return NULL;
+	if (session_num > sdp->sessions_num)
+		return NULL;
+	session = sdp->sessions;
+	while (session) {
+		if (session->session_num == session_num) {
+			stream = session->streams;
+			while (stream) {
+				if (stream->stream_num == stream_num) return stream;
+				stream = stream->next;
+			}
+		} else {
+			session = session->next;
+		}
+	}
+
+	return NULL;
+}
+
+sdp_stream_cell_t* get_sdp_stream(struct sip_msg* _m, int session_num, int stream_num)
+{
+        sdp_session_cell_t *session;
+	sdp_stream_cell_t *stream;
+
+	return get_sdp_stream_sdp(_m->sdp, session_num, stream_num);
+
+        if (_m->sdp == NULL) return NULL;
+        if (session_num > _m->sdp->sessions_num) return NULL;
+
+	session = _m->sdp->sessions;
+	while (session) {
+		if (session->session_num == session_num) {
+			stream = session->streams;
+			while (stream) {
+				if (stream->stream_num == stream_num) return stream;
+				stream = stream->next;
+			}
+		} else {
+			session = session->next;
+		}
+	}
+
+	return NULL;
+}
+
+
+sdp_payload_attr_t* get_sdp_payload4payload(sdp_stream_cell_t *stream, str *rtp_payload)
+{
+	sdp_payload_attr_t *payload;
+	int i;
+
+	if (stream == NULL) {
+		LM_ERR("Invalid stream location\n");
+		return NULL;
+	}
+	if (stream->p_payload_attr == NULL) {
+		LM_ERR("Invalid access pointer to payloads\n");
+		return NULL;
+	}
+
+	for (i=0;i<stream->payloads_num;i++) {
+		payload = stream->p_payload_attr[i];
+		if (rtp_payload->len == payload->rtp_payload.len &&
+			(strncmp(rtp_payload->s, payload->rtp_payload.s, rtp_payload->len)==0)) {
+			return payload;
+		}
+	}
+
+	return NULL;
+}
+
+sdp_payload_attr_t* get_sdp_payload4index(sdp_stream_cell_t *stream, int index)
+{
+	if (stream == NULL) {
+		LM_ERR("Invalid stream location\n");
+		return NULL;
+	}
+	if (stream->p_payload_attr == NULL) {
+		LM_ERR("Invalid access pointer to payloads\n");
+		return NULL;
+	}
+	if (index >= stream->payloads_num) {
+		LM_ERR("Out of range index [%d] for payload\n", index);
+		return NULL;
+	}
+
+	return stream->p_payload_attr[index];
+}
+
+
+/**
+ * SDP parser method.
+ */
+static int parse_sdp_session(str *sdp_body, int session_num, str *cnt_disp, sdp_info_t* _sdp)
+{
+	str body = *sdp_body;
+	str sdp_ip, sdp_media, sdp_port, sdp_transport, sdp_payload;
+	str payload;
+	str rtp_payload, rtp_enc, rtp_clock, rtp_params, ptime, sendrecv_mode;
+	char *bodylimit;
+	char *v1p, *o1p, *m1p, *m2p, *c1p, *c2p, *a1p, *a2p, *b1p;
+	str tmpstr1;
+	int stream_num, payloadnum, pf;
+	sdp_session_cell_t *session;
+	sdp_stream_cell_t *stream;
+	sdp_payload_attr_t *payload_attr;
+	int parse_payload_attr;
+
+	/*
+	 * Parsing of SDP body.
+	 * Each session starts with v-line and each session may contain a few
+	 * media descriptions (each starts with m-line).
+	 * We have to change ports in m-lines, and also change IP addresses in
+	 * c-lines which can be placed either in session header (fallback for
+	 * all medias) or media description.
+	 * Ports should be allocated for any media. IPs all should be changed
+	 * to the same value (RTP proxy IP), so we can change all c-lines
+	 * unconditionally.
+	 */
+	bodylimit = body.s + body.len;
+	v1p = find_sdp_line(body.s, bodylimit, 'v');
+	if (v1p == NULL) {
+		LM_ERR("no sessions in SDP\n");
+		return -1;
+	}
+	/* get session origin */
+	o1p = find_sdp_line(v1p, bodylimit, 'o');
+	if (o1p==0) {
+		LM_ERR("no o= in session\n");
+		return -1;
+	}
+	/* Have this session media description? */
+	m1p = find_sdp_line(o1p, bodylimit, 'm');
+	if (m1p == NULL) {
+		LM_ERR("no m= in session\n");
+		return -1;
+	}
+	/* Allocate a session cell */
+	session = add_sdp_session(_sdp, session_num, cnt_disp);
+	if (session == 0) return -1;
+
+	/* Find c1p only between session begin and first media.
+	 * c1p will give common c= for all medias. */
+	c1p = find_sdp_line(o1p, m1p, 'c');
+
+	/* Find b1p only between session begin and first media.
+	 * b1p will give common b= for all medias. */
+	b1p = find_sdp_line(o1p, m1p, 'b');
+	if (b1p) {
+		tmpstr1.s = b1p;
+		tmpstr1.len = m1p - b1p;
+		extract_bwidth(&tmpstr1, &session->bw_type, &session->bw_width);
+	}
+
+	/* Have session. Iterate media descriptions in session */
+	m2p = m1p;
+	stream_num = 0;
+	for (;;) {
+		m1p = m2p; 
+		if (m1p == NULL || m1p >= bodylimit)
+			break;
+		m2p = find_next_sdp_line(m1p, bodylimit, 'm', bodylimit);
+		/* c2p will point to per-media "c=" */
+		c2p = find_sdp_line(m1p, m2p, 'c');
+
+		/* Extract address and port */
+		tmpstr1.s = c2p ? c2p : c1p;
+		if (tmpstr1.s == NULL) {
+			/* No "c=" */
+			LM_ERR("can't find media IP in the message\n");
+			return -1;
+		}
+
+		/* Extract the IP on sdp_ip */
+		tmpstr1.len = bodylimit - tmpstr1.s; /* limit is session limit text */
+		if (extract_mediaip(&tmpstr1, &sdp_ip, &pf,"c=") == -1) {
+			LM_ERR("can't extract media IP from the message\n");
+			return -1;
+		}
+
+		/* Extract the port on sdp_port */
+		tmpstr1.s = m1p;
+		tmpstr1.len = m2p - m1p;
+		if (extract_media_attr(&tmpstr1, &sdp_media, &sdp_port, &sdp_transport, &sdp_payload) == -1) {
+			LM_ERR("can't extract media attr from the message\n");
+			return -1;
+		}
+
+		/* Allocate a stream cell */
+		stream = add_sdp_stream(session, stream_num, &sdp_media, &sdp_port, &sdp_transport, &sdp_payload, pf, &sdp_ip);
+		if (stream == 0) return -1;
+
+		/* b1p will point to per-media "b=" */
+		b1p = find_sdp_line(m1p, m2p, 'b');
+		if (b1p) {
+			tmpstr1.s = b1p;
+			tmpstr1.len = m2p - b1p;
+			extract_bwidth(&tmpstr1, &stream->bw_type, &stream->bw_width);
+		}
+
+		/* Parsing the payloads */
+		tmpstr1.s = sdp_payload.s;
+		tmpstr1.len = sdp_payload.len;
+		payloadnum = 0;
+		if (tmpstr1.len != 0) {
+			for (;;) {
+				a1p = eat_token_end(tmpstr1.s, tmpstr1.s + tmpstr1.len);
+				payload.s = tmpstr1.s;
+				payload.len = a1p - tmpstr1.s;
+				payload_attr = add_sdp_payload(stream, payloadnum, &payload);
+				if (payload_attr == 0) return -1;
+				tmpstr1.len -= payload.len;
+				tmpstr1.s = a1p;
+				a2p = eat_space_end(tmpstr1.s, tmpstr1.s + tmpstr1.len);
+				tmpstr1.len -= a2p - a1p;
+				tmpstr1.s = a2p;
+				if (a1p >= tmpstr1.s)
+					break;
+				payloadnum++;
+			}
+
+			/* Initialize fast access pointers */
+			if (0 == init_p_payload_attr(stream, USE_PKG_MEM)) {
+				return -1;
+			}
+			parse_payload_attr = 1;
+		} else {
+			parse_payload_attr = 0;
+		}
+
+		payload_attr = 0;
+		/* Let's figure out the atributes */
+		a1p = find_sdp_line(m1p, m2p, 'a');
+		a2p = a1p;
+		for (;;) {
+			a1p = a2p;
+			if (a1p == NULL || a1p >= m2p)
+				break;
+			tmpstr1.s = a2p;
+			tmpstr1.len = m2p - a2p;
+
+			if (parse_payload_attr && extract_ptime(&tmpstr1, &ptime) == 0) {
+				a1p = ptime.s + ptime.len;
+				set_sdp_ptime_attr(stream, payload_attr, &ptime);
+			} else if (parse_payload_attr && extract_sendrecv_mode(&tmpstr1, &sendrecv_mode) == 0) {
+				a1p = sendrecv_mode.s + sendrecv_mode.len;
+				set_sdp_sendrecv_mode_attr(stream, payload_attr, &sendrecv_mode);
+			} else if (parse_payload_attr && extract_rtpmap(&tmpstr1, &rtp_payload, &rtp_enc, &rtp_clock, &rtp_params) == 0) {
+				if (rtp_params.len != 0 && rtp_params.s != NULL) {
+					a1p = rtp_params.s + rtp_params.len;
+				} else {
+					a1p = rtp_clock.s + rtp_clock.len;
+				}
+				payload_attr = (sdp_payload_attr_t*)get_sdp_payload4payload(stream, &rtp_payload);
+				set_sdp_payload_attr(payload_attr, &rtp_enc, &rtp_clock, &rtp_params);
+			} else if (extract_accept_types(&tmpstr1, &stream->accept_types) == 0) {
+				a1p = stream->accept_types.s + stream->accept_types.len;
+			} else if (extract_accept_wrapped_types(&tmpstr1, &stream->accept_wrapped_types) == 0) {
+				a1p = stream->accept_wrapped_types.s + stream->accept_wrapped_types.len;
+			} else if (extract_max_size(&tmpstr1, &stream->max_size) == 0) {
+				a1p = stream->max_size.s + stream->max_size.len;
+			} else if (extract_path(&tmpstr1, &stream->path) == 0) {
+				a1p = stream->path.s + stream->path.len;
+			/*} else { */
+			/*	LM_DBG("else: `%.*s'\n", tmpstr1.len, tmpstr1.s); */
+			}
+
+			a2p = find_next_sdp_line(a1p, m2p, 'a', m2p);
+		}
+		++stream_num;
+	} /* Iterate medias/streams in session */
+	return 0;
+}
+
+static int parse_mixed_content(str *mixed_body, str delimiter, sdp_info_t* _sdp)
+{
+	int res, no_eoh_found, start_parsing;
+	char *bodylimit, *rest;
+	char *d1p, *d2p;
+	char *ret, *end;
+	unsigned int mime;
+	str sdp_body, cnt_disp;
+	int session_num;
+	struct hdr_field hf;
+
+	bodylimit = mixed_body->s + mixed_body->len;
+	d1p = find_sdp_line_delimiter(mixed_body->s, bodylimit, delimiter);
+	if (d1p == NULL) {
+		LM_ERR("empty multipart content\n");
+		return -1;
+	}
+	d2p = d1p;
+	session_num = 0;
+	for(;;) {
+		/* Per-application iteration */
+		d1p = d2p;
+		if (d1p == NULL || d1p >= bodylimit)
+			break; /* No applications left */
+		d2p = find_next_sdp_line_delimiter(d1p, bodylimit, delimiter, bodylimit);
+		/* d2p is text limit for application parsing */
+		memset(&hf,0, sizeof(struct hdr_field));
+		rest = eat_line(d1p + delimiter.len + 2, d2p - d1p - delimiter.len - 2);
+		if ( rest > d2p ) {
+			LM_ERR("Unparsable <%.*s>\n", (int)(d2p-d1p), d1p);
+			return -1;
+		}
+		no_eoh_found = 1;
+		start_parsing = 0;
+		/*LM_DBG("we need to parse this: <%.*s>\n", d2p-rest, rest); */
+		while( rest<d2p && no_eoh_found ) {
+			rest = get_sdp_hdr_field(rest, d2p, &hf);
+			switch (hf.type){
+			case HDR_EOH_T:
+				no_eoh_found = 0;
+				break;
+			case HDR_CONTENTTYPE_T:
+				end = hf.body.s + hf.body.len;
+				ret = decode_mime_type(hf.body.s, end , &mime);
+				if (ret==0)
+					return -1;
+				if (ret!=end) {
+					LM_ERR("the header CONTENT_TYPE contains "
+						"more then one mime type :-(!\n");
+					return -1;
+				}
+				if ((mime&0x00ff)==SUBTYPE_ALL || (mime>>16)==TYPE_ALL) {
+					LM_ERR("invalid mime with wildcard '*' in Content-Type hdr!\n");
+					return -1;
+				}
+			    	//LM_DBG("HDR_CONTENTTYPE_T: %d:%d %p-> <%.*s:%.*s>\n",mime&0x00ff,mime>>16,
+				//			hf.name.s,hf.name.len,hf.name.s,hf.body.len,hf.body.s);
+				if (((((unsigned int)mime)>>16) == TYPE_APPLICATION) && ((mime&0x00ff) == SUBTYPE_SDP)) {
+			    		/*LM_DBG("start_parsing: %d:%d\n",mime&0x00ff,mime>>16); */
+					start_parsing = 1;
+				}
+				break;
+			case HDR_CONTENTDISPOSITION_T:
+				cnt_disp.s = hf.body.s;
+				cnt_disp.len = hf.body.len;
+				break;
+			case HDR_ERROR_T:
+				return -1;
+				break;
+			default:
+				LM_DBG("unknown header: <%.*s:%.*s>\n",hf.name.len,hf.name.s,hf.body.len,hf.body.s);
+			}
+		} /* end of while */
+		/* and now we need to parse the content */
+		if (start_parsing) {
+			sdp_body.s = rest;
+			sdp_body.len = d2p-rest;
+			/* LM_DBG("we need to check session %d: <%.*s>\n", session_num, sdp_body.len, sdp_body.s); */
+			res = parse_sdp_session(&sdp_body, session_num, &cnt_disp, _sdp);
+			if (res != 0) {
+				LM_DBG("free_sdp\n");
+				free_sdp((sdp_info_t**)(void*)&(_sdp));
+				return -1;
+			}
+			session_num++;
+		}
+	}
+	return 0;
+}
+
+/**
+ * Parse SDP.
+ *
+ * returns 0 on success.
+ * non zero on error.
+ */
+int parse_sdp(struct sip_msg* _m)
+{
+	int res;
+	str body, mp_delimiter;
+	int mime;
+
+	if (_m->sdp) {
+		return 0;  /* Already parsed */
+	}
+
+	body.s = get_body(_m);
+	if (body.s==0) {
+		LM_ERR("failed to get the message body\n");
+		return -1;
+	}
+
+	body.len = _m->len -(int)(body.s - _m->buf);
+	if (body.len==0) {
+		LM_DBG("message body has length zero\n");
+		return 1;
+	}
+
+	mime = parse_content_type_hdr(_m);
+	if (mime <= 0) {
+		return -1;
+	}
+	switch (((unsigned int)mime)>>16) {
+	case TYPE_APPLICATION:
+		/* LM_DBG("TYPE_APPLICATION: %d\n",((unsigned int)mime)>>16); */
+		switch (mime&0x00ff) {
+		case SUBTYPE_SDP:
+			/* LM_DBG("SUBTYPE_SDP: %d\n",mime&0x00ff); */
+			if (new_sdp(_m) < 0) {
+				LM_ERR("Can't create sdp\n");
+				return -1;
+			}
+			res = parse_sdp_session(&body, 0, NULL, _m->sdp);
+			if (res != 0) {
+				LM_DBG("free_sdp\n");
+				free_sdp((sdp_info_t**)(void*)&(_m->sdp));
+			}
+			return res;
+			break;
+		default:
+			LM_DBG("TYPE_APPLICATION: unknown %d\n",mime&0x00ff);
+			return -1;
+		}
+		break;
+	case TYPE_MULTIPART:
+		/* LM_DBG("TYPE_MULTIPART: %d\n",((unsigned int)mime)>>16); */
+		switch (mime&0x00ff) {
+		case SUBTYPE_MIXED:
+			/* LM_DBG("SUBTYPE_MIXED: %d <%.*s>\n",mime&0x00ff,_m->content_type->body.len,_m->content_type->body.s); */
+			if(get_mixed_part_delimiter(&(_m->content_type->body),&mp_delimiter) > 0) {
+				/*LM_DBG("got delimiter: <%.*s>\n",mp_delimiter.len,mp_delimiter.s); */
+				if (new_sdp(_m) < 0) {
+					LM_ERR("Can't create sdp\n");
+					return -1;
+				}
+				res = parse_mixed_content(&body, mp_delimiter, _m->sdp);
+				if (res != 0) {
+					free_sdp((sdp_info_t**)(void*)&(_m->sdp));
+				}
+				return res;
+			} else {
+				return -1;
+			}
+			break;
+		default:
+			LM_DBG("TYPE_MULTIPART: unknown %d\n",mime&0x00ff);
+			return -1;
+		}
+		break;
+	default:
+		LM_DBG("%d\n",((unsigned int)mime)>>16);
+		return -1;
+	}
+
+	LM_CRIT("We should not see this!\n");
+	return res;
+}
+
+
+/**
+ * Free all memory.
+ */
+void free_sdp(sdp_info_t** _sdp)
+{
+	sdp_info_t *sdp = *_sdp;
+	sdp_session_cell_t *session, *l_session;
+	sdp_stream_cell_t *stream, *l_stream;
+	sdp_payload_attr_t *payload, *l_payload;
+
+	LM_DBG("_sdp = %p\n", _sdp);
+	if (sdp == 0) return;
+	LM_DBG("sdp = %p\n", sdp);
+	session = sdp->sessions;
+	LM_DBG("session = %p\n", session);
+	while (session) {
+		l_session = session;
+		session = session->next;
+		stream = l_session->streams;
+		while (stream) {
+			l_stream = stream;
+			stream = stream->next;
+			payload = l_stream->payload_attr;
+			while (payload) {
+				l_payload = payload;
+				payload = payload->next;
+				pkg_free(l_payload);
+			}
+			if (l_stream->p_payload_attr) {
+				pkg_free(l_stream->p_payload_attr);
+			}
+			pkg_free(l_stream);
+		}
+		pkg_free(l_session);
+	}
+	pkg_free(sdp);
+	*_sdp = NULL;
+}
+
+
+void print_sdp_stream(sdp_stream_cell_t *stream)
+{
+	sdp_payload_attr_t *payload;
+
+	LM_DBG("....stream[%d]:%p=>%p {%p} '%.*s' '%.*s:%.*s' '%.*s' '%.*s' '%.*s:%.*s' (%d)=>%p '%.*s' '%.*s' '%.*s' '%.*s'\n",
+		stream->stream_num, stream, stream->next,
+		stream->p_payload_attr,
+		stream->media.len, stream->media.s,
+		stream->ip_addr.len, stream->ip_addr.s, stream->port.len, stream->port.s,
+		stream->transport.len, stream->transport.s,
+		stream->payloads.len, stream->payloads.s,
+		stream->bw_type.len, stream->bw_type.s, stream->bw_width.len, stream->bw_width.s,
+		stream->payloads_num, stream->payload_attr,
+		stream->path.len, stream->path.s,
+		stream->max_size.len, stream->max_size.s,
+		stream->accept_types.len, stream->accept_types.s,
+		stream->accept_wrapped_types.len, stream->accept_wrapped_types.s);
+	payload = stream->payload_attr;
+	while (payload) {
+		LM_DBG("......payload[%d]:%p=>%p p_payload_attr[%d]:%p '%.*s' '%.*s' '%.*s' '%.*s' '%.*s' '%.*s'\n",
+			payload->payload_num, payload, payload->next,
+			payload->payload_num, stream->p_payload_attr[payload->payload_num],
+			payload->rtp_payload.len, payload->rtp_payload.s,
+			payload->rtp_enc.len, payload->rtp_enc.s,
+			payload->rtp_clock.len, payload->rtp_clock.s,
+			payload->rtp_params.len, payload->rtp_params.s,
+			payload->sendrecv_mode.len, payload->sendrecv_mode.s,
+			payload->ptime.len, payload->ptime.s);
+		payload=payload->next;
+	}
+}
+
+void print_sdp_session(sdp_session_cell_t *session)
+{
+	sdp_stream_cell_t *stream = session->streams;
+
+	if (session==NULL) {
+		LM_ERR("NULL session\n");
+		return;
+	}
+
+	LM_DBG("..session[%d]:%p=>%p '%.*s' '%.*s:%.*s' (%d)=>%p\n",
+		session->session_num, session, session->next,
+		session->cnt_disp.len, session->cnt_disp.s,
+		session->bw_type.len, session->bw_type.s, session->bw_width.len, session->bw_width.s,
+		session->streams_num, session->streams);
+	while (stream) {
+		print_sdp_stream(stream);
+		stream=stream->next;
+	}
+}
+
+
+void print_sdp(sdp_info_t* sdp)
+{
+	sdp_session_cell_t *session;
+
+	LM_DBG("sdp:%p=>%p (%d)\n", sdp, sdp->sessions, sdp->sessions_num);
+	session = sdp->sessions;
+	while (session) {
+		print_sdp_session(session);
+		session = session->next;
+	}
+}
+
+/*
+ * Free cloned stream.
+ */
+void free_cloned_sdp_stream(sdp_stream_cell_t *_stream)
+{
+	sdp_stream_cell_t *stream, *l_stream;
+	sdp_payload_attr_t *payload, *l_payload;
+
+	stream = _stream;
+	while (stream) {
+		l_stream = stream;
+		stream = stream->next;
+		payload = l_stream->payload_attr;
+		while (payload) {
+			l_payload = payload;
+			payload = payload->next;
+			shm_free(l_payload);
+		}
+		if (l_stream->p_payload_attr) {
+			shm_free(l_stream->p_payload_attr);
+		}
+		shm_free(l_stream);
+	}
+}
+
+/*
+ * Free cloned session.
+ */
+void free_cloned_sdp_session(sdp_session_cell_t *_session)
+{
+	sdp_session_cell_t *session, *l_session;
+
+	session = _session;
+	while (session) {
+		l_session = session;
+		session = l_session->next;
+		free_cloned_sdp_stream(l_session->streams);
+		shm_free(l_session);
+	}
+}
+
+void free_cloned_sdp(sdp_info_t* sdp)
+{
+	free_cloned_sdp_session(sdp->sessions);
+	shm_free(sdp);
+}
+
+sdp_payload_attr_t * clone_sdp_payload_attr(sdp_payload_attr_t *attr)
+{
+	sdp_payload_attr_t * clone_attr;
+	int len;
+	char *p;
+
+	if (attr == NULL) {
+		LM_ERR("arg:NULL\n");
+		return NULL;
+	}
+
+	len = sizeof(sdp_payload_attr_t) +
+			attr->rtp_payload.len +
+			attr->rtp_enc.len +
+			attr->rtp_clock.len +
+			attr->rtp_params.len +
+			attr->sendrecv_mode.len;
+	clone_attr = (sdp_payload_attr_t*)shm_malloc(len);
+	if (clone_attr == NULL) {
+		LM_ERR("no more shm mem (%d)\n",len);
+		return NULL;
+	}
+	memset( clone_attr, 0, len);
+	p = (char*)(clone_attr+1);
+
+	clone_attr->payload_num = attr->payload_num;
+
+	if (attr->rtp_payload.len) {
+		clone_attr->rtp_payload.s = p;
+		clone_attr->rtp_payload.len = attr->rtp_payload.len;
+		memcpy( p, attr->rtp_payload.s, attr->rtp_payload.len);
+		p += attr->rtp_payload.len;
+	}
+
+	if (attr->rtp_enc.len) {
+		clone_attr->rtp_enc.s = p;
+		clone_attr->rtp_enc.len = attr->rtp_enc.len;
+		memcpy( p, attr->rtp_enc.s, attr->rtp_enc.len);
+		p += attr->rtp_enc.len;
+	}
+
+	if (attr->rtp_clock.len) {
+		clone_attr->rtp_clock.s = p;
+		clone_attr->rtp_clock.len = attr->rtp_clock.len;
+		memcpy( p, attr->rtp_clock.s, attr->rtp_clock.len);
+		p += attr->rtp_clock.len;
+	}
+
+	if (attr->rtp_params.len) {
+		clone_attr->rtp_params.s = p;
+		clone_attr->rtp_params.len = attr->rtp_params.len;
+		memcpy( p, attr->rtp_params.s, attr->rtp_params.len);
+		p += attr->rtp_params.len;
+	}
+
+	if (attr->sendrecv_mode.len) {
+		clone_attr->sendrecv_mode.s = p;
+		clone_attr->sendrecv_mode.len = attr->sendrecv_mode.len;
+		memcpy( p, attr->sendrecv_mode.s, attr->sendrecv_mode.len);
+		p += attr->sendrecv_mode.len;
+	}
+
+	if (attr->ptime.len) {
+		clone_attr->ptime.s = p;
+		clone_attr->ptime.len = attr->ptime.len;
+		memcpy( p, attr->ptime.s, attr->ptime.len);
+		p += attr->ptime.len;
+	}
+
+	return clone_attr;
+}
+
+sdp_stream_cell_t * clone_sdp_stream_cell(sdp_stream_cell_t *stream)
+{
+	sdp_stream_cell_t *clone_stream;
+	sdp_payload_attr_t *clone_payload_attr, *payload_attr;
+	int len, i;
+	char *p;
+
+	if (stream == NULL) {
+		LM_ERR("arg:NULL\n");
+		return NULL;
+	}
+
+	/* NOTE: we are not cloning RFC4975 attributes */
+	len = sizeof(sdp_stream_cell_t) +
+			stream->media.len +
+			stream->port.len +
+			stream->transport.len +
+			stream->payloads.len +
+			stream->bw_type.len +
+			stream->bw_width.len +
+			stream->ip_addr.len;
+	clone_stream = (sdp_stream_cell_t*)shm_malloc(len);
+	if (clone_stream == NULL) {
+		LM_ERR("no more shm mem (%d)\n",len);
+		return NULL;
+	}
+	memset( clone_stream, 0, len);
+	p = (char*)(clone_stream+1);
+
+	payload_attr = NULL;
+	for (i=0;i<stream->payloads_num;i++) {
+		clone_payload_attr = clone_sdp_payload_attr(stream->p_payload_attr[i]);
+		if (clone_payload_attr == NULL) {
+			LM_ERR("unable to clone attributes for payload[%d]\n", i);
+			goto error;
+		}
+		clone_payload_attr->next = payload_attr;
+		payload_attr = clone_payload_attr;
+	}
+	clone_stream->payload_attr = payload_attr;
+
+	clone_stream->payloads_num = stream->payloads_num;
+	if (clone_stream->payloads_num) {
+		if (0 == init_p_payload_attr(clone_stream, USE_SHM_MEM)) {
+			goto error;
+		}
+	}
+
+	clone_stream->stream_num = stream->stream_num;
+	clone_stream->pf = stream->pf;
+
+	if (stream->media.len) {
+		clone_stream->media.s = p;
+		clone_stream->media.len = stream->media.len;
+		memcpy( p, stream->media.s, stream->media.len);
+		p += stream->media.len;
+	}
+
+	if (stream->port.len) {
+		clone_stream->port.s = p;
+		clone_stream->port.len = stream->port.len;
+		memcpy( p, stream->port.s, stream->port.len);
+		p += stream->port.len;
+	}
+
+	if (stream->transport.len) {
+		clone_stream->transport.s = p;
+		clone_stream->transport.len = stream->transport.len;
+		memcpy( p, stream->transport.s, stream->transport.len);
+		p += stream->transport.len;
+	}
+
+	if (stream->payloads.len) {
+		clone_stream->payloads.s = p;
+		clone_stream->payloads.len = stream->payloads.len;
+		memcpy( p, stream->payloads.s, stream->payloads.len);
+		p += stream->payloads.len;
+	}
+
+	if (stream->bw_type.len) {
+		clone_stream->bw_type.s = p;
+		clone_stream->bw_type.len = stream->bw_type.len;
+		p += stream->bw_type.len;
+	}
+
+	if (stream->bw_width.len) {
+		clone_stream->bw_width.s = p;
+		clone_stream->bw_width.len = stream->bw_width.len;
+		p += stream->bw_width.len;
+	}
+
+	if (stream->ip_addr.len) {
+		clone_stream->ip_addr.s = p;
+		clone_stream->ip_addr.len = stream->ip_addr.len;
+		memcpy( p, stream->ip_addr.s, stream->ip_addr.len);
+		//p += stream->payloads.len;
+	}
+
+	/* NOTE: we are not cloning RFC4975 attributes:
+	 * - path
+	 * - max_size
+	 * - accept_types
+	 * - accept_wrapped_types
+	 */
+
+	return clone_stream;
+error:
+	free_cloned_sdp_stream(clone_stream);
+	return NULL;
+}
+
+sdp_session_cell_t * clone_sdp_session_cell(sdp_session_cell_t *session)
+{
+	sdp_session_cell_t *clone_session;
+	sdp_stream_cell_t *clone_stream, *prev_clone_stream, *stream;
+	int len, i;
+	char *p;
+
+	if (session == NULL) {
+		LM_ERR("arg:NULL\n");
+		return NULL;
+	}
+	len = sizeof(sdp_session_cell_t) + session->cnt_disp.len +
+		session->bw_type.len + session->bw_width.len;
+	clone_session = (sdp_session_cell_t*)shm_malloc(len);
+	if (clone_session == NULL) {
+		LM_ERR("no more shm mem (%d)\n",len);
+		return NULL;
+	}
+	memset( clone_session, 0, len);
+	p = (char*)(clone_session+1);
+
+	if (session->streams_num) {
+		stream=session->streams;
+		clone_stream=clone_sdp_stream_cell(stream);
+		if (clone_stream==NULL) {
+			goto error;
+		}
+		clone_session->streams=clone_stream;
+		prev_clone_stream=clone_stream;
+		stream=stream->next;
+		for (i=1;i<session->streams_num;i++) {
+			clone_stream=clone_sdp_stream_cell(stream);
+			if (clone_stream==NULL) {
+				goto error;
+			}
+			prev_clone_stream->next=clone_stream;
+			prev_clone_stream=clone_stream;
+			stream=stream->next;
+		}
+	}
+
+	clone_session->session_num = session->session_num;
+	clone_session->streams_num = session->streams_num;
+
+	if (session->cnt_disp.len) {
+		clone_session->cnt_disp.s = p;
+		clone_session->cnt_disp.len = session->cnt_disp.len;
+		memcpy( p, session->cnt_disp.s, session->cnt_disp.len);
+		p += session->cnt_disp.len;
+	}
+
+	if (session->bw_type.len) {
+		clone_session->bw_type.s = p;
+		clone_session->bw_type.len = session->bw_type.len;
+		memcpy( p, session->bw_type.s, session->bw_type.len);
+		p += session->bw_type.len;
+	}
+
+	if (session->bw_width.len) {
+		clone_session->bw_width.s = p;
+		clone_session->bw_width.len = session->bw_width.len;
+		memcpy( p, session->bw_width.s, session->bw_width.len);
+		//p += session->bw_type.len;
+	}
+
+	return clone_session;
+error:
+	free_cloned_sdp_session(clone_session);
+	return NULL;
+}
+
+sdp_info_t * clone_sdp_info(struct sip_msg* _m)
+{
+	sdp_info_t *clone_sdp_info, *sdp_info=_m->sdp;
+	sdp_session_cell_t *clone_session, *prev_clone_session, *session;
+	int i, len;
+
+	if (sdp_info==NULL) {
+		LM_ERR("no sdp to clone\n");
+		return NULL;
+	}
+	if (sdp_info->sessions_num == 0) {
+		LM_ERR("no sessions to clone\n");
+		return NULL;
+	}
+
+	len = sizeof(sdp_info_t);
+	clone_sdp_info = (sdp_info_t*)shm_malloc(len);
+	if (clone_sdp_info == NULL) {
+		LM_ERR("no more shm mem (%d)\n",len);
+		return NULL;
+	}
+	LM_DBG("clone_sdp_info: %p\n", clone_sdp_info);
+	memset( clone_sdp_info, 0, len);
+	LM_DBG("we have %d sessions\n", sdp_info->sessions_num);
+	clone_sdp_info->sessions_num = sdp_info->sessions_num;
+
+	session=sdp_info->sessions;
+	clone_session=clone_sdp_session_cell(session);
+	if (clone_session==NULL) {
+		goto error;
+	}
+	clone_sdp_info->sessions=clone_session;
+	prev_clone_session=clone_session;
+	session=session->next;
+	for (i=1;i<sdp_info->sessions_num;i++) {
+		clone_session=clone_sdp_session_cell(session);
+		if (clone_session==NULL) {
+			goto error;
+		}
+		prev_clone_session->next=clone_session;
+		prev_clone_session=clone_session;
+		session=session->next;
+	}
+
+	return clone_sdp_info;
+error:
+	free_cloned_sdp(clone_sdp_info);
+	return NULL;
+}
+

+ 155 - 0
parser/sdp/sdp.h

@@ -0,0 +1,155 @@
+/*
+ * $Id: sdp.h 4807 2008-09-02 15:00:48Z osas $
+ *
+ * SDP parser interface
+ *
+ * Copyright (C) 2008-2009 SOMA Networks, 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.
+ *
+ */
+
+
+#ifndef SDP_H
+#define SDP_H
+
+#include "../msg_parser.h"
+
+typedef struct sdp_payload_attr {
+	struct sdp_payload_attr *next;
+	int payload_num; /**< payload index inside stream */
+	str rtp_payload;
+	str rtp_enc;
+	str rtp_clock;
+	str rtp_params;
+	str sendrecv_mode;
+	str ptime;
+} sdp_payload_attr_t;
+
+typedef struct sdp_stream_cell {
+	struct sdp_stream_cell *next;
+	/* c=<network type> <address type> <connection address> */
+	int pf;         /**< connection address family: AF_INET/AF_INET6 */
+	str ip_addr;    /**< connection address */
+	int stream_num; /**< stream index inside a session */
+	/* m=<media> <port> <transport> <payloads> */
+	str media;
+	str port;
+	str transport;
+	str payloads;
+	int payloads_num;                         /**< number of payloads inside a stream */
+	/* b=<bwtype>:<bandwidth> */
+	str bw_type;                              /**< alphanumeric modifier giving the meaning of the <bandwidth> figure:
+							CT - conference total;
+							AS - application specific */
+	str bw_width;                            /**< The <bandwidth> is interpreted as kilobits per second by default */
+	str path;                                 /**< RFC4975: path attribute */
+	str max_size;                             /**< RFC4975: max-size attribute */
+	str accept_types;                         /**< RFC4975: accept-types attribute */
+	str accept_wrapped_types;                 /**< RFC4975: accept-wrapped-types attribute */
+	struct sdp_payload_attr **p_payload_attr; /**< fast access pointers to payloads */
+	struct sdp_payload_attr *payload_attr;
+} sdp_stream_cell_t;
+
+typedef struct sdp_session_cell {
+	struct sdp_session_cell *next;
+	int session_num;  /**< session index inside sdp */
+	str cnt_disp;     /**< the Content-Disposition header (for Content-Type:multipart/mixed) */
+	/* b=<bwtype>:<bandwidth> */
+	str bw_type;      /**< alphanumeric modifier giving the meaning of the <bandwidth> figure:
+				CT - conference total;
+				AS - application specific */
+	str bw_width;   /**< The <bandwidth> is interpreted as kilobits per second by default */
+	int streams_num;  /**< number of streams inside a session */
+	struct sdp_stream_cell*  streams;
+} sdp_session_cell_t;
+
+/**
+ * Here we hold the head of the parsed sdp structure
+ */
+typedef struct sdp_info {
+	int sessions_num;	/**< number of SDP sessions */
+	struct sdp_session_cell *sessions;
+} sdp_info_t;
+
+
+/*
+ * Parse SDP.
+ */
+int parse_sdp(struct sip_msg* _m);
+
+/**
+ * Get a session for the current sip msg based on position inside SDP.
+ */
+sdp_session_cell_t* get_sdp_session(struct sip_msg* _m, int session_num);
+/**
+ * Get a session for the given sdp based on position inside SDP.
+ */
+sdp_session_cell_t* get_sdp_session_sdp(struct sdp_info* sdp, int session_num);
+
+/**
+ * Get a stream for the current sip msg based on positions inside SDP.
+ */
+sdp_stream_cell_t* get_sdp_stream(struct sip_msg* _m, int session_num, int stream_num);
+/**
+ * Get a stream for the given sdp based on positions inside SDP.
+ */
+sdp_stream_cell_t* get_sdp_stream_sdp(struct sdp_info* sdp, int session_num, int stream_num);
+
+/**
+ * Get a payload from a stream based on payload.
+ */
+sdp_payload_attr_t* get_sdp_payload4payload(sdp_stream_cell_t *stream, str *rtp_payload);
+
+/**
+ * Get a payload from a stream based on position.
+ */
+sdp_payload_attr_t* get_sdp_payload4index(sdp_stream_cell_t *stream, int index);
+
+/**
+ * Free all memory associated with parsed structure.
+ *
+ * Note: this will free up the parsed sdp structure (form PKG_MEM).
+ */
+void free_sdp(sdp_info_t** _sdp);
+
+
+/**
+ * Print the content of the given sdp_info structure.
+ *
+ * Note: only for debug purposes.
+ */
+void print_sdp(sdp_info_t* sdp);
+/**
+ * Print the content of the given sdp_session structure.
+ *
+ * Note: only for debug purposes.
+ */
+void print_sdp_session(sdp_session_cell_t* sdp_session);
+/**
+ * Print the content of the given sdp_stream structure.
+ *
+ * Note: only for debug purposes.
+ */
+void print_sdp_stream(sdp_stream_cell_t *stream);
+
+
+#endif /* SDP_H */

+ 61 - 0
parser/sdp/sdp_cloner.h

@@ -0,0 +1,61 @@
+/*
+ * $Id: sdp_cloner.h 4518 2008-07-28 15:39:28Z henningw $
+ *
+ * SDP parser interface
+ *
+ * Copyright (C) 2008-2009 SOMA Networks, 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.
+ *
+ */
+
+
+#ifndef SDP_CLONER_H
+#define SDP_CLONER_H
+
+#include "sdp.h"
+
+
+/**
+ * Clone the given sdp_session_cell structure.
+ */
+sdp_session_cell_t * clone_sdp_session_cell(sdp_session_cell_t *session);
+/**
+ * Free all memory associated with the cloned sdp_session structure.
+ *
+ * Note: this will free up the parsed sdp structure (form SHM_MEM).
+ */
+void free_cloned_sdp_session(sdp_session_cell_t *_session);
+
+/**
+ * Clone the given sdp_info structure.
+ *
+ * Note: all cloned structer will be in SHM_MEM.
+ */
+sdp_info_t* clone_sdp_info(struct sip_msg* _m);
+/**
+ * Free all memory associated with the cloned sdp_info structure.
+ *
+ * Note: this will free up the parsed sdp structure (form SHM_MEM).
+ */
+void free_cloned_sdp(sdp_info_t* sdp);
+
+#endif /* SDP_H */

+ 625 - 0
parser/sdp/sdp_helpr_funcs.c

@@ -0,0 +1,625 @@
+/*
+ * $Id: sdp_helpr_funcs.c 5573 2009-02-09 16:55:22Z osas $
+ *
+ * SDP parser helpers
+ *
+ * Copyright (C) 2008-2009 SOMA Networks, 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 "../../ut.h"
+#include "../msg_parser.h"
+#include "../parser_f.h"
+#include "../parse_hname2.h"
+
+
+static str sup_ptypes[] = {
+	str_init("udp"),
+	str_init("udptl"),
+	str_init("rtp/avp"),
+	str_init("rtp/savpf"),
+	str_init("TCP/MSRP"),
+	str_init("TCP/TLS/MSRP"),
+	{ NULL, 0}
+};
+
+
+#define READ(val) \
+	(*(val + 0) + (*(val + 1) << 8) + (*(val + 2) << 16) + (*(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])
+
+/*
+ * ser_memmem() returns the location of the first occurrence of data
+ * pattern b2 of size len2 in memory block b1 of size len1 or
+ * NULL if none is found. Obtained from NetBSD.
+ */
+static void * ser_memmem(const void *b1, const void *b2, size_t len1, size_t len2)
+{
+	/* Initialize search pointer */
+	char *sp = (char *) b1;
+
+	/* Initialize pattern pointer */
+	char *pp = (char *) b2;
+
+	/* Initialize end of search address space pointer */
+	char *eos = sp + len1 - len2;
+
+	/* Sanity check */
+	if(!(b1 && b2 && len1 && len2))
+		return NULL;
+
+	while (sp <= eos) {
+		if (*sp == *pp)
+			if (memcmp(sp, pp, len2) == 0)
+				return sp;
+
+			sp++;
+	}
+
+	return NULL;
+}
+
+
+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;
+	return 1;
+
+error:  
+	return -1;
+other:  
+	LM_DBG("'boundary' parsing error\n");
+	return -1;
+}
+
+
+
+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);
+	len -= cp1 - cp;
+	if (len <= 0 || cp == cp1) {
+		LM_ERR("invalid encoding in `a=rtpmap'\n");
+		return -1;
+	}
+	rtpmap_encoding->len = cp1 - cp;
+
+	cp = cp1;
+	cp1 = (char*)ser_memmem(cp, "/", len, 1);
+	len -= cp1 - cp;
+	if (len <= 0) {
+		LM_ERR("invalid encoding in `a=rtpmap:'\n");
+		return -1;
+	}
+
+	rtpmap_clockrate->s = cp + 1; /* skip '/' */
+	rtpmap_clockrate->len = len -1; /* skip '/' */
+	if ( cp == cp1) {
+		rtpmap_parmas->s = NULL;
+		rtpmap_parmas->len = 0;
+	} else {
+		rtpmap_parmas->s = cp1 + 1;
+		rtpmap_parmas->len = cp1 - cp;
+	}
+	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_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_sendrecv_mode(str *body, str *sendrecv_mode)
+{
+	char *cp1;
+
+	cp1 = body->s;
+	if ( !( (strncasecmp(cp1, "a=sendrecv", 10) == 0) ||
+		(strncasecmp(cp1, "a=inactive", 10) == 0) ||
+		(strncasecmp(cp1, "a=recvonly", 10) == 0) ||
+		(strncasecmp(cp1, "a=sendonly", 10) == 0) )) {
+		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, nextisip;
+
+	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);
+
+	nextisip = 0;
+	for (cp = mediaip->s; cp < mediaip->s + mediaip->len;) {
+		len = eat_token_end(cp, mediaip->s + mediaip->len) - cp;
+		if (nextisip == 1) {
+			mediaip->s = cp;
+			mediaip->len = len;
+			nextisip++;
+			break;
+		}
+		if (len == 3 && memcmp(cp, "IP", 2) == 0) {
+			switch (cp[2]) {
+			case '4':
+				nextisip = 1;
+				*pf = AF_INET;
+				break;
+
+			case '6':
+				nextisip = 1;
+				*pf = AF_INET6;
+				break;
+
+			default:
+				break;
+			}
+		}
+		cp = eat_space_end(cp + len, mediaip->s + mediaip->len);
+	}
+	if (nextisip != 2 || mediaip->len == 0) {
+		LM_ERR("no `IP[4|6]' in `%s' field\n",line);
+		return -1;
+	}
+	return 1;
+}
+
+int extract_media_attr(str *body, str *mediamedia, str *mediaport, str *mediatransport, str *mediapayload)
+{
+	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)
+			return 0;
+	/* Unproxyable protocol type. Generally it isn't error. */
+	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;
+}
+
+
+/* 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;
+	}
+
+	/* if header-field well-known, parse it, find its end otherwise ;
+	 * after leaving the hdr->type switch, tmp should be set to the
+	 * next header field
+	 */
+	switch(hdr->type){
+		case HDR_CONTENTTYPE_T:
+		case HDR_CONTENTDISPOSITION_T:
+			/* 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.s, hdr->type);
+					tmp=end;
+					goto error;
+				}
+				tmp=match;
+			}while( match<end &&( (*match==' ')||(*match=='\t') ) );
+			tmp=match;
+			hdr->body.len=match-hdr->body.s;
+			break;
+		default:
+			LM_CRIT("unknown header type %d\n", hdr->type);
+			goto error;
+	}
+	/* 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 (strncmp(cp1+2, delimiter.s, delimiter.len) == 0)
+	break;
+      else
+	cp = cp1 + 2 + delimiter.len;
+      if (cp >= plimit)
+	return NULL;
+    }
+    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;
+}
+

+ 66 - 0
parser/sdp/sdp_helpr_funcs.h

@@ -0,0 +1,66 @@
+/*
+ * $Id: sdp_helpr_funcs.h 4807 2008-09-02 15:00:48Z osas $
+ *
+ * SDP parser helpers
+ *
+ * Copyright (C) 2008-2009 SOMA Networks, 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.
+ *
+ */
+
+
+
+#ifndef _SDP_HLPR_FUNCS_H
+#define  _SDP_HLPR_FUNCS_H
+
+#include "../../str.h"
+#include "../msg_parser.h"
+
+/**
+ * Detect the mixed part delimiter.
+ *
+ * Example: "boundary1"
+ * Content-Type: multipart/mixed; boundary="boundary1"
+ */
+int get_mixed_part_delimiter(str * body, str * mp_delimiter);
+
+int extract_rtpmap(str *body, str *rtpmap_payload, str *rtpmap_encoding, str *rtpmap_clockrate, str *rtpmap_parmas);
+int extract_ptime(str *body, str *ptime);
+int extract_sendrecv_mode(str *body, str *sendrecv_mode);
+int extract_mediaip(str *body, str *mediaip, int *pf, char *line);
+int extract_media_attr(str *body, str *mediamedia, str *mediaport, str *mediatransport, str *mediapayload);
+int extract_bwidth(str *body, str *bwtype, str *bwwitdth);
+
+/* RFC4975 attributes */
+int extract_accept_types(str *body, str *accept_types);
+int extract_accept_wrapped_types(str *body, str *accept_wrapped_types);
+int extract_max_size(str *body, str *max_size);
+int extract_path(str *body, str *path);
+
+char *find_sdp_line(char *p, char *plimit, char linechar);
+char *find_next_sdp_line(char *p, char *plimit, char linechar, char *defptr);
+
+char* get_sdp_hdr_field(char* , char* , struct hdr_field* );
+
+char *find_sdp_line_delimiter(char *p, char *plimit, str delimiter);
+char *find_next_sdp_line_delimiter(char *p, char *plimit, str delimiter, char *defptr); 
+#endif