src/core/parser/sdp/sdp.c
450962d0
 /*
  * SDP parser interface
  *
  * Copyright (C) 2008-2009 SOMA Networks, INC.
6face3f2
  * Copyright (C) 2010 VoIP Embedded, Inc
450962d0
  *
  * 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.
  *
  */
 
 
27e1d6f4
 #include "../../ut.h"
450962d0
 #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"
 
dc03199e
 #define SDP_USE_PKG_MEM 0
 #define SDP_USE_SHM_MEM 1
450962d0
 
27e1d6f4
 #define HOLD_IP_STR "0.0.0.0"
 #define HOLD_IP_LEN 7
 
450962d0
 /**
  * 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));
cfedffe1
 	if (sdp == NULL) {
e3ecad34
 		PKG_MEM_ERROR;
450962d0
 		return -1;
 	}
 	memset( sdp, 0, sizeof(sdp_info_t));
d6ac6a12
 	sdp->type = MSG_BODY_SDP;
 	sdp->free = (free_msg_body_f)free_sdp;
 	_m->body = (msg_body_t*)sdp;
450962d0
 
 	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);
12a33bc8
 	if (session == NULL) {
e3ecad34
 		PKG_MEM_ERROR;
12a33bc8
 		return NULL;
450962d0
 	}
 	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,
6face3f2
 		str* media, str* port, str* transport, str* payloads, int is_rtp, int pf, str* sdp_ip)
450962d0
 {
 	sdp_stream_cell_t *stream;
 	int len;
 
 	len = sizeof(sdp_stream_cell_t);
 	stream = (sdp_stream_cell_t*)pkg_malloc(len);
12a33bc8
 	if (stream == NULL) {
e3ecad34
 		PKG_MEM_ERROR;
12a33bc8
 		return NULL;
450962d0
 	}
 	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;
 
6face3f2
 	stream->is_rtp = is_rtp;
 
450962d0
 	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);
12a33bc8
 	if (payload_attr == NULL) {
e3ecad34
 		PKG_MEM_ERROR;
12a33bc8
 		return NULL;
450962d0
 	}
 	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;
 }
 
b7a1ea4c
 
450962d0
 /**
  * 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;
 
12a33bc8
 	if (_stream == NULL) {
450962d0
 		LM_ERR("Invalid stream\n");
12a33bc8
 		return NULL;
450962d0
 	}
 	payloads_num = _stream->payloads_num;
 	if (payloads_num == 0) {
 		LM_ERR("Invalid number of payloads\n");
12a33bc8
 		return NULL;
450962d0
 	}
dc03199e
 	if (pkg == SDP_USE_PKG_MEM) {
450962d0
 		_stream->p_payload_attr = (sdp_payload_attr_t**)pkg_malloc(payloads_num * sizeof(sdp_payload_attr_t*));
dc03199e
 	} else if (pkg == SDP_USE_SHM_MEM) {
450962d0
 		_stream->p_payload_attr = (sdp_payload_attr_t**)shm_malloc(payloads_num * sizeof(sdp_payload_attr_t*));
 	} else {
 		LM_ERR("undefined memory type\n");
12a33bc8
 		return NULL;
450962d0
 	}
12a33bc8
 	if (_stream->p_payload_attr == NULL) {
dc03199e
 		if (pkg == SDP_USE_PKG_MEM) {
e3ecad34
 			PKG_MEM_ERROR;
dc03199e
 		} else if (pkg == SDP_USE_SHM_MEM) {
e3ecad34
 			SHM_MEM_ERROR;
 		}
12a33bc8
 		return NULL;
450962d0
 	}
 
 	--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)
 {
12a33bc8
 	if (payload_attr == NULL) {
450962d0
 		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;
 }
 
12a33bc8
 void set_sdp_payload_fmtp(sdp_payload_attr_t *payload_attr, str *fmtp_string )
 {
 	if (payload_attr == NULL) {
 		LM_ERR("Invalid payload location\n");
 		return;
 	}
 	payload_attr->fmtp_string.s = fmtp_string->s;
 	payload_attr->fmtp_string.len = fmtp_string->len;
 
 	return;
 }
 
450962d0
 /*
  * Getters ....
  */
