freeswitch/libs/sofia-sip/libsofia-sip-ua/sdp/sdp.c
2013-01-14 08:31:10 -05:00

1907 lines
48 KiB
C

/*
* This file is part of the Sofia-SIP package
*
* Copyright (C) 2005 Nokia Corporation.
*
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
/**@CFILE sdp.c Simple SDP interface.
*
* @author Pekka Pessi <Pekka.Pessi@nokia.com>
* @author Kai Vehmanen <kai.vehmanen@nokia.com>
*
* @date Created: Fri Feb 18 10:25:08 2000 ppessi
*/
#include "config.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_types.h>
#include <sofia-sip/su_string.h>
#include "sofia-sip/sdp.h"
struct align { void *_a; char _b; };
#define ALIGN(v, n) ((n - (intptr_t)(v)) & (n - 1))
#define STRUCT_ALIGN_ (sizeof(struct align) - offsetof(struct align, _b))
#define STRUCT_ALIGN(v) ALIGN(v, sizeof(void *))
#define ASSERT_STRUCT_ALIGN(p) \
(STRUCT_ALIGN(p) ? (void)assert(!"STRUCT_ALIGNED(" #p ")") : (void)0)
const unsigned sdp_struct_align_ = sizeof(void *) - STRUCT_ALIGN_;
#define STR_XTRA(rv, s) ((s) ? rv += strlen((s)) + 1 : 0)
#define PTR_XTRA(rv, p, f) \
((p) ? (rv += STRUCT_ALIGN(rv) + f(p)) : 0)
#define LST_XTRA(rv, l, f) \
((l) ? (rv += STRUCT_ALIGN(rv) + list_xtra_all((xtra_f*)f, l)) : 0)
#define STRUCT_DUP(p, dst, src) \
ASSERT_STRUCT_ALIGN(p); \
((*(int*)(src) >= (int)sizeof(*src) \
? (dst = memcpy((p), (src), sizeof(*src))) \
: (dst = memcpy((p), (src), *(int*)(src))), \
memset((p)+*(int*)(src), 0, sizeof(*src) - *(int*)(src))), \
((p) += sizeof(*src)))
#define STRUCT_DUP2(p, dst, src) \
ASSERT_STRUCT_ALIGN(p); assert(*(int*)(src) >= (int)sizeof(*src)); \
(dst = memcpy((p), (src), *(int*)(src)), ((p) += *(int*)(src)))
#define STR_DUP(p, dst, src, m) \
((src->m) ? ((dst->m) = strcpy((p), (src->m)), (p) += strlen((p)) + 1) \
: ((dst->m) = 0))
#define PTR_DUP(p, dst, src, m, dup) \
((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)), ((dup)(&(p), (src->m)))): 0)
#define LST_DUP(p, dst, src, m, dup) \
((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)),\
list_dup_all((dup_f*)(dup), &(p), src->m)) : 0)
#define MED_XTRA_EX(rv, l, c) \
((l) ? (rv += STRUCT_ALIGN(rv) + media_xtra_ex(l, c)) : 0)
#define MED_DUP_EX(p, dst, src, m, dst_c, src_c) \
((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)),\
media_dup_all(&(p), src->m, dst, dst_c, src_c)) : 0)
#define MED_XTRA_ALL(rv, m) \
((m) ? (rv += STRUCT_ALIGN(rv) + media_xtra_all(m)) : 0)
#define MED_DUP_ALL(p, dst, src, m) \
((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)),\
media_dup_all(&(p), src->m, dst)) : 0)
typedef size_t xtra_f(void const *);
typedef void *dup_f(char **bb, void const *src);
static size_t list_xtra_all(xtra_f *xtra, void const *v);
static void *list_dup_all(dup_f *dup, char **bb, void const *vsrc);
static size_t session_xtra(sdp_session_t const *o);
static sdp_session_t *session_dup(char **pp, sdp_session_t const *o);
static size_t origin_xtra(sdp_origin_t const *o);
static sdp_origin_t *origin_dup(char **pp, sdp_origin_t const *o);
static size_t connection_xtra(sdp_connection_t const *o);
static sdp_connection_t *connection_dup(char **pp, sdp_connection_t const *o);
static size_t bandwidth_xtra(sdp_bandwidth_t const *o);
static sdp_bandwidth_t *bandwidth_dup(char **pp, sdp_bandwidth_t const *o);
static size_t time_xtra(sdp_time_t const *o);
static sdp_time_t *time_dup(char **pp, sdp_time_t const *o);
static size_t repeat_xtra(sdp_repeat_t const *o);
static sdp_repeat_t *repeat_dup(char **pp, sdp_repeat_t const *o);
static size_t zone_xtra(sdp_zone_t const *o);
static sdp_zone_t *zone_dup(char **pp, sdp_zone_t const *o);
static size_t key_xtra(sdp_key_t const *o);
static sdp_key_t *key_dup(char **pp, sdp_key_t const *o);
static size_t attribute_xtra(sdp_attribute_t const *o);
static sdp_attribute_t *attribute_dup(char **pp, sdp_attribute_t const *o);
static size_t list_xtra(sdp_list_t const *o);
static sdp_list_t *list_dup(char **pp, sdp_list_t const *o);
static size_t rtpmap_xtra(sdp_rtpmap_t const *o);
static sdp_rtpmap_t *rtpmap_dup(char **pp, sdp_rtpmap_t const *o);
static size_t media_xtra(sdp_media_t const *o);
static sdp_media_t *media_dup(char **pp,
sdp_media_t const *o,
sdp_session_t *sdp);
#ifdef nomore
static size_t media_xtra_ex(sdp_media_t const *o,
sdp_connection_t const *c);
static sdp_media_t *media_dup_ex(char **pp,
sdp_media_t const *o,
sdp_session_t *sdp,
sdp_connection_t *dst_c,
sdp_connection_t const *src_c);
#endif
static size_t media_xtra_all(sdp_media_t const *o);
static sdp_media_t *media_dup_all(char **pp,
sdp_media_t const *o,
sdp_session_t *sdp);
/** Define a function body duplicating an SDP structure. */
#define SDP_DUP(type, name) \
sdp_##type##_t *rv; size_t size; char *p, *end; \
if (!name) return NULL; \
size = type##_xtra(name); \
p = su_alloc(h, size); end = p + size; \
rv = type##_dup(&p, name); \
assert(p == end); \
return rv;
/** Define a function body duplicating a list of SDP structures. */
#define SDP_LIST_DUP(type, name) \
sdp_##type##_t *rv; size_t size; char *p, *end; \
if (!name) return NULL; \
size = list_xtra_all((xtra_f*)type##_xtra, name); \
rv = su_alloc(h, size); p = (char *)rv; end = p + size; \
list_dup_all((dup_f*)type##_dup, &p, name); \
assert(p == end); \
return rv;
/**Duplicate an SDP origin description.
*
* The function sdp_origin_dup() duplicates (deeply copies) an SDP origin
* description @a o allocating memory using memory @a home.
*
* @param h Memory home
* @param o SDP origin description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_origin_t structure is
* returned, otherwise NULL is returned.
*/
sdp_origin_t *sdp_origin_dup(su_home_t *h, sdp_origin_t const *o)
{
SDP_DUP(origin, o);
}
/**Duplicate an SDP connection description.
*
* The function sdp_connection_dup() duplicates (deeply copies) a list of
* SDP connection description @a c allocating memory using memory @a home.
*
* @param h Memory home
* @param c SDP connection description to be duplicated
*
* @note The duplicated list is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_connection_t structure is
* returned, otherwise NULL is returned.
*/
sdp_connection_t *sdp_connection_dup(su_home_t *h, sdp_connection_t const *c)
{
SDP_LIST_DUP(connection, c);
}
/**Duplicate an SDP bandwidth description.
*
* The function sdp_bandwidth_dup() duplicates (deeply copies) a list of SDP
* bandwidth descriptions @a b allocating memory using memory @a home.
*
* @param h Memory home
* @param b SDP bandwidth description to be duplicated
*
* @note The duplicated list is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_bandwidth_t structure is
* returned, otherwise NULL is returned.
*/
sdp_bandwidth_t *sdp_bandwidth_dup(su_home_t *h, sdp_bandwidth_t const *b)
{
SDP_LIST_DUP(bandwidth, b);
}
/**Duplicate an SDP time description.
*
* The function sdp_time_dup() duplicates (deeply copies) a list of SDP time
* descriptions @a t allocating memory using memory @a home.
*
* @param h Memory home
* @param t SDP time description to be duplicated
*
* @note The duplicated list is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_time_t structure is
* returned, otherwise NULL is returned.
*/
sdp_time_t *sdp_time_dup(su_home_t *h, sdp_time_t const *t)
{
SDP_LIST_DUP(time, t);
}
/**Duplicate an SDP repeat description.
*
* The function sdp_repeat_dup() duplicates (deeply copies) an SDP repeat
* description @a r allocating memory using memory @a home.
*
* @param h Memory home
* @param r SDP repeat description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_repeat_t structure is
* returned, otherwise NULL is returned.
*/
sdp_repeat_t *sdp_repeat_dup(su_home_t *h, sdp_repeat_t const *r)
{
SDP_DUP(repeat, r);
}
/**Duplicate an SDP zone description.
*
* The function sdp_zone_dup() duplicates (deeply copies) an SDP zone
* description @a z allocating memory using memory @a home.
*
* @param h Memory home
* @param z SDP zone description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_zone_t structure is
* returned, otherwise NULL is returned.
*/
sdp_zone_t *sdp_zone_dup(su_home_t *h, sdp_zone_t const *z)
{
SDP_DUP(zone, z);
}
/**Duplicate an SDP key description.
*
* The function sdp_key_dup() duplicates (deeply copies) an SDP key
* description @a k allocating memory using memory @a home.
*
* @param h Memory home
* @param k SDP key description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_key_t structure is
* returned, otherwise NULL is returned.
*/
sdp_key_t *sdp_key_dup(su_home_t *h, sdp_key_t const *k)
{
SDP_DUP(key, k);
}
/**Duplicate an SDP attribute list.
*
* The function sdp_attribute_dup() duplicates (deeply copies) an SDP
* attribute list @a a allocating memory using memory @a home.
*
* @param h Memory home
* @param a SDP attribute description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_attribute_t structure is
* returned, otherwise NULL is returned.
*/
sdp_attribute_t *sdp_attribute_dup(su_home_t *h, sdp_attribute_t const *a)
{
SDP_LIST_DUP(attribute, a);
}
/**Duplicate an SDP list of text.
*
* The function sdp_list_dup() duplicates (deeply copies) an SDP text
* list @a l allocating memory using memory @a home.
*
* @param h Memory home
* @param l SDP list description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_list_t structure is
* returned, otherwise NULL is returned.
*/
sdp_list_t *sdp_list_dup(su_home_t *h, sdp_list_t const *l)
{
SDP_LIST_DUP(list, l);
}
/**Duplicate an SDP rtpmap list.
*
* The function sdp_rtpmap_dup() duplicates (deeply copies) an SDP rtpmap
* list @a rm allocating memory using memory @a home.
*
* @param h Memory home
* @param rm SDP rtpmap description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_rtpmap_t structure is
* returned, otherwise NULL is returned.
*/
sdp_rtpmap_t *sdp_rtpmap_dup(su_home_t *h, sdp_rtpmap_t const *rm)
{
SDP_LIST_DUP(rtpmap, rm);
}
/**Duplicate an SDP media description.
*
* The function sdp_media_dup() duplicates (deeply copies) an SDP media
* description @a m allocating memory using memory @a home.
*
* @param h Memory home
* @param m SDP media description to be duplicated
* @param sdp SDP session description to which the newly allocated
* media description is linked
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_media_t structure is
* returned, otherwise NULL is returned.
*/
sdp_media_t *sdp_media_dup(su_home_t *h, sdp_media_t const *m,
sdp_session_t *sdp)
{
sdp_media_t *rv; size_t size; char *p, *end;
size = media_xtra(m);
p = su_alloc(h, size); end = p + size;
rv = media_dup(&p, m, sdp);
assert(p == end);
return rv;
}
/**Duplicate an SDP media description.
*
* The function sdp_media_dup_all() duplicates (deeply copies) a list of SDP
* media descriptions @a m allocating memory using memory @a home.
*
* @param h Memory home
* @param m list of SDP media descriptions to be duplicated
* @param sdp SDP session description to which the newly allocated
* media descriptions are linked
*
* @note The duplicated list is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to a newly allocated list of sdp_media_t
* structures is returned, otherwise NULL is returned.
*/
sdp_media_t *sdp_media_dup_all(su_home_t *h, sdp_media_t const *m,
sdp_session_t *sdp)
{
sdp_media_t *rv; size_t size; char *p, *end;
size = media_xtra_all(m);
p = su_alloc(h, size); end = p + size;
rv = media_dup_all(&p, m, sdp);
assert(p == end);
return rv;
}
#ifdef nomore /* really deprecated */
/**Duplicate media description with common address.
*
* This function is provided in order to avoid duplicate @c c= lines. If
* the @c c= line in media description equals to @a src_c, it is not
* duplicated but replaced with @a dst_c instead.
*
* @param home Memory home
* @param src SDP media description to be duplicated
* @param sdp SDP session description to which the newly allocated
* media description is linked
* @param dst_c Connection description used instead of duplicate of @a src_c.
* @param src_c Connection description not to be duplicated
* @return
* If successful, a pointer to newly allocated sdp_media_t structure is
* returned, otherwise NULL is returned.
*
* @deprecated
* This function is deprecated. Use sdp_media_dup() instead.
*/
sdp_media_t *sdp_media_dup_ex(su_home_t *home,
sdp_media_t const *src,
sdp_session_t *sdp,
sdp_connection_t *dst_c,
sdp_connection_t const *src_c)
{
sdp_media_t *rv; size_t size; char *p, *end;
size = media_xtra_all(src, src_c);
p = su_alloc(home, size); end = p + size;
rv = media_dup_all(&p, src, sdp, dst_c, src_c);
assert(p == end);
return rv;
}
#endif
/* ---------------------------------------------------------------------- */
static size_t origin_xtra(sdp_origin_t const *o)
{
size_t rv = sizeof(*o);
STR_XTRA(rv, o->o_username);
PTR_XTRA(rv, o->o_address, connection_xtra);
return rv;
}
static
sdp_origin_t *origin_dup(char **pp, sdp_origin_t const *src)
{
char *p;
sdp_origin_t *o;
p = *pp;
STRUCT_DUP(p, o, src);
STR_DUP(p, o, src, o_username);
PTR_DUP(p, o, src, o_address, connection_dup);
assert((size_t)(p - *pp) == origin_xtra(src));
*pp = p;
return o;
}
static size_t connection_xtra(sdp_connection_t const *c)
{
size_t rv = sizeof(*c);
STR_XTRA(rv, c->c_address);
return rv;
}
static
sdp_connection_t *connection_dup(char **pp, sdp_connection_t const *src)
{
char *p;
sdp_connection_t *c;
p = *pp;
STRUCT_DUP(p, c, src);
c->c_next = NULL;
STR_DUP(p, c, src, c_address);
assert((size_t)(p - *pp) == connection_xtra(src));
*pp = p;
return c;
}
static size_t bandwidth_xtra(sdp_bandwidth_t const *b)
{
size_t rv = sizeof(*b);
STR_XTRA(rv, b->b_modifier_name);
return rv;
}
static
sdp_bandwidth_t *bandwidth_dup(char **pp, sdp_bandwidth_t const *src)
{
char *p;
sdp_bandwidth_t *b;
p = *pp;
STRUCT_DUP(p, b, src);
b->b_next = NULL;
STR_DUP(p, b, src, b_modifier_name);
assert((size_t)(p - *pp) == bandwidth_xtra(src));
*pp = p;
return b;
}
static size_t time_xtra(sdp_time_t const *t)
{
size_t rv = sizeof(*t);
PTR_XTRA(rv, t->t_repeat, repeat_xtra);
PTR_XTRA(rv, t->t_zone, zone_xtra);
return rv;
}
static
sdp_time_t *time_dup(char **pp, sdp_time_t const *src)
{
char *p;
sdp_time_t *t;
p = *pp;
STRUCT_DUP(p, t, src);
t->t_next = NULL;
PTR_DUP(p, t, src, t_repeat, repeat_dup);
PTR_DUP(p, t, src, t_zone, zone_dup);
assert((size_t)(p - *pp) == time_xtra(src));
*pp = p;
return t;
}
static size_t repeat_xtra(sdp_repeat_t const *r)
{
return (size_t)r->r_size;
}
static
sdp_repeat_t *repeat_dup(char **pp, sdp_repeat_t const *src)
{
char *p;
sdp_repeat_t *r;
p = *pp;
STRUCT_DUP2(p, r, src);
assert((size_t)(p - *pp) == repeat_xtra(src));
*pp = p;
return r;
}
static size_t zone_xtra(sdp_zone_t const *z)
{
return z->z_size;
}
static
sdp_zone_t *zone_dup(char **pp, sdp_zone_t const *src)
{
char *p;
sdp_zone_t *z;
p = *pp;
STRUCT_DUP2(p, z, src);
assert((size_t)(p - *pp) == zone_xtra(src));
*pp = p;
return z;
}
static size_t key_xtra(sdp_key_t const *k)
{
size_t rv = sizeof(*k);
STR_XTRA(rv, k->k_method_name);
STR_XTRA(rv, k->k_material);
return rv;
}
static
sdp_key_t *key_dup(char **pp, sdp_key_t const *src)
{
char *p;
sdp_key_t *k;
p = *pp;
STRUCT_DUP(p, k, src);
STR_DUP(p, k, src, k_method_name);
STR_DUP(p, k, src, k_material);
assert((size_t)(p - *pp) == key_xtra(src));
*pp = p;
return k;
}
static size_t attribute_xtra(sdp_attribute_t const *a)
{
size_t rv = sizeof(*a);
STR_XTRA(rv, a->a_name);
STR_XTRA(rv, a->a_value);
return rv;
}
static
sdp_attribute_t *attribute_dup(char **pp, sdp_attribute_t const *src)
{
char *p;
sdp_attribute_t *a;
p = *pp;
STRUCT_DUP(p, a, src);
a->a_next = NULL;
STR_DUP(p, a, src, a_name);
STR_DUP(p, a, src, a_value);
assert((size_t)(p - *pp) == attribute_xtra(src));
*pp = p;
return a;
}
static size_t media_xtra(sdp_media_t const *m)
{
size_t rv = sizeof(*m);
STR_XTRA(rv, m->m_type_name);
STR_XTRA(rv, m->m_proto_name);
LST_XTRA(rv, m->m_format, list_xtra);
LST_XTRA(rv, m->m_rtpmaps, rtpmap_xtra);
STR_XTRA(rv, m->m_information);
LST_XTRA(rv, m->m_connections, connection_xtra);
LST_XTRA(rv, m->m_bandwidths, bandwidth_xtra);
PTR_XTRA(rv, m->m_key, key_xtra);
LST_XTRA(rv, m->m_attributes, attribute_xtra);
return rv;
}
static
sdp_media_t *media_dup(char **pp,
sdp_media_t const *src,
sdp_session_t *sdp)
{
char *p;
sdp_media_t *m;
p = *pp;
STRUCT_DUP(p, m, src);
m->m_next = NULL;
STR_DUP(p, m, src, m_type_name);
STR_DUP(p, m, src, m_proto_name);
LST_DUP(p, m, src, m_format, list_dup);
LST_DUP(p, m, src, m_rtpmaps, rtpmap_dup);
STR_DUP(p, m, src, m_information);
LST_DUP(p, m, src, m_connections, connection_dup);
LST_DUP(p, m, src, m_bandwidths, bandwidth_dup);
PTR_DUP(p, m, src, m_key, key_dup);
LST_DUP(p, m, src, m_attributes, attribute_dup);
/* note! we must not implicitly use 'src->m_session' as it
might point to a temporary session */
m->m_session = sdp;
m->m_rejected = src->m_rejected;
m->m_mode = src->m_mode;
assert((size_t)(p - *pp) == media_xtra(src));
*pp = p;
return m;
}
#ifdef nomore
static
int media_xtra_ex(sdp_media_t const *m, sdp_connection_t const *c)
{
int rv = 0;
for (; m; m = m->m_next) {
rv += STRUCT_ALIGN(rv);
rv += sizeof(*m);
STR_XTRA(rv, m->m_type_name);
STR_XTRA(rv, m->m_proto_name);
LST_XTRA(rv, m->m_format, list_xtra);
LST_XTRA(rv, m->m_rtpmaps, rtpmap_xtra);
STR_XTRA(rv, m->m_information);
if (c != m->m_connections)
LST_XTRA(rv, m->m_connections, connection_xtra);
LST_XTRA(rv, m->m_bandwidths, bandwidth_xtra);
PTR_XTRA(rv, m->m_key, key_xtra);
LST_XTRA(rv, m->m_attributes, attribute_xtra);
}
return rv;
}
static
sdp_media_t *media_dup_ex(char **pp,
sdp_media_t const *src,
sdp_session_t *sdp,
sdp_connection_t *dst_c,
sdp_connection_t const *src_c)
{
char *p;
sdp_media_t *retval = NULL, *m, **mm = &retval;
int xtra = media_xtra_ex(src, src_c);
p = *pp;
for (; src; src = src->m_next) {
p += STRUCT_ALIGN(p);
STRUCT_DUP(p, m, src);
m->m_next = NULL;
STR_DUP(p, m, src, m_type_name);
STR_DUP(p, m, src, m_proto_name);
LST_DUP(p, m, src, m_format, list_dup);
LST_DUP(p, m, src, m_rtpmaps, rtpmap_dup);
STR_DUP(p, m, src, m_information);
if (src_c != src->m_connections)
LST_DUP(p, m, src, m_connections, connection_dup);
else
m->m_connections = dst_c;
LST_DUP(p, m, src, m_bandwidths, bandwidth_dup);
PTR_DUP(p, m, src, m_key, key_dup);
LST_DUP(p, m, src, m_attributes, attribute_dup);
/* note! we must not implicitly use 'src->m_session' as it
might point to a temporary session */
m->m_session = sdp;
m->m_rejected = src->m_rejected;
m->m_mode = src->m_mode;
assert(m);
*mm = m; mm = &m->m_next;
}
assert(p - *pp == xtra);
*pp = p;
return retval;
}
#endif
static size_t media_xtra_all(sdp_media_t const *m)
{
size_t rv = 0;
for (; m; m = m->m_next) {
rv += STRUCT_ALIGN(rv);
rv += media_xtra(m);
}
return rv;
}
static
sdp_media_t *media_dup_all(char **pp,
sdp_media_t const *src,
sdp_session_t *sdp)
{
char *p;
sdp_media_t *retval = NULL, *m, **mm = &retval;
p = *pp;
for (; src; src = src->m_next) {
p += STRUCT_ALIGN(p);
m = media_dup(&p, src, sdp);
assert(m);
*mm = m; mm = &m->m_next;
}
*pp = p;
return retval;
}
static size_t list_xtra(sdp_list_t const *l)
{
size_t rv = sizeof(*l);
rv += strlen(l->l_text) + 1;
return rv;
}
static
sdp_list_t *list_dup(char **pp, sdp_list_t const *src)
{
char *p;
sdp_list_t *l;
p = *pp;
STRUCT_DUP(p, l, src);
l->l_next = NULL;
STR_DUP(p, l, src, l_text);
assert((size_t)(p - *pp) == list_xtra(src));
*pp = p;
return l;
}
static size_t rtpmap_xtra(sdp_rtpmap_t const *rm)
{
size_t rv = sizeof(*rm);
STR_XTRA(rv, rm->rm_encoding);
STR_XTRA(rv, rm->rm_params);
STR_XTRA(rv, rm->rm_fmtp);
return rv;
}
static
sdp_rtpmap_t *rtpmap_dup(char **pp, sdp_rtpmap_t const *src)
{
char *p;
sdp_rtpmap_t *rm;
p = *pp;
STRUCT_DUP(p, rm, src);
rm->rm_next = NULL;
STR_DUP(p, rm, src, rm_encoding);
STR_DUP(p, rm, src, rm_params);
STR_DUP(p, rm, src, rm_fmtp);
assert((size_t)(p - *pp) == rtpmap_xtra(src));
*pp = p;
return rm;
}
/** Return total size of a list, including size of all nodes */
static size_t list_xtra_all(xtra_f *xtra, void const *v)
{
size_t rv = 0;
sdp_list_t const *l;
for (l = v; l; l = l->l_next) {
rv += STRUCT_ALIGN(rv);
rv += xtra(l);
}
return rv;
}
static
void *list_dup_all(dup_f *dup, char **pp, void const *vsrc)
{
char *p;
sdp_list_t const *src;
sdp_list_t *retval = NULL, *l, **ll = &retval;
p = *pp;
for (src = vsrc; src; src = src->l_next) {
p += STRUCT_ALIGN(p);
l = dup(&p, src);
assert(l);
*ll = l; ll = &l->l_next;
}
*pp = p;
return retval;
}
#if 0
static size_t XXX_xtra(sdp_XXX_t const *YYY)
{
size_t rv = sizeof(*YYY);
rv += strlen(YYY->YYY_encoding) + 1;
if (YYY->YYY_params);
rv += strlen(YYY->YYY_params) + 1;
return rv;
}
static
sdp_XXX_t *XXX_dup(char **pp, sdp_XXX_t const *src)
{
char *p;
sdp_XXX_t *YYY;
p = *pp; ASSERT_STRUCT_ALIGN(p);
YYY = memcpy(p, src, src->YYY_size);
p += src->YYY_size;
YYY->YYY_next = NULL;
ZZZ
*pp = p;
return YYY;
}
#endif
static size_t session_xtra(sdp_session_t const *sdp)
{
size_t rv = sizeof(*sdp);
PTR_XTRA(rv, sdp->sdp_origin, origin_xtra);
STR_XTRA(rv, sdp->sdp_subject);
STR_XTRA(rv, sdp->sdp_information);
STR_XTRA(rv, sdp->sdp_uri);
LST_XTRA(rv, sdp->sdp_emails, list_xtra);
LST_XTRA(rv, sdp->sdp_phones, list_xtra);
LST_XTRA(rv, sdp->sdp_connection, connection_xtra);
LST_XTRA(rv, sdp->sdp_bandwidths, bandwidth_xtra);
LST_XTRA(rv, sdp->sdp_time, time_xtra);
PTR_XTRA(rv, sdp->sdp_key, key_xtra);
LST_XTRA(rv, sdp->sdp_attributes, attribute_xtra);
STR_XTRA(rv, sdp->sdp_charset);
MED_XTRA_ALL(rv, sdp->sdp_media);
return rv;
}
static
sdp_session_t *session_dup(char **pp, sdp_session_t const *src)
{
char *p;
sdp_session_t *sdp;
p = *pp;
STRUCT_DUP(p, sdp, src);
sdp->sdp_next = NULL;
PTR_DUP(p, sdp, src, sdp_origin, origin_dup);
STR_DUP(p, sdp, src, sdp_subject);
STR_DUP(p, sdp, src, sdp_information);
STR_DUP(p, sdp, src, sdp_uri);
LST_DUP(p, sdp, src, sdp_emails, list_dup);
LST_DUP(p, sdp, src, sdp_phones, list_dup);
LST_DUP(p, sdp, src, sdp_connection, connection_dup);
LST_DUP(p, sdp, src, sdp_bandwidths, bandwidth_dup);
LST_DUP(p, sdp, src, sdp_time, time_dup);
PTR_DUP(p, sdp, src, sdp_key, key_dup);
LST_DUP(p, sdp, src, sdp_attributes, attribute_dup);
STR_DUP(p, sdp, src, sdp_charset);
MED_DUP_ALL(p, sdp, src, sdp_media);
assert((size_t)(p - *pp) == session_xtra(src));
*pp = p;
return sdp;
}
/**Duplicate an SDP session description.
*
* The function sdp_session_dup() duplicates (deeply copies) an SDP
* session description @a sdp allocating memory using memory @a home.
*
* @param h Memory home
* @param sdp SDP session description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_session_t structure is
* returned, otherwise NULL is returned.
*/
sdp_session_t *sdp_session_dup(su_home_t *h, sdp_session_t const *sdp)
{
SDP_DUP(session, sdp);
}
/* ---------------------------------------------------------------------- */
static size_t session_without_media_xtra(sdp_session_t const *sdp)
{
size_t rv = sizeof(*sdp);
PTR_XTRA(rv, sdp->sdp_origin, origin_xtra);
STR_XTRA(rv, sdp->sdp_subject);
STR_XTRA(rv, sdp->sdp_information);
STR_XTRA(rv, sdp->sdp_uri);
LST_XTRA(rv, sdp->sdp_emails, list_xtra);
LST_XTRA(rv, sdp->sdp_phones, list_xtra);
LST_XTRA(rv, sdp->sdp_connection, connection_xtra);
LST_XTRA(rv, sdp->sdp_bandwidths, bandwidth_xtra);
LST_XTRA(rv, sdp->sdp_time, time_xtra);
PTR_XTRA(rv, sdp->sdp_key, key_xtra);
LST_XTRA(rv, sdp->sdp_attributes, attribute_xtra);
STR_XTRA(rv, sdp->sdp_charset);
return rv;
}
static
sdp_session_t *session_without_media_dup(char **pp, sdp_session_t const *src)
{
char *p;
sdp_session_t *sdp;
p = *pp;
STRUCT_DUP(p, sdp, src);
sdp->sdp_next = NULL;
PTR_DUP(p, sdp, src, sdp_origin, origin_dup);
STR_DUP(p, sdp, src, sdp_subject);
STR_DUP(p, sdp, src, sdp_information);
STR_DUP(p, sdp, src, sdp_uri);
LST_DUP(p, sdp, src, sdp_emails, list_dup);
LST_DUP(p, sdp, src, sdp_phones, list_dup);
LST_DUP(p, sdp, src, sdp_connection, connection_dup);
LST_DUP(p, sdp, src, sdp_bandwidths, bandwidth_dup);
LST_DUP(p, sdp, src, sdp_time, time_dup);
PTR_DUP(p, sdp, src, sdp_key, key_dup);
LST_DUP(p, sdp, src, sdp_attributes, attribute_dup);
STR_DUP(p, sdp, src, sdp_charset);
sdp->sdp_media = NULL;
assert((size_t)(p - *pp) == session_without_media_xtra(src));
*pp = p;
return sdp;
}
/* SDP_DUP macro requires this */
typedef sdp_session_t sdp_session_without_media_t;
/**Duplicate an SDP session description without media descriptions.
*
* The function sdp_session_dup() duplicates (deeply copies) an SDP session
* description @a sdp allocating memory using memory @a home. It does not
* copy the media descriptions, however.
*
* @param h memory h
* @param sdp SDP session description to be duplicated
*
* @note The duplicated structure is allocated using a single call to
* su_alloc() and it can be freed with su_free().
*
* @return
* If successful, a pointer to newly allocated sdp_session_t structure is
* returned, otherwise NULL is returned.
*/
sdp_session_t *sdp_session_dup_without_media(su_home_t *h,
sdp_session_t const *sdp)
{
SDP_DUP(session_without_media, sdp);
}
/* ---------------------------------------------------------------------- */
/* SDP Tag classes */
#include <sofia-sip/su_tag_class.h>
size_t sdptag_session_xtra(tagi_t const *t, size_t offset)
{
sdp_session_t const *sdp = (sdp_session_t *)t->t_value;
if (sdp)
return STRUCT_ALIGN(offset) + session_xtra(sdp);
else
return 0;
}
tagi_t *sdptag_session_dup(tagi_t *dst, tagi_t const *src, void **bb)
{
sdp_session_t *sdp;
sdp_session_t const *srcsdp;
char *b;
assert(src); assert(*bb);
b = *bb;
b += STRUCT_ALIGN(b);
srcsdp = (sdp_session_t *)src->t_value;
sdp = srcsdp ? session_dup(&b, srcsdp) : NULL;
dst->t_tag = src->t_tag;
dst->t_value = (tag_value_t)sdp;
*bb = b;
return dst + 1;
}
int sdptag_session_snprintf(tagi_t const *t, char b[], size_t size)
{
sdp_session_t const *sdp;
sdp_printer_t *print;
size_t retval;
assert(t);
if (!t || !t->t_value) {
if (size && b) b[0] = 0;
return 0;
}
sdp = (sdp_session_t const *)t->t_value;
print = sdp_print(NULL, sdp, b, size, 0);
retval = sdp_message_size(print);
sdp_printer_free(print);
return (int)retval;
}
/** Tag class for SDP tags. @HIDE */
tag_class_t sdptag_session_class[1] =
{{
sizeof(sdptag_session_class),
/* tc_next */ NULL,
/* tc_len */ NULL,
/* tc_move */ NULL,
/* tc_xtra */ sdptag_session_xtra,
/* tc_dup */ sdptag_session_dup,
/* tc_free */ NULL,
/* tc_find */ NULL,
/* tc_snprintf */ sdptag_session_snprintf,
/* tc_filter */ NULL /* msgtag_str_filter */,
/* tc_ref_set */ t_ptr_ref_set,
}};
/* ---------------------------------------------------------------------- */
/** Compare two session descriptions
*/
int sdp_session_cmp(sdp_session_t const *a, sdp_session_t const *b)
{
int rv;
sdp_bandwidth_t const *ab, *bb;
sdp_attribute_t const *aa, *ba;
sdp_media_t const *am, *bm;
if ((rv = (a != NULL) - (b != NULL)))
return rv;
if (a == b)
return 0;
if ((rv = (a->sdp_version[0] - b->sdp_version[0])))
return rv;
if ((rv = sdp_origin_cmp(a->sdp_origin, b->sdp_origin)))
return rv;
if ((rv = su_strcmp(a->sdp_subject, b->sdp_subject)))
return rv;
if ((rv = su_strcmp(a->sdp_information, b->sdp_information)))
return rv;
if ((rv = su_strcmp(a->sdp_uri, b->sdp_uri)))
return rv;
if ((rv = sdp_list_cmp(a->sdp_emails, b->sdp_emails)))
return rv;
if ((rv = sdp_list_cmp(a->sdp_phones, b->sdp_phones)))
return rv;
if ((rv = sdp_connection_cmp(a->sdp_connection, b->sdp_connection)))
return rv;
for (ab = a->sdp_bandwidths, bb = b->sdp_bandwidths;
ab || bb;
ab = ab->b_next, bb = bb->b_next)
if ((rv = sdp_bandwidth_cmp(a->sdp_bandwidths, b->sdp_bandwidths)))
return rv;
if ((rv = sdp_time_cmp(a->sdp_time, b->sdp_time)))
return rv;
if ((rv = sdp_key_cmp(a->sdp_key, b->sdp_key)))
return rv;
for (aa = a->sdp_attributes, ba = b->sdp_attributes;
aa || bb;
aa = aa->a_next, ba = ba->a_next)
if ((rv = sdp_attribute_cmp(aa, ba)))
return rv;
for (am = a->sdp_media, bm = b->sdp_media;
am || bm;
am = am->m_next, bm = bm->m_next)
if ((rv = sdp_media_cmp(am, bm)))
return rv;
return 0;
}
/** Compare two origin fields
*/
int sdp_origin_cmp(sdp_origin_t const *a, sdp_origin_t const *b)
{
int rv;
if ((rv = (a != NULL) - (b != NULL)))
return rv;
if (a == b)
return 0;
if (a->o_version != b->o_version)
return a->o_version < b->o_version ? -1 : 1;
if (a->o_id != b->o_id)
return a->o_id < b->o_id ? -1 : 1;
if ((rv = su_strcasecmp(a->o_username, b->o_username)))
return rv;
if ((rv = su_strcasecmp(a->o_address->c_address, b->o_address->c_address)))
return rv;
return 0;
}
/** Compare two connection fields
*/
int sdp_connection_cmp(sdp_connection_t const *a, sdp_connection_t const *b)
{
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
if (a->c_nettype != b->c_nettype)
return a->c_nettype < b->c_nettype ? -1 : 1;
if (a->c_addrtype != b->c_addrtype)
return a->c_addrtype < b->c_addrtype ? -1 : 1;
if (a->c_ttl != b->c_ttl)
return a->c_ttl < b->c_ttl ? -1 : 1;
if (a->c_groups != b->c_groups)
return a->c_groups < b->c_groups ? -1 : 1;
return strcmp(a->c_address, b->c_address);
}
/** Compare two bandwidth (b=) fields */
int sdp_bandwidth_cmp(sdp_bandwidth_t const *a, sdp_bandwidth_t const *b)
{
int rv;
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
if (a->b_modifier != b->b_modifier)
return a->b_modifier < b->b_modifier ? -1 : 1;
if (a->b_modifier == sdp_bw_x &&
(rv = strcmp(a->b_modifier_name, b->b_modifier_name)))
return rv;
if (a->b_value != b->b_value)
return a->b_value < b->b_value ? -1 : 1;
return 0;
}
/** Compare two time fields */
int sdp_time_cmp(sdp_time_t const *a, sdp_time_t const *b)
{
int rv;
if ((rv = (a != NULL) - (b != NULL)))
return rv;
if (a == b)
return 0;
if (a->t_start != b->t_start)
return a->t_start < b->t_start ? -1 : 1;
if (a->t_stop != b->t_stop)
return a->t_stop < b->t_stop ? -1 : 1;
if ((rv = sdp_zone_cmp(a->t_zone, b->t_zone)))
return rv;
if ((rv = sdp_repeat_cmp(a->t_repeat, b->t_repeat)))
return rv;
return 0;
}
/** Compare two repeat (r=) fields */
int sdp_repeat_cmp(sdp_repeat_t const *a, sdp_repeat_t const *b)
{
int i, n;
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
if (a->r_interval != b->r_interval)
return a->r_interval < b->r_interval ? -1 : 1;
if (a->r_duration != b->r_duration)
return a->r_duration < b->r_duration ? -1 : 1;
n = a->r_number_of_offsets < b->r_number_of_offsets
? a->r_number_of_offsets : b->r_number_of_offsets;
for (i = 0; i < n; i++)
if (a->r_offsets[i] != b->r_offsets[i])
return a->r_offsets[i] < b->r_offsets[i] ? -1 : 1;
if (a->r_number_of_offsets != b->r_number_of_offsets)
return a->r_number_of_offsets < b->r_number_of_offsets ? -1 : 1;
return 0;
}
/** Compare two zone (z=) fields */
int sdp_zone_cmp(sdp_zone_t const *a, sdp_zone_t const *b)
{
int i, n;
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
n = a->z_number_of_adjustments < b->z_number_of_adjustments
? a->z_number_of_adjustments : b->z_number_of_adjustments;
for (i = 0; i < n; i++) {
if (a->z_adjustments[i].z_at != b->z_adjustments[i].z_at)
return a->z_adjustments[i].z_at < b->z_adjustments[i].z_at ? -1 : 1;
if (a->z_adjustments[i].z_offset != b->z_adjustments[i].z_offset)
return a->z_adjustments[i].z_offset < b->z_adjustments[i].z_offset
? -1 : 1;
}
if (a->z_number_of_adjustments != b->z_number_of_adjustments)
return a->z_number_of_adjustments < b->z_number_of_adjustments ? -1 : 1;
return 0;
}
/** Compare two key (k=) fields */
int sdp_key_cmp(sdp_key_t const *a, sdp_key_t const *b)
{
int rv;
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
if (a->k_method != b->k_method)
return a->k_method < b->k_method ? -1 : 1;
if (a->k_method == sdp_key_x &&
(rv = su_strcmp(a->k_method_name, b->k_method_name)))
return rv;
return su_strcmp(a->k_material, b->k_material);
}
/** Compare two attribute (a=) fields */
int sdp_attribute_cmp(sdp_attribute_t const *a, sdp_attribute_t const *b)
{
int rv;
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
if ((rv = su_strcmp(a->a_name, b->a_name)))
return rv;
return su_strcmp(a->a_value, b->a_value);
}
/** Compare two rtpmap structures. */
int sdp_rtpmap_cmp(sdp_rtpmap_t const *a, sdp_rtpmap_t const *b)
{
int rv;
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
if (a->rm_pt != b->rm_pt)
return a->rm_pt < b->rm_pt ? -1 : 1;
/* Case insensitive encoding */
if ((rv = su_strcmp(a->rm_encoding, b->rm_encoding)))
return rv;
/* Rate */
if (a->rm_rate != b->rm_rate)
return a->rm_rate < b->rm_rate ? -1 : 1;
{
char const *a_param = "1", *b_param = "1";
if (a->rm_params)
a_param = a->rm_params;
if (b->rm_params)
b_param = b->rm_params;
rv = su_strcasecmp(a_param, b_param);
if (rv)
return rv;
}
return su_strcasecmp(a->rm_fmtp, b->rm_fmtp);
}
/** Compare two lists. */
int sdp_list_cmp(sdp_list_t const *a, sdp_list_t const *b)
{
int rv;
for (;a || b; a = a->l_next, b = b->l_next) {
if (a == b)
return 0;
if ((a != NULL) != (b != NULL))
return (a != NULL) < (b != NULL) ? -1 : 1;
if ((rv = su_strcmp(a->l_text, b->l_text)))
return rv;
}
return 0;
}
/** Compare two media (m=) fields */
int sdp_media_cmp(sdp_media_t const *a, sdp_media_t const *b)
{
int rv;
sdp_connection_t const *ac, *bc;
sdp_bandwidth_t const *ab, *bb;
sdp_rtpmap_t const *arm, *brm;
sdp_attribute_t const *aa, *ba;
if (a == b)
return 0;
if ((rv = (a != NULL) - (b != NULL)))
return rv;
if (a->m_type != b->m_type)
return a->m_type < b->m_type ? -1 : 1;
if (a->m_type == sdp_media_x)
if ((rv = su_strcmp(a->m_type_name, b->m_type_name)))
return rv;
if (a->m_port != b->m_port)
return a->m_port < b->m_port ? -1 : 1;
if (a->m_port == 0 /* && b->m_port == 0 */)
/* Ignore transport protocol and media list if media has been rejected */
return 0;
if (a->m_number_of_ports != b->m_number_of_ports)
return a->m_number_of_ports < b->m_number_of_ports ? -1 : 1;
if (a->m_proto != b->m_proto)
return a->m_proto < b->m_proto ? -1 : 1;
if (a->m_proto == sdp_proto_x)
if ((rv = su_strcmp(a->m_proto_name, b->m_proto_name)))
return rv;
if (a->m_mode != b->m_mode)
return a->m_mode < b->m_mode ? -1 : 1;
for (arm = a->m_rtpmaps, brm = b->m_rtpmaps;
arm || brm;
arm = arm->rm_next, brm = brm->rm_next)
if ((rv = sdp_rtpmap_cmp(arm, brm)))
return rv;
if ((rv = sdp_list_cmp(a->m_format, b->m_format)))
return rv;
if ((rv = su_strcmp(a->m_information, b->m_information)))
return rv;
for (ac = a->m_connections, bc = b->m_connections;
ac || bc;
ac = ac->c_next, bc = bc->c_next)
if ((rv = sdp_connection_cmp(ac, bc)))
return rv;
for (ab = a->m_bandwidths, bb = b->m_bandwidths;
ab || bb;
ab = ab->b_next, bb = bb->b_next)
if ((rv = sdp_bandwidth_cmp(a->m_bandwidths, b->m_bandwidths)))
return rv;
if ((rv = sdp_key_cmp(a->m_key, b->m_key)))
return rv;
for (aa = a->m_attributes, ba = b->m_attributes;
aa || bb;
aa = aa->a_next, ba = ba->a_next)
if ((rv = sdp_attribute_cmp(aa, ba)))
return rv;
return 0;
}
/* ---------------------------------------------------------------------- */
sdp_connection_t *sdp_media_connections(sdp_media_t const *m)
{
if (m) {
if (m->m_connections)
return m->m_connections;
if (m->m_session)
return m->m_session->sdp_connection;
}
return NULL;
}
/* ---------------------------------------------------------------------- */
/** Find named attribute from given list. */
sdp_attribute_t *sdp_attribute_find(sdp_attribute_t const *a, char const *name)
{
for (; a; a = a->a_next) {
if (su_casematch(a->a_name, name))
break;
}
return (sdp_attribute_t *)a;
}
/** Find named attribute from given lists (a or a2). */
sdp_attribute_t *sdp_attribute_find2(sdp_attribute_t const *a,
sdp_attribute_t const *a2,
char const *name)
{
for (; a; a = a->a_next) {
if (su_casematch(a->a_name, name))
break;
}
if (a == 0)
for (a = a2; a; a = a->a_next) {
if (su_casematch(a->a_name, name))
break;
}
return (sdp_attribute_t *)a;
}
/** Get session mode from attribute list. */
sdp_mode_t sdp_attribute_mode(sdp_attribute_t const *a, sdp_mode_t defmode)
{
for (; a; a = a->a_next) {
if (su_casematch(a->a_name, "sendrecv"))
return sdp_sendrecv;
if (su_casematch(a->a_name, "inactive"))
return sdp_inactive;
if (su_casematch(a->a_name, "recvonly"))
return sdp_recvonly;
if (su_casematch(a->a_name, "sendonly"))
return sdp_sendonly;
}
return defmode;
}
/** Convert session mode as #sdp_attribute_t structure. */
sdp_attribute_t *sdp_attribute_by_mode(su_home_t *home, sdp_mode_t mode)
{
sdp_attribute_t *a;
char const *name;
if (mode == sdp_inactive)
name = "inactive";
else if (mode == sdp_sendonly)
name = "sendonly";
else if (mode == sdp_recvonly)
name = "recvonly";
else if (mode == sdp_sendrecv)
name = "sendrecv";
else
return NULL;
a = su_salloc(home, sizeof(*a));
if (a)
a->a_name = name;
return a;
}
/** Find a mapped attribute.
*
* A mapped attribute has form 'a=<name>:<pt> <value>' where pt is a RTP
* payload type, integer in range 0..127. For example, "a=atmmap" [@RFC3108]
* is a mapped attribute. Note that common mapped attributes, "a=rtpmap" and
* "a=fmtp" are already parsed as list of #sdp_rtpmap_t in #sdp_media_t.
*
* @param a pointer to first attribute in the list
* @param name name of the attribute
* @param pt payload type number (must be 0..127)
* @param return_result return value parameter for mapped attribute value
*
* @return Pointer to a matching attribute structure, or NULL.
*
* If a matching attribute is found, @a return_result will point to part of
* the attribute after the payload type and whitespace.
*/
sdp_attribute_t *sdp_attribute_mapped_find(sdp_attribute_t const *a,
char const *name,
int pt, char **return_result)
{
char pt_value[4];
size_t pt_len;
if (return_result)
*return_result = NULL;
if (0 > pt || pt > 127)
return NULL;
snprintf(pt_value, sizeof(pt_value), "%u", (unsigned)pt);
pt_len = strlen(pt_value);
for (; (a = sdp_attribute_find(a, name)); a = a->a_next) {
char const *value = a->a_value;
size_t wlen;
if (strncmp(value, pt_value, pt_len))
continue;
wlen = strspn(value + pt_len, " \t");
if (wlen == 0 || value[pt_len + wlen] == '\0')
continue;
if (return_result)
*return_result = (char *)value + pt_len + wlen;
return (sdp_attribute_t *)a;
}
return NULL;
}
/** Append a (list of) attribute(s) to a list of attributes. */
void sdp_attribute_append(sdp_attribute_t **list,
sdp_attribute_t const *a)
{
assert(list);
if (list == NULL || a == NULL)
return;
for (;*list; list = &(*list)->a_next)
;
*list = (sdp_attribute_t *)a;
}
/**Replace or append a attribute within a list of attributes.
*
* @retval 1 if replaced existing attribute
* @retval 0 if attribute was appended
* @retval -1 upon an error
*/
int sdp_attribute_replace(sdp_attribute_t **list,
sdp_attribute_t *a,
sdp_attribute_t **return_replaced)
{
sdp_attribute_t *replaced;
assert(list);
if (return_replaced)
*return_replaced = NULL;
if (list == NULL || a == NULL)
return -1;
assert(a->a_name != NULL); assert(a->a_next == NULL);
for (; *list; list = &(*list)->a_next) {
if (su_casematch((*list)->a_name, a->a_name))
break;
}
replaced = *list, *list = a;
if (replaced) {
a->a_next = replaced->a_next;
replaced->a_next = NULL;
if (return_replaced)
*return_replaced = replaced;
return 1;
}
return 0;
}
/** Remove a named attribute from a list of attributes. */
sdp_attribute_t *sdp_attribute_remove(sdp_attribute_t **list,
char const *name)
{
sdp_attribute_t *a;
assert(list);
if (list == NULL)
return NULL;
if (name == NULL)
return NULL;
for (a = *list; a; list = &a->a_next, a = *list) {
if (su_casematch(name, a->a_name))
break;
}
if (a) {
*list = a->a_next;
a->a_next = NULL;
}
return a;
}
/* Return 1 if m= line struct matches with given type and name */
unsigned sdp_media_match(sdp_media_t const *m,
sdp_media_e type,
sdp_text_t *type_name,
sdp_proto_e proto,
sdp_text_t *proto_name)
{
if (m == NULL)
return 0;
if (type == sdp_media_any || m->m_type == sdp_media_any)
return 1;
if (type_name == NULL)
type_name = "";
if (type != m->m_type ||
(type == sdp_media_x && !su_casematch(m->m_type_name, type_name)))
return 0;
if (proto == sdp_proto_any || m->m_proto == sdp_proto_any)
return 1;
if (proto_name == NULL)
proto_name = "";
if (proto != m->m_proto ||
(proto == sdp_proto_x && !su_casematch(m->m_proto_name, proto_name)))
return 0;
return 1;
}
/* Return 1 if media type and protocol of m= line structs matches */
unsigned sdp_media_match_with(sdp_media_t const *a,
sdp_media_t const *b)
{
if (a == NULL || b == NULL)
return a == b;
if (a->m_type == sdp_media_any || b->m_type == sdp_media_any)
return 1;
if (a->m_type != b->m_type ||
(a->m_type == sdp_media_x
&& !su_casematch(b->m_type_name, a->m_type_name)))
return 0;
if (a->m_proto == sdp_proto_any || b->m_proto == sdp_proto_any)
return 1;
if (a->m_proto != b->m_proto ||
(a->m_proto == sdp_proto_x
&& !su_casematch(b->m_proto_name, a->m_proto_name)))
return 0;
return 1;
}
/** Count matching media lines in SDP. */
unsigned sdp_media_count(sdp_session_t const *sdp,
sdp_media_e type,
sdp_text_t *type_name,
sdp_proto_e proto,
sdp_text_t *proto_name)
{
unsigned count = 0;
sdp_media_t const *m;
if (sdp != NULL)
for (m = sdp->sdp_media; m; m = m->m_next)
count += sdp_media_match(m, type, type_name, proto, proto_name);
return count;
}
/** Count matching media lines in SDP. */
unsigned sdp_media_count_with(sdp_session_t const *sdp,
sdp_media_t const *m0)
{
unsigned count = 0;
sdp_media_t const *m;
if (sdp != NULL)
for (m = sdp->sdp_media; m; m = m->m_next)
count += sdp_media_match_with(m, m0);
return count;
}
/** Return true if media uses RTP */
int sdp_media_uses_rtp(sdp_media_t const *m)
{
return m &&
(m->m_proto == sdp_proto_rtp ||
m->m_proto == sdp_proto_srtp || m->m_proto == sdp_proto_extended_srtp ||
(m->m_proto == sdp_proto_x && m->m_proto_name &&
su_casenmatch(m->m_proto_name, "RTP/", 4)));
}
/** Check if payload type, rtp rate and parameters match in rtpmaps*/
int sdp_rtpmap_match(sdp_rtpmap_t const *a, sdp_rtpmap_t const *b)
{
char const *aparam, *bparam;
if (a == b)
return 1;
if (a == 0 || b == 0)
return 0;
if (a->rm_rate != b->rm_rate)
return 0;
if (!su_casematch(a->rm_encoding, b->rm_encoding))
return 0;
aparam = a->rm_params; bparam = b->rm_params;
if (aparam == bparam)
return 1;
if (!aparam) aparam = "1"; if (!bparam) bparam = "1";
if (!su_casematch(aparam, bparam))
return 0;
return 1;
}
/** Search for matching rtpmap from list.
*
* @note
* The a=fmtp: for the codecs are not compared.
*/
sdp_rtpmap_t *sdp_rtpmap_find_matching(sdp_rtpmap_t const *list,
sdp_rtpmap_t const *rm)
{
char const *lparam, *rparam;
sdp_rtpmap_t const *cp_list = NULL;
if (rm == NULL)
return NULL;
for (; list; list = list->rm_next) {
if (rm->rm_rate != list->rm_rate)
continue;
if (!su_casematch(rm->rm_encoding, list->rm_encoding))
continue;
lparam = rm->rm_params; rparam = list->rm_params;
if (lparam == rparam) {
cp_list = list;
if (rm->rm_pt != list->rm_pt) continue;
break;
}
if (!lparam) lparam = "1"; if (!rparam) rparam = "1";
if (!su_casematch(lparam, rparam))
continue;
break;
}
return cp_list ? (sdp_rtpmap_t *) cp_list : (sdp_rtpmap_t *)list;
}