/*
  The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-)
  Copyright (C) 2001,2002,2003  Aymeric MOIZARD jack@atosc.org
  
  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include <osipparser2/osip_const.h>
#include <osipparser2/sdp_message.h>
#include <osipparser2/osip_port.h>

#include <stdio.h>
#include <stdlib.h>

#define ERR_ERROR   -1		/* bad header */
#define ERR_DISCARD  0		/* wrong header */
#define WF           1		/* well formed header */

static int sdp_message_parse_v (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_o (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_s (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_i (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_u (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_e (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_p (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_c (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_b (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_t (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_r (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_z (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_k (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_a (sdp_message_t * sdp, char *buf, char **next);
static int sdp_message_parse_m (sdp_message_t * sdp, char *buf, char **next);

static int sdp_append_media (char *string, int size, char *tmp,
			     sdp_media_t * media, char **next_tmp);
static int sdp_append_attribute (char *string, int size, char *tmp,
				 sdp_attribute_t * attribute,
				 char **next_tmp);
static int sdp_append_key (char *string, int size, char *tmp, sdp_key_t * key,
			   char **next_tmp);
static int sdp_append_time_descr (char *string, int size, char *tmp,
				  sdp_time_descr_t * time_descr,
				  char **next_tmp);
static int sdp_append_bandwidth (char *string, int size, char *tmp,
				 sdp_bandwidth_t * bandwidth,
				 char **next_tmp);
static int sdp_append_connection (char *string, int size, char *tmp,
				  sdp_connection_t * conn, char **next_tmp);

int
sdp_bandwidth_init (sdp_bandwidth_t ** b)
{
  *b = (sdp_bandwidth_t *) osip_malloc (sizeof (sdp_bandwidth_t));
  if (*b == NULL)
    return -1;
  (*b)->b_bwtype = NULL;
  (*b)->b_bandwidth = NULL;
  return 0;
}

void
sdp_bandwidth_free (sdp_bandwidth_t * b)
{
  if (b == NULL)
    return;
  osip_free (b->b_bwtype);
  osip_free (b->b_bandwidth);
  osip_free (b);
}

int
sdp_time_descr_init (sdp_time_descr_t ** td)
{
  *td = (sdp_time_descr_t *) osip_malloc (sizeof (sdp_time_descr_t));
  if (*td == NULL)
    return -1;
  (*td)->t_start_time = NULL;
  (*td)->t_stop_time = NULL;
  (*td)->r_repeats = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init ((*td)->r_repeats);
  return 0;
}

void
sdp_time_descr_free (sdp_time_descr_t * td)
{
  if (td == NULL)
    return;
  osip_free (td->t_start_time);
  osip_free (td->t_stop_time);
  osip_list_ofchar_free (td->r_repeats);
  osip_free (td);
}

int
sdp_key_init (sdp_key_t ** key)
{
  *key = (sdp_key_t *) osip_malloc (sizeof (sdp_key_t));
  if (*key == NULL)
    return -1;
  (*key)->k_keytype = NULL;
  (*key)->k_keydata = NULL;
  return 0;
}

void
sdp_key_free (sdp_key_t * key)
{
  if (key == NULL)
    return;
  osip_free (key->k_keytype);
  osip_free (key->k_keydata);
  osip_free (key);
}

int
sdp_attribute_init (sdp_attribute_t ** attribute)
{
  *attribute = (sdp_attribute_t *) osip_malloc (sizeof (sdp_attribute_t));
  if (*attribute == NULL)
    return -1;
  (*attribute)->a_att_field = NULL;
  (*attribute)->a_att_value = NULL;
  return 0;
}

void
sdp_attribute_free (sdp_attribute_t * attribute)
{
  if (attribute == NULL)
    return;
  osip_free (attribute->a_att_field);
  osip_free (attribute->a_att_value);
  osip_free (attribute);
}

int
sdp_connection_init (sdp_connection_t ** connection)
{
  *connection = (sdp_connection_t *) osip_malloc (sizeof (sdp_connection_t));
  if (*connection == NULL)
    return -1;
  (*connection)->c_nettype = NULL;
  (*connection)->c_addrtype = NULL;
  (*connection)->c_addr = NULL;
  (*connection)->c_addr_multicast_ttl = NULL;
  (*connection)->c_addr_multicast_int = NULL;
  return 0;
}

void
sdp_connection_free (sdp_connection_t * connection)
{
  if (connection == NULL)
    return;
  osip_free (connection->c_nettype);
  osip_free (connection->c_addrtype);
  osip_free (connection->c_addr);
  osip_free (connection->c_addr_multicast_ttl);
  osip_free (connection->c_addr_multicast_int);
  osip_free (connection);
}

int
sdp_media_init (sdp_media_t ** media)
{
  *media = (sdp_media_t *) osip_malloc (sizeof (sdp_media_t));
  if (*media == NULL)
    return -1;
  (*media)->m_media = NULL;
  (*media)->m_port = NULL;
  (*media)->m_number_of_port = NULL;
  (*media)->m_proto = NULL;
  (*media)->m_payloads = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init ((*media)->m_payloads);
  (*media)->i_info = NULL;
  (*media)->c_connections =
    (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init ((*media)->c_connections);
  (*media)->b_bandwidths = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init ((*media)->b_bandwidths);
  (*media)->a_attributes = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init ((*media)->a_attributes);
  (*media)->k_key = NULL;
  return 0;
}

void
sdp_media_free (sdp_media_t * media)
{
  if (media == NULL)
    return;
  osip_free (media->m_media);
  osip_free (media->m_port);
  osip_free (media->m_number_of_port);
  osip_free (media->m_proto);
  osip_list_ofchar_free (media->m_payloads);
  osip_free (media->i_info);
  osip_list_special_free (media->c_connections,
			  (void *(*)(void *)) &sdp_connection_free);
  osip_list_special_free (media->b_bandwidths,
			  (void *(*)(void *)) &sdp_bandwidth_free);
  osip_list_special_free (media->a_attributes,
			  (void *(*)(void *)) &sdp_attribute_free);
  sdp_key_free (media->k_key);
  osip_free (media);
}

/* to be changed to sdp_message_init(sdp_message_t **dest) */
int
sdp_message_init (sdp_message_t ** sdp)
{
  (*sdp) = (sdp_message_t *) osip_malloc (sizeof (sdp_message_t));
  if (*sdp == NULL)
    return -1;

  (*sdp)->v_version = NULL;
  (*sdp)->o_username = NULL;
  (*sdp)->o_sess_id = NULL;
  (*sdp)->o_sess_version = NULL;
  (*sdp)->o_nettype = NULL;
  (*sdp)->o_addrtype = NULL;
  (*sdp)->o_addr = NULL;
  (*sdp)->s_name = NULL;
  (*sdp)->i_info = NULL;
  (*sdp)->u_uri = NULL;

  (*sdp)->e_emails = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  if ((*sdp)->e_emails == NULL)
    return -1;
  osip_list_init ((*sdp)->e_emails);

  (*sdp)->p_phones = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  if ((*sdp)->p_phones == NULL)
    return -1;
  osip_list_init ((*sdp)->p_phones);

  (*sdp)->c_connection = NULL;

  (*sdp)->b_bandwidths = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  if ((*sdp)->b_bandwidths == NULL)
    return -1;
  osip_list_init ((*sdp)->b_bandwidths);

  (*sdp)->t_descrs = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  if ((*sdp)->t_descrs == NULL)
    return -1;
  osip_list_init ((*sdp)->t_descrs);

  (*sdp)->z_adjustments = NULL;
  (*sdp)->k_key = NULL;

  (*sdp)->a_attributes = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  if ((*sdp)->a_attributes == NULL)
    return -1;
  osip_list_init ((*sdp)->a_attributes);

  (*sdp)->m_medias = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  if ((*sdp)->m_medias == NULL)
    return -1;
  osip_list_init ((*sdp)->m_medias);
  return 0;
}

static int
sdp_message_parse_v (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  if (equal == buf)
    return ERR_DISCARD;
  
  /* check if header is "v" */
  if (equal[-1] != 'v')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/*v=\r ?? bad header */
  sdp->v_version = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (sdp->v_version, equal + 1, crlf - (equal + 1));

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_o (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *tmp;
  char *tmp_next;
  int i;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "o" */
  if (equal[-1] != 'o')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* o=\r ?? bad header */

  tmp = equal + 1;
  /* o=username sess-id sess-version nettype addrtype addr */

  /* useranme can contain any char (ascii) except "space" and CRLF */
  i = __osip_set_next_token (&(sdp->o_username), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  /* sess_id contains only numeric characters */
  i = __osip_set_next_token (&(sdp->o_sess_id), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  /* sess_id contains only numeric characters */
  i = __osip_set_next_token (&(sdp->o_sess_version), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  /* nettype is "IN" but will surely be extented!!! assume it's some alpha-char */
  i = __osip_set_next_token (&(sdp->o_nettype), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  /* addrtype  is "IP4" or "IP6" but will surely be extented!!! */
  i = __osip_set_next_token (&(sdp->o_addrtype), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  /* addr  is "IP4" or "IP6" but will surely be extented!!! */
  i = __osip_set_next_token (&(sdp->o_addr), tmp, '\r', &tmp_next);
  if (i != 0)
    {				/* could it be "\n" only??? rfc says to accept CR or LF instead of CRLF */
      i = __osip_set_next_token (&(sdp->o_addr), tmp, '\n', &tmp_next);
      if (i != 0)
	return -1;
    }

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_s (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "s" */
  if (equal[-1] != 's')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* o=\r ?? bad header */

  /* s=text */

  /* text is interpreted as ISO-10646 UTF8! */
  /* using ISO 8859-1 requires "a=charset:ISO-8859-1 */
  sdp->s_name = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (sdp->s_name, equal + 1, crlf - (equal + 1));

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_i (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  int i;
  char *i_info;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "i" */
  if (equal[-1] != 'i')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* o=\r ?? bad header */

  /* s=text */

  /* text is interpreted as ISO-10646 UTF8! */
  /* using ISO 8859-1 requires "a=charset:ISO-8859-1 */
  i_info = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (i_info, equal + 1, crlf - (equal + 1));

  /* add the bandwidth at the correct place:
     if there is no media line yet, then the "b=" is the
     global one.
   */
  i = osip_list_size (sdp->m_medias);
  if (i == 0)
    sdp->i_info = i_info;
  else
    {
      sdp_media_t *last_sdp_media =
	(sdp_media_t *) osip_list_get (sdp->m_medias, i - 1);
      last_sdp_media->i_info = i_info;
    }

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_u (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "u" */
  if (equal[-1] != 'u')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* u=\r ?? bad header */

  /* u=uri */
  /* we assume this is a URI */
  sdp->u_uri = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (sdp->u_uri, equal + 1, crlf - (equal + 1));

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_e (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *e_email;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "e" */
  if (equal[-1] != 'e')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* e=\r ?? bad header */

  /* e=email */
  /* we assume this is an EMAIL-ADDRESS */
  e_email = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (e_email, equal + 1, crlf - (equal + 1));

  osip_list_add (sdp->e_emails, e_email, -1);

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_p (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *p_phone;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "p" */
  if (equal[-1] != 'p')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* p=\r ?? bad header */

  /* e=email */
  /* we assume this is an EMAIL-ADDRESS */
  p_phone = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (p_phone, equal + 1, crlf - (equal + 1));

  osip_list_add (sdp->p_phones, p_phone, -1);

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_c (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *tmp;
  char *tmp_next;
  sdp_connection_t *c_header;
  int i;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "c" */
  if (equal[-1] != 'c')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* c=\r ?? bad header */

  tmp = equal + 1;
  i = sdp_connection_init (&c_header);
  if (i != 0)
    return ERR_ERROR;
  /* c=nettype addrtype (multicastaddr | addr) */

  /* nettype is "IN" and will be extended */
  i = __osip_set_next_token (&(c_header->c_nettype), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  /* nettype is "IP4" or "IP6" and will be extended */
  i = __osip_set_next_token (&(c_header->c_addrtype), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  /* there we have a multicast or unicast address */
  /* multicast can be ip/ttl [/integer] */
  /* unicast is FQDN or ip (no ttl, no integer) */

  /* is MULTICAST? */
  {
    char *slash = strchr (tmp, '/');

    if (slash != NULL && slash < crlf)	/* it's a multicast address! */
      {
	i = __osip_set_next_token (&(c_header->c_addr), tmp, '/', &tmp_next);
	if (i != 0)
	  return -1;
	tmp = tmp_next;
	slash = strchr (slash + 1, '/');
	if (slash != NULL && slash < crlf)	/* optionnal integer is there! */
	  {
	    i =
	      __osip_set_next_token (&(c_header->c_addr_multicast_ttl), tmp,
				     '/', &tmp_next);
	    if (i != 0)
	      return -1;
	    tmp = tmp_next;
	    i =
	      __osip_set_next_token (&(c_header->c_addr_multicast_int), tmp,
				     '\r', &tmp_next);
	    if (i != 0)
	      {
		i =
		  __osip_set_next_token (&(c_header->c_addr_multicast_int),
					 tmp, '\n', &tmp_next);
		if (i != 0)
		  {
		    sdp_connection_free (c_header);
		    return -1;
		  }
	      }
	  }
	else
	  {
	    i =
	      __osip_set_next_token (&(c_header->c_addr_multicast_ttl), tmp,
				     '\r', &tmp_next);
	    if (i != 0)
	      {
		i =
		  __osip_set_next_token (&(c_header->c_addr_multicast_ttl),
					 tmp, '\n', &tmp_next);
		if (i != 0)
		  {
		    sdp_connection_free (c_header);
		    return -1;
		  }
	      }
	  }
      }
    else
      {
	/* in this case, we have a unicast address */
	i = __osip_set_next_token (&(c_header->c_addr), tmp, '\r', &tmp_next);
	if (i != 0)
	  {
	    i =
	      __osip_set_next_token (&(c_header->c_addr), tmp, '\n',
				     &tmp_next);
	    if (i != 0)
	      {
		sdp_connection_free (c_header);
		return -1;
	      }
	  }
      }
  }

  /* add the connection at the correct place:
     if there is no media line yet, then the "c=" is the
     global one.
   */
  i = osip_list_size (sdp->m_medias);
  if (i == 0)
    sdp->c_connection = c_header;
  else
    {
      sdp_media_t *last_sdp_media =
	(sdp_media_t *) osip_list_get (sdp->m_medias, i - 1);
      osip_list_add (last_sdp_media->c_connections, c_header, -1);
    }
  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_b (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *tmp;
  char *tmp_next;
  int i;
  sdp_bandwidth_t *b_header;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "b" */
  if (equal[-1] != 'b')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* b=\r ?? bad header */

  tmp = equal + 1;
  /* b = bwtype: bandwidth */
  i = sdp_bandwidth_init (&b_header);
  if (i != 0)
    return ERR_ERROR;

  /* bwtype is alpha-numeric */
  i = __osip_set_next_token (&(b_header->b_bwtype), tmp, ':', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;

  i = __osip_set_next_token (&(b_header->b_bandwidth), tmp, '\r', &tmp_next);
  if (i != 0)
    {
      i =
	__osip_set_next_token (&(b_header->b_bandwidth), tmp, '\n',
			       &tmp_next);
      if (i != 0)
	{
	  sdp_bandwidth_free (b_header);
	  return -1;
	}
    }

  /* add the bandwidth at the correct place:
     if there is no media line yet, then the "b=" is the
     global one.
   */
  i = osip_list_size (sdp->m_medias);
  if (i == 0)
    osip_list_add (sdp->b_bandwidths, b_header, -1);
  else
    {
      sdp_media_t *last_sdp_media =
	(sdp_media_t *) osip_list_get (sdp->m_medias, i - 1);
      osip_list_add (last_sdp_media->b_bandwidths, b_header, -1);
    }

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_t (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *tmp;
  char *tmp_next;
  int i;
  sdp_time_descr_t *t_header;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "t" */
  if (equal[-1] != 't')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* t=\r ?? bad header */

  tmp = equal + 1;
  /* t = start_time stop_time */
  i = sdp_time_descr_init (&t_header);
  if (i != 0)
    return ERR_ERROR;

  i = __osip_set_next_token (&(t_header->t_start_time), tmp, ' ', &tmp_next);
  if (i != 0)
    {
      sdp_time_descr_free (t_header);
      return -1;
    }
  tmp = tmp_next;

  i = __osip_set_next_token (&(t_header->t_stop_time), tmp, '\r', &tmp_next);
  if (i != 0)
    {
      i =
	__osip_set_next_token (&(t_header->t_stop_time), tmp, '\n',
			       &tmp_next);
      if (i != 0)
	{
	  sdp_time_descr_free (t_header);
	  return -1;
	}
    }

  /* add the new time_description header */
  osip_list_add (sdp->t_descrs, t_header, -1);

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_r (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  int index;
  char *r_header;
  sdp_time_descr_t *t_descr;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "r" */
  if (equal[-1] != 'r')
    return ERR_DISCARD;

  index = osip_list_size (sdp->t_descrs);
  if (index == 0)
    return ERR_ERROR;		/* r field can't come alone! */

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* r=\r ?? bad header */

  /* r=far too complexe and somewhat useless... I don't parse it! */
  r_header = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (r_header, equal + 1, crlf - (equal + 1));

  /* r field carry information for the last "t" field */
  t_descr = (sdp_time_descr_t *) osip_list_get (sdp->t_descrs, index - 1);
  osip_list_add (t_descr->r_repeats, r_header, -1);

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_z (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *z_header;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "z" */
  if (equal[-1] != 'z')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* z=\r ?? bad header */

  /* z=somewhat useless... I don't parse it! */
  z_header = osip_malloc (crlf - (equal + 1) + 1);
  osip_strncpy (z_header, equal + 1, crlf - (equal + 1));

  sdp->z_adjustments = z_header;

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_k (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  int i;
  char *colon;
  sdp_key_t *k_header;
  char *tmp;
  char *tmp_next;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "k" */
  if (equal[-1] != 'k')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* k=\r ?? bad header */

  tmp = equal + 1;

  i = sdp_key_init (&k_header);
  if (i != 0)
    return ERR_ERROR;
  /* k=key-type[:key-data] */

  /* is there any key-data? */
  colon = strchr (equal + 1, ':');
  if ((colon != NULL) && (colon < crlf))
    {
      /* att-field is alpha-numeric */
      i = __osip_set_next_token (&(k_header->k_keytype), tmp, ':', &tmp_next);
      if (i != 0)
	{
	  sdp_key_free (k_header);
	  return -1;
	}
      tmp = tmp_next;

      i =
	__osip_set_next_token (&(k_header->k_keydata), tmp, '\r', &tmp_next);
      if (i != 0)
	{
	  i =
	    __osip_set_next_token (&(k_header->k_keydata), tmp, '\n',
				   &tmp_next);
	  if (i != 0)
	    {
	      sdp_key_free (k_header);
	      return -1;
	    }
	}
    }
  else
    {
      i =
	__osip_set_next_token (&(k_header->k_keytype), tmp, '\r', &tmp_next);
      if (i != 0)
	{
	  i =
	    __osip_set_next_token (&(k_header->k_keytype), tmp, '\n',
				   &tmp_next);
	  if (i != 0)
	    {
	      sdp_key_free (k_header);
	      return -1;
	    }
	}
    }

  /* add the key at the correct place:
     if there is no media line yet, then the "k=" is the
     global one.
   */
  i = osip_list_size (sdp->m_medias);
  if (i == 0)
    sdp->k_key = k_header;
  else
    {
      sdp_media_t *last_sdp_media =
	(sdp_media_t *) osip_list_get (sdp->m_medias, i - 1);
      last_sdp_media->k_key = k_header;
    }

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_a (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *tmp;
  char *tmp_next;
  int i;
  sdp_attribute_t *a_attribute;
  char *colon;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "a" */
  if (equal[-1] != 'a')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* a=\r ?? bad header */

  tmp = equal + 1;

  i = sdp_attribute_init (&a_attribute);
  if (i != 0)
    return ERR_ERROR;

  /* a=att-field[:att-value] */

  /* is there any att-value? */
  colon = strchr (equal + 1, ':');
  if ((colon != NULL) && (colon < crlf))
    {
      /* att-field is alpha-numeric */
      i =
	__osip_set_next_token (&(a_attribute->a_att_field), tmp, ':',
			       &tmp_next);
      if (i != 0)
	{
	  sdp_attribute_free (a_attribute);
	  return -1;
	}
      tmp = tmp_next;

      i =
	__osip_set_next_token (&(a_attribute->a_att_value), tmp, '\r',
			       &tmp_next);
      if (i != 0)
	{
	  i =
	    __osip_set_next_token (&(a_attribute->a_att_value), tmp, '\n',
				   &tmp_next);
	  if (i != 0)
	    {
	      sdp_attribute_free (a_attribute);
	      return -1;
	    }
	}
    }
  else
    {
      i =
	__osip_set_next_token (&(a_attribute->a_att_field), tmp, '\r',
			       &tmp_next);
      if (i != 0)
	{
	  i =
	    __osip_set_next_token (&(a_attribute->a_att_field), tmp, '\n',
				   &tmp_next);
	  if (i != 0)
	    {
	      sdp_attribute_free (a_attribute);
	      return -1;
	    }
	}
    }

  /* add the attribute at the correct place:
     if there is no media line yet, then the "a=" is the
     global one.
   */
  i = osip_list_size (sdp->m_medias);
  if (i == 0)
    osip_list_add (sdp->a_attributes, a_attribute, -1);
  else
    {
      sdp_media_t *last_sdp_media =
	(sdp_media_t *) osip_list_get (sdp->m_medias, i - 1);
      osip_list_add (last_sdp_media->a_attributes, a_attribute, -1);
    }

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}

static int
sdp_message_parse_m (sdp_message_t * sdp, char *buf, char **next)
{
  char *equal;
  char *crlf;
  char *tmp;
  char *tmp_next;
  int i;
  sdp_media_t *m_header;
  char *slash;
  char *space;

  *next = buf;

  equal = buf;
  while ((*equal != '=') && (*equal != '\0'))
    equal++;
  if (*equal == '\0')
    return ERR_ERROR;

  /* check if header is "m" */
  if (equal[-1] != 'm')
    return ERR_DISCARD;

  crlf = equal + 1;

  while ((*crlf != '\r') && (*crlf != '\n') && (*crlf != '\0'))
    crlf++;
  if (*crlf == '\0')
    return ERR_ERROR;
  if (crlf == equal + 1)
    return ERR_ERROR;		/* a=\r ?? bad header */

  tmp = equal + 1;

  i = sdp_media_init (&m_header);
  if (i != 0)
    return ERR_ERROR;

  /* m=media port ["/"integer] proto *(payload_number) */

  /* media is "audio" "video" "application" "data" or other... */
  i = __osip_set_next_token (&(m_header->m_media), tmp, ' ', &tmp_next);
  if (i != 0)
    {
      sdp_media_free (m_header);
      return -1;
    }
  tmp = tmp_next;

  slash = strchr (tmp, '/');
  space = strchr (tmp, ' ');
  if (space == NULL)		/* not possible! */
    {
      sdp_media_free (m_header);
      return ERR_ERROR;
    }
  if ((slash != NULL) && (slash < space))
    {				/* a number of port is specified! */
      i = __osip_set_next_token (&(m_header->m_port), tmp, '/', &tmp_next);
      if (i != 0)
	{
	  sdp_media_free (m_header);
	  return -1;
	}
      tmp = tmp_next;

      i =
	__osip_set_next_token (&(m_header->m_number_of_port), tmp, ' ',
			       &tmp_next);
      if (i != 0)
	{
	  sdp_media_free (m_header);
	  return -1;
	}
      tmp = tmp_next;
    }
  else
    {
      i = __osip_set_next_token (&(m_header->m_port), tmp, ' ', &tmp_next);
      if (i != 0)
	{
	  sdp_media_free (m_header);
	  return -1;
	}
      tmp = tmp_next;
    }

  i = __osip_set_next_token (&(m_header->m_proto), tmp, ' ', &tmp_next);
  if (i != 0)
    {
      sdp_media_free (m_header);
      return -1;
    }
  tmp = tmp_next;

  {
    char *str;
    int more_space_before_crlf;

    space = strchr (tmp + 1, ' ');
    if (space == NULL)
      more_space_before_crlf = 1;
    else if ((space != NULL) && (space > crlf))
      more_space_before_crlf = 1;
    else
      more_space_before_crlf = 0;
    while (more_space_before_crlf == 0)
      {
	i = __osip_set_next_token (&str, tmp, ' ', &tmp_next);
	if (i != 0)
	  {
	    sdp_media_free (m_header);
	    return -1;
	  }
	tmp = tmp_next;
	osip_list_add (m_header->m_payloads, str, -1);

	space = strchr (tmp + 1, ' ');
	if (space == NULL)
	  more_space_before_crlf = 1;
	else if ((space != NULL) && (space > crlf))
	  more_space_before_crlf = 1;
	else
	  more_space_before_crlf = 0;
      }
    if (tmp_next < crlf)
      {				/* tmp_next is still less than clrf: no space */
	i = __osip_set_next_token (&str, tmp, '\r', &tmp_next);
	if (i != 0)
	  {
	    i = __osip_set_next_token (&str, tmp, '\n', &tmp_next);
	    if (i != 0)
	      {
		sdp_media_free (m_header);
		return -1;
	      }
	  }
	osip_list_add (m_header->m_payloads, str, -1);
      }
  }

  osip_list_add (sdp->m_medias, m_header, -1);

  if (crlf[1] == '\n')
    *next = crlf + 2;
  else
    *next = crlf + 1;
  return WF;
}


int
sdp_message_parse (sdp_message_t * sdp, const char *buf)
{

  /* In SDP, headers must be in the right order */
  /* This is a simple example
     v=0
     o=user1 53655765 2353687637 IN IP4 128.3.4.5
     s=Mbone Audio
     i=Discussion of Mbone Engineering Issues
     e=mbone@somewhere.com
     c=IN IP4 224.2.0.1/127
     t=0 0
     m=audio 3456 RTP/AVP 0
     a=rtpmap:0 PCMU/8000
   */

  char *next_buf;
  char *ptr;
  int i;

  ptr = (char *) buf;
  /* mandatory */
  i = sdp_message_parse_v (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  else if (0 == i)		/* header is not "v" */
    return -1;
  ptr = next_buf;

  /* adtech phone use the wrong ordering and place "s" before "o" */
  i = sdp_message_parse_s (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  /* else if (0==i) header is not "s" */
  /* else ADTECH PHONE DETECTED */

  ptr = next_buf;



  i = sdp_message_parse_o (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  else if (0 == i)		/* header is not "o" */
    return -1;
  ptr = next_buf;

  i = sdp_message_parse_s (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  else if (0 == i)		/* header is not "s" */
    /* return -1; */
    {
      OSIP_TRACE (osip_trace
		  (__FILE__, __LINE__, OSIP_INFO4, NULL,
		   "The \"s\" parameter is mandatory, but this packet does not contain any! - anyway, we don't mind about it.\n"));
    }
  ptr = next_buf;

  i = sdp_message_parse_i (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  ptr = next_buf;

  i = sdp_message_parse_u (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  ptr = next_buf;

  i = 1;
  while (i == 1)
    {
      i = sdp_message_parse_e (sdp, ptr, &next_buf);
      if (i == -1)		/* header is bad */
	return -1;
      ptr = next_buf;
    }
  i = 1;
  while (i == 1)
    {
      i = sdp_message_parse_p (sdp, ptr, &next_buf);
      if (i == -1)		/* header is bad */
	return -1;
      ptr = next_buf;
    }

  /* rfc2327: there should be at least of email or phone number! */
  if (osip_list_size (sdp->e_emails) == 0
      && osip_list_size (sdp->p_phones) == 0)
    {
      OSIP_TRACE (osip_trace
		  (__FILE__, __LINE__, OSIP_INFO4, NULL,
		   "The rfc2327 says there should be at least an email or a phone header!- anyway, we don't mind about it.\n"));
    }

  i = sdp_message_parse_c (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  ptr = next_buf;

  i = 1;
  while (i == 1)
    {
      i = sdp_message_parse_b (sdp, ptr, &next_buf);
      if (i == -1)		/* header is bad */
	return -1;
      ptr = next_buf;
    }

  /* 1 or more "t" header + 0 or more "r" header for each "t" header */
  i = sdp_message_parse_t (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  else if (i == ERR_DISCARD)
    return -1;			/* t is mandatory */
  ptr = next_buf;

  if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
    return 0;

  i = 1;
  while (i == 1)		/* is a "r" header */
    {
      i = sdp_message_parse_r (sdp, ptr, &next_buf);
      if (i == -1)		/* header is bad */
	return -1;
      ptr = next_buf;
      if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	return 0;

    }


  {
    int more_t_header = 1;

    i = sdp_message_parse_t (sdp, ptr, &next_buf);
    if (i == -1)		/* header is bad */
      return -1;
    ptr = next_buf;

    if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
      return 0;

    while (more_t_header == 1)
      {
	i = 1;
	while (i == 1)		/* is a "r" header */
	  {
	    i = sdp_message_parse_r (sdp, ptr, &next_buf);
	    if (i == -1)	/* header is bad */
	      return -1;
	    ptr = next_buf;
	    if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	      return 0;
	  }

	i = sdp_message_parse_t (sdp, ptr, &next_buf);
	if (i == -1)		/* header is bad */
	  return -1;
	else if (i == ERR_DISCARD)
	  more_t_header = 0;
	else
	  more_t_header = 1;	/* no more "t" headers */
	ptr = next_buf;
	if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	  return 0;
      }
  }

  i = sdp_message_parse_z (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  ptr = next_buf;
  if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
    return 0;

  i = sdp_message_parse_k (sdp, ptr, &next_buf);
  if (i == -1)			/* header is bad */
    return -1;
  ptr = next_buf;
  if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
    return 0;

  /* 0 or more "a" header */
  i = 1;
  while (i == 1)		/* no more "a" header */
    {
      i = sdp_message_parse_a (sdp, ptr, &next_buf);
      if (i == -1)		/* header is bad */
	return -1;
      ptr = next_buf;
      if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	return 0;
    }
  /* 0 or more media headers */
  {
    int more_m_header = 1;

    while (more_m_header == 1)
      {
	more_m_header = sdp_message_parse_m (sdp, ptr, &next_buf);
	if (more_m_header == -1)	/* header is bad */
	  return -1;
	ptr = next_buf;
	if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	  return 0;

	i = sdp_message_parse_i (sdp, ptr, &next_buf);
	if (i == -1)		/* header is bad */
	  return -1;
	ptr = next_buf;
	if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	  return 0;

	i = 1;
	while (i == 1)
	  {
	    i = sdp_message_parse_c (sdp, ptr, &next_buf);
	    if (i == -1)	/* header is bad */
	      return -1;
	    ptr = next_buf;
	    if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	      return 0;
	  }

	i = 1;
	while (i == 1)
	  {
	    i = sdp_message_parse_b (sdp, ptr, &next_buf);
	    if (i == -1)	/* header is bad */
	      return -1;
	    ptr = next_buf;
	    if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	      return 0;
	  }
	i = sdp_message_parse_k (sdp, ptr, &next_buf);
	if (i == -1)		/* header is bad */
	  return -1;
	ptr = next_buf;
	if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	  return 0;
	/* 0 or more a headers */
	i = 1;
	while (i == 1)
	  {
	    i = sdp_message_parse_a (sdp, ptr, &next_buf);
	    if (i == -1)	/* header is bad */
	      return -1;
	    ptr = next_buf;
	    if (*ptr == '\0' || (*ptr == '\r') || (*ptr == '\n'))
	      return 0;
	  }
      }
  }

  return 0;
}

static int
sdp_append_connection (char *string, int size, char *tmp,
		       sdp_connection_t * conn, char **next_tmp)
{
  if (conn->c_nettype == NULL)
    return -1;
  if (conn->c_addrtype == NULL)
    return -1;
  if (conn->c_addr == NULL)
    return -1;

  tmp = __osip_sdp_append_string (string, size, tmp, "c=");
  tmp = __osip_sdp_append_string (string, size, tmp, conn->c_nettype);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, conn->c_addrtype);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, conn->c_addr);
  if (conn->c_addr_multicast_ttl != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "/");
      tmp =
	__osip_sdp_append_string (string, size, tmp,
				  conn->c_addr_multicast_ttl);
    }
  if (conn->c_addr_multicast_int != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "/");
      tmp =
	__osip_sdp_append_string (string, size, tmp,
				  conn->c_addr_multicast_int);
    }
  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
  *next_tmp = tmp;
  return 0;
}

static int
sdp_append_bandwidth (char *string, int size, char *tmp,
		      sdp_bandwidth_t * bandwidth, char **next_tmp)
{
  if (bandwidth->b_bwtype == NULL)
    return -1;
  if (bandwidth->b_bandwidth == NULL)
    return -1;

  tmp = __osip_sdp_append_string (string, size, tmp, "b=");
  tmp = __osip_sdp_append_string (string, size, tmp, bandwidth->b_bwtype);
  tmp = __osip_sdp_append_string (string, size, tmp, ":");
  tmp = __osip_sdp_append_string (string, size, tmp, bandwidth->b_bandwidth);
  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);

  *next_tmp = tmp;
  return 0;
}

static int
sdp_append_time_descr (char *string, int size, char *tmp,
		       sdp_time_descr_t * time_descr, char **next_tmp)
{
  int pos;

  if (time_descr->t_start_time == NULL)
    return -1;
  if (time_descr->t_stop_time == NULL)
    return -1;


  tmp = __osip_sdp_append_string (string, size, tmp, "t=");
  tmp =
    __osip_sdp_append_string (string, size, tmp, time_descr->t_start_time);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, time_descr->t_stop_time);

  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);

  pos = 0;
  while (!osip_list_eol (time_descr->r_repeats, pos))
    {
      char *str = (char *) osip_list_get (time_descr->r_repeats, pos);

      tmp = __osip_sdp_append_string (string, size, tmp, "r=");
      tmp = __osip_sdp_append_string (string, size, tmp, str);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
      pos++;
    }

  *next_tmp = tmp;
  return 0;
}

static int
sdp_append_key (char *string, int size, char *tmp, sdp_key_t * key,
		char **next_tmp)
{
  if (key->k_keytype == NULL)
    return -1;

  tmp = __osip_sdp_append_string (string, size, tmp, "k=");
  tmp = __osip_sdp_append_string (string, size, tmp, key->k_keytype);
  if (key->k_keydata != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, ":");
      tmp = __osip_sdp_append_string (string, size, tmp, key->k_keydata);
    }
  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
  *next_tmp = tmp;
  return 0;
}

static int
sdp_append_attribute (char *string, int size, char *tmp,
		      sdp_attribute_t * attribute, char **next_tmp)
{
  if (attribute->a_att_field == NULL)
    return -1;

  tmp = __osip_sdp_append_string (string, size, tmp, "a=");
  tmp = __osip_sdp_append_string (string, size, tmp, attribute->a_att_field);
  if (attribute->a_att_value != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, ":");
      tmp =
	__osip_sdp_append_string (string, size, tmp, attribute->a_att_value);
    }
  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);

  *next_tmp = tmp;
  return 0;
}

/* internal facility */
static int
sdp_append_media (char *string, int size, char *tmp, sdp_media_t * media,
		  char **next_tmp)
{
  int pos;

  if (media->m_media == NULL)
    return -1;
  if (media->m_port == NULL)
    return -1;
  if (media->m_proto == NULL)
    return -1;

  tmp = __osip_sdp_append_string (string, size, tmp, "m=");
  tmp = __osip_sdp_append_string (string, size, tmp, media->m_media);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, media->m_port);
  if (media->m_number_of_port != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "/");
      tmp =
	__osip_sdp_append_string (string, size, tmp, media->m_number_of_port);
    }
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, media->m_proto);
  pos = 0;
  while (!osip_list_eol (media->m_payloads, pos))
    {
      char *str = (char *) osip_list_get (media->m_payloads, pos);

      tmp = __osip_sdp_append_string (string, size, tmp, " ");
      tmp = __osip_sdp_append_string (string, size, tmp, str);
      pos++;
    }
  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);

  if (media->i_info != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "i=");
      tmp = __osip_sdp_append_string (string, size, tmp, media->i_info);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
    }

  pos = 0;
  while (!osip_list_eol (media->c_connections, pos))
    {
      sdp_connection_t *conn =
	(sdp_connection_t *) osip_list_get (media->c_connections, pos);
      char *next_tmp2;
      int i;

      i = sdp_append_connection (string, size, tmp, conn, &next_tmp2);
      if (i != 0)
	return -1;
      tmp = next_tmp2;
      pos++;
    }

  pos = 0;
  while (!osip_list_eol (media->b_bandwidths, pos))
    {
      sdp_bandwidth_t *band =
	(sdp_bandwidth_t *) osip_list_get (media->b_bandwidths, pos);
      char *next_tmp2;
      int i;

      i = sdp_append_bandwidth (string, size, tmp, band, &next_tmp2);
      if (i != 0)
	return -1;
      tmp = next_tmp2;
      pos++;
    }

  if (media->k_key != NULL)
    {
      char *next_tmp2;
      int i;

      i = sdp_append_key (string, size, tmp, media->k_key, &next_tmp2);
      if (i != 0)
	return -1;
      tmp = next_tmp2;
    }

  pos = 0;
  while (!osip_list_eol (media->a_attributes, pos))
    {
      sdp_attribute_t *attr =
	(sdp_attribute_t *) osip_list_get (media->a_attributes, pos);
      char *next_tmp2;
      int i;

      i = sdp_append_attribute (string, size, tmp, attr, &next_tmp2);
      if (i != 0)
	return -1;
      tmp = next_tmp2;
      pos++;
    }

  *next_tmp = tmp;
  return 0;
}

int
sdp_message_to_str (sdp_message_t * sdp, char **dest)
{
  int size;
  int pos;
  char *tmp;
  char *string;

  *dest = NULL;
  if (sdp->v_version == NULL)
    return -1;
  if (sdp->o_username == NULL ||
      sdp->o_sess_id == NULL ||
      sdp->o_sess_version == NULL ||
      sdp->o_nettype == NULL || sdp->o_addrtype == NULL
      || sdp->o_addr == NULL)
    return -1;

  /* RFC says "s=" is mandatory... rfc2543 (SIP) recommends to
     accept SDP datas without s_name... as some buggy implementations
     often forget it...
   */
  /* if (sdp->s_name == NULL)
     return -1; */

  size = 4000;
  tmp = (char *) osip_malloc (size);
  string = tmp;

  tmp = __osip_sdp_append_string (string, size, tmp, "v=");
  tmp = __osip_sdp_append_string (string, size, tmp, sdp->v_version);
  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
  tmp = __osip_sdp_append_string (string, size, tmp, "o=");
  tmp = __osip_sdp_append_string (string, size, tmp, sdp->o_username);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, sdp->o_sess_id);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, sdp->o_sess_version);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, sdp->o_nettype);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, sdp->o_addrtype);
  tmp = __osip_sdp_append_string (string, size, tmp, " ");
  tmp = __osip_sdp_append_string (string, size, tmp, sdp->o_addr);
  tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
  if (sdp->s_name != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "s=");
      tmp = __osip_sdp_append_string (string, size, tmp, sdp->s_name);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
    }
  if (sdp->i_info != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "i=");
      tmp = __osip_sdp_append_string (string, size, tmp, sdp->i_info);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
    }
  if (sdp->u_uri != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "u=");
      tmp = __osip_sdp_append_string (string, size, tmp, sdp->u_uri);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
    }
  pos = 0;
  while (!osip_list_eol (sdp->e_emails, pos))
    {
      char *email = (char *) osip_list_get (sdp->e_emails, pos);

      tmp = __osip_sdp_append_string (string, size, tmp, "e=");
      tmp = __osip_sdp_append_string (string, size, tmp, email);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
      pos++;
    }
  pos = 0;
  while (!osip_list_eol (sdp->p_phones, pos))
    {
      char *phone = (char *) osip_list_get (sdp->p_phones, pos);

      tmp = __osip_sdp_append_string (string, size, tmp, "p=");
      tmp = __osip_sdp_append_string (string, size, tmp, phone);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
      pos++;
    }
  if (sdp->c_connection != NULL)
    {
      char *next_tmp;
      int i;

      i =
	sdp_append_connection (string, size, tmp, sdp->c_connection,
			       &next_tmp);
      if (i != 0)
	return -1;
      tmp = next_tmp;
    }
  pos = 0;
  while (!osip_list_eol (sdp->b_bandwidths, pos))
    {
      sdp_bandwidth_t *header =
	(sdp_bandwidth_t *) osip_list_get (sdp->b_bandwidths, pos);
      char *next_tmp;
      int i;

      i = sdp_append_bandwidth (string, size, tmp, header, &next_tmp);
      if (i != 0)
	return -1;
      tmp = next_tmp;
      pos++;
    }

  pos = 0;
  while (!osip_list_eol (sdp->t_descrs, pos))
    {
      sdp_time_descr_t *header =
	(sdp_time_descr_t *) osip_list_get (sdp->t_descrs, pos);
      char *next_tmp;
      int i;

      i = sdp_append_time_descr (string, size, tmp, header, &next_tmp);
      if (i != 0)
	return -1;
      tmp = next_tmp;
      pos++;
    }

  if (sdp->z_adjustments != NULL)
    {
      tmp = __osip_sdp_append_string (string, size, tmp, "z=");
      tmp = __osip_sdp_append_string (string, size, tmp, sdp->z_adjustments);
      tmp = __osip_sdp_append_string (string, size, tmp, CRLF);
    }

  if (sdp->k_key != NULL)
    {
      char *next_tmp;
      int i;

      i = sdp_append_key (string, size, tmp, sdp->k_key, &next_tmp);
      if (i != 0)
	return -1;
      tmp = next_tmp;
    }

  pos = 0;
  while (!osip_list_eol (sdp->a_attributes, pos))
    {
      sdp_attribute_t *header =
	(sdp_attribute_t *) osip_list_get (sdp->a_attributes, pos);
      char *next_tmp;
      int i;

      i = sdp_append_attribute (string, size, tmp, header, &next_tmp);
      if (i != 0)
	return -1;
      tmp = next_tmp;
      pos++;
    }

  pos = 0;
  while (!osip_list_eol (sdp->m_medias, pos))
    {
      sdp_media_t *header =
	(sdp_media_t *) osip_list_get (sdp->m_medias, pos);
      char *next_tmp;
      int i;

      i = sdp_append_media (string, size, tmp, header, &next_tmp);
      if (i != 0)
	return -1;
      tmp = next_tmp;
      pos++;
    }
  *dest = string;
  return 0;
}

void
sdp_message_free (sdp_message_t * sdp)
{
  if (sdp == NULL)
    return;
  osip_free (sdp->v_version);
  osip_free (sdp->o_username);
  osip_free (sdp->o_sess_id);
  osip_free (sdp->o_sess_version);
  osip_free (sdp->o_nettype);
  osip_free (sdp->o_addrtype);
  osip_free (sdp->o_addr);
  osip_free (sdp->s_name);
  osip_free (sdp->i_info);
  osip_free (sdp->u_uri);

  osip_list_ofchar_free (sdp->e_emails);

  osip_list_ofchar_free (sdp->p_phones);

  sdp_connection_free (sdp->c_connection);

  osip_list_special_free (sdp->b_bandwidths,
			  (void *(*)(void *)) &sdp_bandwidth_free);

  osip_list_special_free (sdp->t_descrs,
			  (void *(*)(void *)) &sdp_time_descr_free);

  osip_free (sdp->z_adjustments);
  sdp_key_free (sdp->k_key);

  osip_list_special_free (sdp->a_attributes,
			  (void *(*)(void *)) &sdp_attribute_free);

  osip_list_special_free (sdp->m_medias, (void *(*)(void *)) &sdp_media_free);

  osip_free (sdp);
}

int
sdp_message_clone (sdp_message_t * sdp, sdp_message_t ** dest)
{
  int i;
  char *body;

  i = sdp_message_init (dest);
  if (i != 0)
    return -1;

  i = sdp_message_to_str (sdp, &body);
  if (i != 0)
    goto error_sc1;

  i = sdp_message_parse (*dest, body);
  osip_free (body);
  if (i != 0)
    goto error_sc1;

  return 0;

error_sc1:
  sdp_message_free (*dest);
  return -1;
}