5a1d36d4
 int get_sdp_session_num(struct sip_msg* _m)
 {
 	if (_m->body == NULL) return 0;
 	if (_m->body->type != MSG_BODY_SDP) return 0;
 	return ((sdp_info_t*)_m->body)->sessions_num;
 }
 
 int get_sdp_stream_num(struct sip_msg* _m)
 {
 	if (_m->body == NULL) return 0;
 	if (_m->body->type != MSG_BODY_SDP) return 0;
 	return ((sdp_info_t*)_m->body)->streams_num;
 }
450962d0
 
 sdp_session_cell_t* get_sdp_session_sdp(struct sdp_info* sdp, int session_num)
 {
 	sdp_session_cell_t *session;
 
 	session = sdp->sessions;
92761dbd
 	if (session_num >= sdp->sessions_num) return NULL;
450962d0
 	while (session) {
92761dbd
 		if (session->session_num == session_num) return session;
 		session = session->next;
450962d0
 	}
 	return NULL;
 }
 
 sdp_session_cell_t* get_sdp_session(struct sip_msg* _m, int session_num)
 {
d6ac6a12
 	if (_m->body == NULL) return NULL;
 	if (_m->body->type != MSG_BODY_SDP) return NULL;
 	return get_sdp_session_sdp((sdp_info_t*)_m->body, session_num);
450962d0
 }
 
 
 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;
 
92761dbd
 	if (sdp==NULL) return NULL;
 	if (session_num >= sdp->sessions_num) return NULL;
450962d0
 	session = sdp->sessions;
 	while (session) {
 		if (session->session_num == session_num) {
92761dbd
 			if (stream_num >= session->streams_num) return NULL;
450962d0
 			stream = session->streams;
 			while (stream) {
 				if (stream->stream_num == stream_num) return stream;
 				stream = stream->next;
 			}
92761dbd
 			break;
450962d0
 		} else {
 			session = session->next;
 		}
 	}
 
 	return NULL;
 }
 
 sdp_stream_cell_t* get_sdp_stream(struct sip_msg* _m, int session_num, int stream_num)
 {
1dec97e8
 	if (_m->body == NULL) return NULL;
 	if (_m->body->type != MSG_BODY_SDP) return NULL;
d6ac6a12
 	return get_sdp_stream_sdp((sdp_info_t*)_m->body, session_num, stream_num);
450962d0
 }
 
 
 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;
10ba0083
 	str sdp_ip = {NULL,0};
 	str sdp_media, sdp_port, sdp_transport, sdp_payload;
450962d0
 	str payload;
c90ff4d4
 	str rtp_payload, rtp_enc, rtp_clock, rtp_params;
6face3f2
 	int is_rtp;
450962d0
 	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;
12a33bc8
 	str fmtp_string;
88a8eb34
 	str remote_candidates = {"a:remote-candidates:", 20};
450962d0
 
f68eb2b6
 	/* hook the start and length of sdp body inside structure
d768eb5c
 	 * - shorcut useful for multi-part bodies and sdp operations
 	 */
 	_sdp->text = *sdp_body;
d7871cd8
 	pf = AF_INET;
d768eb5c
 
450962d0
 	/*
 	 * 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');
88429acf
 	if (o1p == NULL) {
450962d0
 		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);
88429acf
 	if (session == NULL) return -1;
 
e1a5b5ce
 	/* Get sess-version */
 	tmpstr1.s = o1p;
 	tmpstr1.len = eat_line(o1p,m1p-o1p) - o1p;
 	if ( extract_sess_version(&tmpstr1, &session->o_sess_version) == -1 ) {
 		LM_ERR("can't extract origin sess-version from the message\n");
 		return -1;
 	}
 
88429acf
 	/* Get origin IP */
 	tmpstr1.s = o1p;
 	tmpstr1.len = bodylimit - tmpstr1.s; /* limit is session limit text */
 	if (extract_mediaip(&tmpstr1, &session->o_ip_addr, &session->o_pf,"o=") == -1) {
 		LM_ERR("can't extract origin media IP from the message\n");
 		return -1;
 	}
450962d0
 
 	/* Find c1p only between session begin and first media.
 	 * c1p will give common c= for all medias. */
 	c1p = find_sdp_line(o1p, m1p, 'c');
 
