/*
  The oSIP library implements the Session Initiation Protocol (SIP -rfc3261-)
  Copyright (C) 2001,2002,2003,2004,2005,2006,2007 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_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;
  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;
  osip_list_init (&(*media)->m_payloads);
  (*media)->i_info = NULL;
  osip_list_init (&(*media)->c_connections);
  osip_list_init (&(*media)->b_bandwidths);
  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;

  osip_list_init (&(*sdp)->e_emails);

  osip_list_init (&(*sdp)->p_phones);

  (*sdp)->c_connection = NULL;

  osip_list_init (&(*sdp)->b_bandwidths);

  osip_list_init (&(*sdp)->t_descrs);

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

  osip_list_init (&(*sdp)->a_attributes);

  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 */
#ifdef FIREFLY_BUG_SUPPORT
  if (tmp[0] == ' ')
    {
      sdp->o_username = osip_strdup ("firefly");
      tmp++;
  } else
    {
      i = __osip_set_next_token (&(sdp->o_username), tmp, ' ', &tmp_next);
      if (i != 0)
        return -1;
      tmp = tmp_next;
    }
#else
  i = __osip_set_next_token (&(sdp->o_username), tmp, ' ', &tmp_next);
  if (i != 0)
    return -1;
  tmp = tmp_next;
#endif

  /* 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;
#ifdef FIREFLY_BUG_SUPPORT
  if (crlf == equal + 1)
    {
      sdp->s_name = osip_strdup (" ");
      if (crlf[1] == '\n')
        *next = crlf + 2;
      else
        *next = crlf + 1;
      return WF;                /* o=\r ?? bad header */
    }
#else
  if (crlf == equal + 1)
    return ERR_ERROR;           /* o=\r ?? bad header */
#endif

  /* 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)
    {
      sdp_connection_free (c_header);
      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)
    {
      sdp_connection_free (c_header);
      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)
	  {
	    sdp_connection_free (c_header);
	    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)
	      {
		sdp_connection_free (c_header);
		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)
    {
      sdp_bandwidth_free (b_header);
      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)
    {
      /* a few stack don't add SPACE after m_proto when rejecting all payloads */
        i = __osip_set_next_token (&(m_header->m_proto), tmp, '\r', &tmp_next);
        if (i != 0)
          {
            i = __osip_set_next_token (&(m_header->m_proto), tmp, '\n', &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 || 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 = BODY_MESSAGE_MAX_SIZE;
  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)
        {
          osip_free (string);
          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)
        {
          osip_free (string);
          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)
        {
          osip_free (string);
          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)
        {
          osip_free (string);
          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)
        {
          osip_free (string);
          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)
        {
          osip_free (string);
          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;
}