88429acf
 	if (c1p) {
10ba0083
 		/* Extract session address */
88429acf
 		tmpstr1.s = c1p;
 		tmpstr1.len = bodylimit - tmpstr1.s; /* limit is session limit text */
 		if (extract_mediaip(&tmpstr1, &session->ip_addr, &session->pf,"c=") == -1) {
 			LM_ERR("can't extract common media IP from the message\n");
 			return -1;
 		}
 	}
 
450962d0
 	/* 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');
 
971386c3
 		sdp_ip.s = NULL;
 		sdp_ip.len = 0;
10ba0083
 		if (c2p) {
 			/* Extract stream address */
 			tmpstr1.s = c2p;
 			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;
 			}
 		} else {
 			if (!c1p) {
 				/* No "c=" */
 				LM_ERR("can't find media IP in the message\n");
 				return -1;
 			}
450962d0
 		}
 
 		/* Extract the port on sdp_port */
 		tmpstr1.s = m1p;
 		tmpstr1.len = m2p - m1p;
6face3f2
 		if (extract_media_attr(&tmpstr1, &sdp_media, &sdp_port, &sdp_transport, &sdp_payload, &is_rtp) == -1) {
450962d0
 			LM_ERR("can't extract media attr from the message\n");
 			return -1;
 		}
 
 		/* Allocate a stream cell */
6face3f2
 		stream = add_sdp_stream(session, stream_num, &sdp_media, &sdp_port, &sdp_transport, &sdp_payload, is_rtp, pf, &sdp_ip);
450962d0
 		if (stream == 0) return -1;
 
cbf2cb0c
         /* Store fast access ptr to raw stream */
         stream->raw_stream.s = tmpstr1.s; 
         stream->raw_stream.len = tmpstr1.len;
                 
5a1d36d4
 		/* increment total number of streams */
 		_sdp->streams_num++;
 
450962d0
 		/* 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);
12a33bc8
 				if (payload_attr == NULL) return -1;
450962d0
 				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 */
dc03199e
 			if (NULL == init_p_payload_attr(stream, SDP_USE_PKG_MEM)) {
450962d0
 				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;
 
c90ff4d4
 			if (parse_payload_attr && extract_ptime(&tmpstr1, &stream->ptime) == 0) {
 				a1p = stream->ptime.s + stream->ptime.len;
27e1d6f4
 			} else if (parse_payload_attr && extract_sendrecv_mode(&tmpstr1,
 					&stream->sendrecv_mode, &stream->is_on_hold) == 0) {
c90ff4d4
 				a1p = stream->sendrecv_mode.s + stream->sendrecv_mode.len;
450962d0
 			} 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);
0e608506
 			} else if (extract_rtcp(&tmpstr1, &stream->rtcp_port) == 0) {
 				a1p = stream->rtcp_port.s + stream->rtcp_port.len;
12a33bc8
 			} else if (parse_payload_attr && extract_fmtp(&tmpstr1,&rtp_payload,&fmtp_string) == 0){
 				a1p = fmtp_string.s + fmtp_string.len;
 				payload_attr = (sdp_payload_attr_t*)get_sdp_payload4payload(stream, &rtp_payload);
 				set_sdp_payload_fmtp(payload_attr, &fmtp_string);
b7a1ea4c
 			} else if (parse_payload_attr && extract_candidate(&tmpstr1, stream) == 0) {
 			        a1p += 2;
88a8eb34
 			} else if (parse_payload_attr && extract_field(&tmpstr1, &stream->remote_candidates,
 								       remote_candidates) == 0) {
 			        a1p += 2;
450962d0
 			} 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;
550a879a
 			} else {
 				/* unknown a= line, ignore -- jump over it */
e5785361
 				LM_DBG("ignoring unknown type in a= line: `%.*s...'\n",
 						(tmpstr1.len>20)?20:tmpstr1.len, tmpstr1.s);
550a879a
 				a1p += 2;
450962d0
 			}
 
550a879a
 			a2p = find_first_sdp_line(a1p, m2p, 'a', m2p);
450962d0
 		}
27e1d6f4
 		/* Let's detect if the media is on hold by checking
 		 * the good old "0.0.0.0" connection address */
 		if (!stream->is_on_hold) {
 			if (stream->ip_addr.s && stream->ip_addr.len) {
 				if (stream->ip_addr.len == HOLD_IP_LEN &&
 					strncmp(stream->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0)
51be5b7a
 					stream->is_on_hold = RFC2543_HOLD;
27e1d6f4
 			} else if (session->ip_addr.s && session->ip_addr.len) {
 				if (session->ip_addr.len == HOLD_IP_LEN &&
 					strncmp(session->ip_addr.s, HOLD_IP_STR, HOLD_IP_LEN)==0)
51be5b7a
 					stream->is_on_hold = RFC2543_HOLD;
27e1d6f4
 			}
 		}
450962d0
 		++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;
b5aafda1
 	str cnt_disp;
450962d0
 	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) {
b5aafda1
 			while (('\n' == *rest) || ('\r' == *rest) || ('\t' == *rest)|| (' ' == *rest)) rest++; /* Skip any whitespace */
 			_sdp->raw_sdp.s = rest;
 			_sdp->raw_sdp.len = d2p-rest;
 			/* LM_DBG("we need to check session %d: <%.*s>\n", session_num, _sdp.raw_sdp.len, _sdp.raw_sdp.s); */
 			res = parse_sdp_session(&_sdp->raw_sdp, session_num, &cnt_disp, _sdp);
450962d0
 			if (res != 0) {
8ec30688
 				/* _sdp is freed by the calling function */
450962d0
 				return -1;
 			}
 			session_num++;
 		}
 	}
 	return 0;
 }
 
 /**
e1c6eafe
  * @brief Parse SIP SDP body and store in _m->body.
450962d0
  *
e1c6eafe
  * @param _m the SIP message structure
  * @return 0 on success, < 0 on error and 1 if there is no message body
450962d0
  */
 int parse_sdp(struct sip_msg* _m)
 {
 	int res;
 	str body, mp_delimiter;
 	int mime;
 
d6ac6a12
 	if (_m->body) {
450962d0
 		return 0;  /* Already parsed */
 	}
 
 	body.s = get_body(_m);
12a33bc8
 	if (body.s==NULL) {
450962d0
 		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;
 			}
d6ac6a12
 			res = parse_sdp_session(&body, 0, NULL, (sdp_info_t*)_m->body);
450962d0
 			if (res != 0) {
faf7806b
 				LM_DBG("failed to parse sdp session - freeing sdp\n");
cd999eae
 				free_sdp((sdp_info_t**)(void*)&_m->body);
faf7806b
 				return res;
450962d0
 			}
b5aafda1
 			/* The whole body is SDP */
 			((sdp_info_t*)_m->body)->raw_sdp.s = body.s;
 			((sdp_info_t*)_m->body)->raw_sdp.len = body.len;
450962d0
 			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;
 				}
d6ac6a12
 				res = parse_mixed_content(&body, mp_delimiter, (sdp_info_t*)_m->body);
450962d0
 				if (res != 0) {
8ec30688
 					LM_DBG("free_sdp\n");
cd999eae
 					free_sdp((sdp_info_t**)(void*)&_m->body);
450962d0
 				}
 				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;
b7a1ea4c
         sdp_ice_attr_t *tmp;
450962d0
 
 	LM_DBG("_sdp = %p\n", _sdp);
12a33bc8
 	if (sdp == NULL) return;
450962d0
 	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);
 			}
b7a1ea4c
 			while (l_stream->ice_attr) {
 			    tmp = l_stream->ice_attr->next;
 			    pkg_free(l_stream->ice_attr);
35bc07e6
 			    l_stream->ice_attr = tmp;
b7a1ea4c
 			}
450962d0
 			pkg_free(l_stream);
 		}
 		pkg_free(l_session);
 	}
 	pkg_free(sdp);
 	*_sdp = NULL;
 }
 
 
986e8c19
 void print_sdp_stream(sdp_stream_cell_t *stream, int log_level)
450962d0
 {
 	sdp_payload_attr_t *payload;
b7a1ea4c
         sdp_ice_attr_t *ice_attr;
450962d0
 
88a8eb34
 	LOG(log_level , "....stream[%d]:%p=>%p {%p} '%.*s' '%.*s:%.*s:%.*s' '%.*s' [%d] '%.*s' '%.*s:%.*s' (%d)=>%p (%d)=>%p '%.*s' '%.*s' '%.*s' '%.*s' '%.*s' '%.*s' '%.*s'\n",
450962d0
 		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,
0e608506
 		stream->rtcp_port.len, stream->rtcp_port.s,
6face3f2
 		stream->transport.len, stream->transport.s, stream->is_rtp,
450962d0
 		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,
b7a1ea4c
 		stream->ice_attrs_num, stream->ice_attr,
c90ff4d4
 		stream->sendrecv_mode.len, stream->sendrecv_mode.s,
 		stream->ptime.len, stream->ptime.s,
450962d0
 		stream->path.len, stream->path.s,
 		stream->max_size.len, stream->max_size.s,
 		stream->accept_types.len, stream->accept_types.s,
88a8eb34
 	        stream->accept_wrapped_types.len, stream->accept_wrapped_types.s,
 	    	stream->remote_candidates.len, stream->remote_candidates.s);
450962d0
 	payload = stream->payload_attr;
 	while (payload) {
12a33bc8
 		LOG(log_level, "......payload[%d]:%p=>%p p_payload_attr[%d]:%p '%.*s' '%.*s' '%.*s' '%.*s' '%.*s'\n",
450962d0
 			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,
12a33bc8
 			payload->rtp_params.len, payload->rtp_params.s,
 			payload->fmtp_string.len, payload->fmtp_string.s);
450962d0
 		payload=payload->next;
 	}
b7a1ea4c
 	ice_attr = stream->ice_attr;
 	while (ice_attr) {
 	    LOG(log_level, "......'%.*s' %u\n",
 		ice_attr->foundation.len, ice_attr->foundation.s,
 		ice_attr->component_id);
 	    ice_attr = ice_attr->next;
 	}
450962d0
 }
 
986e8c19
 void print_sdp_session(sdp_session_cell_t *session, int log_level)
450962d0
 {
1cefc0c5
 	sdp_stream_cell_t *stream;
450962d0
 
 	if (session==NULL) {
 		LM_ERR("NULL session\n");
 		return;
 	}
1cefc0c5
 	stream = session->streams;
450962d0
 
10ba0083
 	LOG(log_level, "..session[%d]:%p=>%p '%.*s' '%.*s' '%.*s' '%.*s:%.*s' (%d)=>%p\n",
450962d0
 		session->session_num, session, session->next,
 		session->cnt_disp.len, session->cnt_disp.s,
10ba0083
 		session->ip_addr.len, session->ip_addr.s,
 		session->o_ip_addr.len, session->o_ip_addr.s,
450962d0
 		session->bw_type.len, session->bw_type.s, session->bw_width.len, session->bw_width.s,
 		session->streams_num, session->streams);
 	while (stream) {
986e8c19
 		print_sdp_stream(stream, log_level);
450962d0
 		stream=stream->next;
 	}
 }
 
 
986e8c19
 void print_sdp(sdp_info_t* sdp, int log_level)
450962d0
 {
 	sdp_session_cell_t *session;
 
e81b3aac
 	if (!sdp)
 	{
 	    LOG(log_level, "no sdp body\n");
 	    return;
 	}
 
986e8c19
 	LOG(log_level, "sdp:%p=>%p (%d:%d)\n", sdp, sdp->sessions, sdp->sessions_num, sdp->streams_num);
450962d0
 	session = sdp->sessions;
 	while (session) {
986e8c19
 		print_sdp_session(session, log_level);
450962d0
 		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 +
12a33bc8
 			attr->rtp_params.len +
 			attr->fmtp_string.len;
450962d0
 	clone_attr = (sdp_payload_attr_t*)shm_malloc(len);
 	if (clone_attr == NULL) {
e3ecad34
 		SHM_MEM_ERROR;
450962d0
 		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;
 	}
 
12a33bc8
 	if (attr->fmtp_string.len) {
 		clone_attr->fmtp_string.s = p;
 		clone_attr->fmtp_string.len = attr->fmtp_string.len;
 		memcpy( p, attr->fmtp_string.s, attr->fmtp_string.len);
 		p += attr->fmtp_string.len;
 	}
 
450962d0
 	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) +
0e608506
 			stream->ip_addr.len +
450962d0
 			stream->media.len +
 			stream->port.len +
 			stream->transport.len +
c90ff4d4
 			stream->sendrecv_mode.len +
 			stream->ptime.len +
450962d0
 			stream->payloads.len +
 			stream->bw_type.len +
 			stream->bw_width.len +
0e608506
 			stream->rtcp_port.len;
450962d0
 	clone_stream = (sdp_stream_cell_t*)shm_malloc(len);
 	if (clone_stream == NULL) {
e3ecad34
 		SHM_MEM_ERROR;
450962d0
 		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) {
dc03199e
 		if (NULL == init_p_payload_attr(clone_stream, SDP_USE_SHM_MEM)) {
450962d0
 			goto error;
 		}
 	}
 
 	clone_stream->stream_num = stream->stream_num;
 	clone_stream->pf = stream->pf;
 
0e608506
 	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->ip_addr.len;
 	}
 
6face3f2
 	clone_stream->is_rtp = stream->is_rtp;
 
450962d0
 	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;
 	}
 
c90ff4d4
 	if (stream->sendrecv_mode.len) {
 		clone_stream->sendrecv_mode.s = p;
 		clone_stream->sendrecv_mode.len = stream->sendrecv_mode.len;
 		memcpy( p, stream->sendrecv_mode.s, stream->sendrecv_mode.len);
 		p += stream->sendrecv_mode.len;
 	}
 
 	if (stream->ptime.len) {
 		clone_stream->ptime.s = p;
 		clone_stream->ptime.len = stream->ptime.len;
 		memcpy( p, stream->ptime.s, stream->ptime.len);
 		p += stream->ptime.len;
 	}
 
450962d0
 	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;
 	}
 
0e608506
 	if (stream->rtcp_port.len) {
 		clone_stream->rtcp_port.s = p;
 		clone_stream->rtcp_port.len = stream->rtcp_port.len;
 		memcpy( p, stream->rtcp_port.s, stream->rtcp_port.len);
 		p += stream->rtcp_port.len;
450962d0
 	}
 
 	/* 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;
 	}
88429acf
 	len = sizeof(sdp_session_cell_t) +
 		session->cnt_disp.len +
 		session->ip_addr.len +
 		session->o_ip_addr.len +
 		session->bw_type.len +
 		session->bw_width.len;
450962d0
 	clone_session = (sdp_session_cell_t*)shm_malloc(len);
 	if (clone_session == NULL) {
e3ecad34
 		SHM_MEM_ERROR;
450962d0
 		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;
88429acf
 	clone_session->pf = session->pf;
 	clone_session->o_pf = session->o_pf;
450962d0
 	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;
 	}
 
88429acf
 	if (session->ip_addr.len) {
 		clone_session->ip_addr.s = p;
 		clone_session->ip_addr.len = session->ip_addr.len;
 		memcpy( p, session->ip_addr.s, session->ip_addr.len);
 		p += session->ip_addr.len;
 	}
 
 	if (session->o_ip_addr.len) {
 		clone_session->o_ip_addr.s = p;
 		clone_session->o_ip_addr.len = session->o_ip_addr.len;
 		memcpy( p, session->o_ip_addr.s, session->o_ip_addr.len);
 		p += session->o_ip_addr.len;
 	}
 
450962d0
 	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);
0e608506
 		p += session->bw_width.len;
450962d0
 	}
 
 	return clone_session;
 error:
 	free_cloned_sdp_session(clone_session);
 	return NULL;
 }
 
 sdp_info_t * clone_sdp_info(struct sip_msg* _m)
 {
12a33bc8
 	sdp_info_t *clone_sdp_info, *sdp_info=(sdp_info_t*)_m->body;
450962d0
 	sdp_session_cell_t *clone_session, *prev_clone_session, *session;
 	int i, len;
 
 	if (sdp_info==NULL) {
 		LM_ERR("no sdp to clone\n");
d6ac6a12
 		return NULL;
 	}
 	if (sdp_info->type != MSG_BODY_SDP) {
 		LM_ERR("Unsupported body type\n");
450962d0
 		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) {
e3ecad34
 		SHM_MEM_ERROR;
450962d0
 		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;
5a1d36d4
 	clone_sdp_info->streams_num = sdp_info->streams_num;
450962d0
 
 	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;
 }
 
dc03199e
 #undef SDP_USE_PKG_MEM
 #undef SDP_USE_SHM_MEM