/*
  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 <osip2/osip_negotiation.h>
#include <osip2/internal.h>

static int sdp_partial_clone (osip_negotiation_t * config,
                              osip_negotiation_ctx_t * con,
                              sdp_message_t * remote, sdp_message_t ** dest);
static int sdp_confirm_media (osip_negotiation_t * config,
                              osip_negotiation_ctx_t * context,
                              sdp_message_t * remote, sdp_message_t ** dest);
static __payload_t *osip_negotiation_find_audio_payload (osip_negotiation_t *
                                                         cfg, char *payload);
static __payload_t *osip_negotiation_find_video_payload (osip_negotiation_t *
                                                         cfg, char *payload);
static __payload_t *osip_negotiation_find_other_payload (osip_negotiation_t *
                                                         cfg, char *payload);



int
osip_negotiation_ctx_init (osip_negotiation_ctx_t ** con)
{
  (*con) =
    (osip_negotiation_ctx_t *) osip_malloc (sizeof (osip_negotiation_ctx_t));
  if (*con == NULL)
    return -1;
  (*con)->mycontext = NULL;     /* fixed Sep 24 2003 */
  (*con)->remote = NULL;
  (*con)->local = NULL;
  return 0;
}

void
osip_negotiation_ctx_free (osip_negotiation_ctx_t * con)
{
  if (con == NULL)
    return;
  sdp_message_free (con->remote);
  sdp_message_free (con->local);
  osip_free (con);
}

/* this method is used by end-user application so any pointer can
   be associated with this context (usefull to link with your own context */
int
osip_negotiation_ctx_set_mycontext (osip_negotiation_ctx_t * con,
                                    void *my_instance)
{
  if (con == NULL)
    return -1;
  con->mycontext = my_instance;
  return 0;
}

void *
osip_negotiation_ctx_get_mycontext (osip_negotiation_ctx_t * con)
{
  if (con == NULL)
    return NULL;
  return con->mycontext;
}

sdp_message_t *
osip_negotiation_ctx_get_local_sdp (osip_negotiation_ctx_t * con)
{
  if (con == NULL)
    return NULL;
  return con->local;
}

int
osip_negotiation_ctx_set_local_sdp (osip_negotiation_ctx_t * con,
                                    sdp_message_t * sdp)
{
  if (con == NULL)
    return -1;
  con->local = sdp;
  return 0;
}

sdp_message_t *
osip_negotiation_ctx_get_remote_sdp (osip_negotiation_ctx_t * con)
{
  if (con == NULL)
    return NULL;
  return con->remote;
}

int
osip_negotiation_ctx_set_remote_sdp (osip_negotiation_ctx_t * con,
                                     sdp_message_t * sdp)
{
  if (con == NULL)
    return -1;
  con->remote = sdp;
  return 0;
}

int
__payload_init (__payload_t ** payload)
{
  *payload = (__payload_t *) osip_malloc (sizeof (__payload_t));
  if (*payload == NULL)
    return -1;
  (*payload)->payload = NULL;
  (*payload)->number_of_port = NULL;
  (*payload)->proto = NULL;
  (*payload)->c_nettype = NULL;
  (*payload)->c_addrtype = NULL;
  (*payload)->c_addr = NULL;
  (*payload)->c_addr_multicast_ttl = NULL;
  (*payload)->c_addr_multicast_int = NULL;
  (*payload)->a_rtpmap = NULL;
  return 0;
}

void
__payload_free (__payload_t * payload)
{
  if (payload == NULL)
    return;
  osip_free (payload->payload);
  osip_free (payload->number_of_port);
  osip_free (payload->proto);
  osip_free (payload->c_nettype);
  osip_free (payload->c_addrtype);
  osip_free (payload->c_addr);
  osip_free (payload->c_addr_multicast_ttl);
  osip_free (payload->c_addr_multicast_int);
  osip_free (payload->a_rtpmap);
  osip_free (payload);
}

int
osip_negotiation_init (osip_negotiation_t ** config_out)
{
  osip_negotiation_t *config;

  config = (osip_negotiation_t *) osip_malloc (sizeof (osip_negotiation_t));
  if (config == NULL)
    return -1;
  config->o_username = NULL;
  config->o_session_id = NULL;
  config->o_session_version = NULL;
  config->o_nettype = NULL;
  config->o_addrtype = NULL;
  config->o_addr = NULL;

  config->c_nettype = NULL;
  config->c_addrtype = NULL;
  config->c_addr = NULL;
  config->c_addr_multicast_ttl = NULL;
  config->c_addr_multicast_int = NULL;

  /* supported codec for the SIP User Agent */
  config->audio_codec = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init (config->audio_codec);
  config->video_codec = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init (config->video_codec);
  config->other_codec = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init (config->other_codec);

  config->fcn_set_info = NULL;
  config->fcn_set_uri = NULL;
  config->fcn_set_emails = NULL;
  config->fcn_set_phones = NULL;
  config->fcn_set_attributes = NULL;
  config->fcn_accept_audio_codec = NULL;
  config->fcn_accept_video_codec = NULL;
  config->fcn_accept_other_codec = NULL;

  *config_out = config;

  return 0;
}

void
osip_negotiation_free (osip_negotiation_t * config)
{
  if (config == NULL)
    return;
  osip_free (config->o_username);
  osip_free (config->o_session_id);
  osip_free (config->o_session_version);
  osip_free (config->o_nettype);
  osip_free (config->o_addrtype);
  osip_free (config->o_addr);

  osip_free (config->c_nettype);
  osip_free (config->c_addrtype);
  osip_free (config->c_addr);
  osip_free (config->c_addr_multicast_ttl);
  osip_free (config->c_addr_multicast_int);

  osip_list_special_free (config->audio_codec,
                          (void *(*)(void *)) &__payload_free);
  osip_list_special_free (config->video_codec,
                          (void *(*)(void *)) &__payload_free);
  osip_list_special_free (config->other_codec,
                          (void *(*)(void *)) &__payload_free);

  /* other are pointer to func, they don't need free() calls */

  /* yes, this is done here... :) */
  osip_free (config);
}

int
osip_negotiation_set_o_username (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->o_username = tmp;
  return 0;
}

int
osip_negotiation_set_o_session_id (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->o_session_id = tmp;
  return 0;
}

int
osip_negotiation_set_o_session_version (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->o_session_version = tmp;
  return 0;
}

int
osip_negotiation_set_o_nettype (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->o_nettype = tmp;
  return 0;
}

int
osip_negotiation_set_o_addrtype (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->o_addrtype = tmp;
  return 0;
}

int
osip_negotiation_set_o_addr (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->o_addr = tmp;
  return 0;
}

int
osip_negotiation_set_c_nettype (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->c_nettype = tmp;
  return 0;
}

int
osip_negotiation_set_c_addrtype (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->c_addrtype = tmp;
  return 0;
}

int
osip_negotiation_set_c_addr (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->c_addr = tmp;
  return 0;
}

int
osip_negotiation_set_c_addr_multicast_ttl (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->c_addr_multicast_ttl = tmp;
  return 0;
}

int
osip_negotiation_set_c_addr_multicast_int (osip_negotiation_t * config, char *tmp)
{
  if (config == NULL)
    return -1;
  config->c_addr_multicast_int = tmp;
  return 0;
}

int
osip_negotiation_add_support_for_audio_codec (osip_negotiation_t * config,
                                              char *payload,
                                              char *number_of_port,
                                              char *proto, char *c_nettype,
                                              char *c_addrtype, char *c_addr,
                                              char *c_addr_multicast_ttl,
                                              char *c_addr_multicast_int,
                                              char *a_rtpmap)
{
  int i;
  __payload_t *my_payload;

  i = __payload_init (&my_payload);
  if (i != 0)
    return -1;
  my_payload->payload = payload;
  my_payload->number_of_port = number_of_port;
  my_payload->proto = proto;
  my_payload->c_nettype = c_nettype;
  my_payload->c_addrtype = c_addrtype;
  my_payload->c_addr = c_addr;
  my_payload->c_addr_multicast_ttl = c_addr_multicast_ttl;
  my_payload->c_addr_multicast_int = c_addr_multicast_int;
  my_payload->a_rtpmap = a_rtpmap;
  osip_list_add (config->audio_codec, my_payload, -1);
  return 0;
}

int
osip_negotiation_add_support_for_video_codec (osip_negotiation_t * config,
                                              char *payload,
                                              char *number_of_port,
                                              char *proto, char *c_nettype,
                                              char *c_addrtype, char *c_addr,
                                              char *c_addr_multicast_ttl,
                                              char *c_addr_multicast_int,
                                              char *a_rtpmap)
{
  int i;
  __payload_t *my_payload;

  i = __payload_init (&my_payload);
  if (i != 0)
    return -1;
  my_payload->payload = payload;
  my_payload->number_of_port = number_of_port;
  my_payload->proto = proto;
  my_payload->c_nettype = c_nettype;
  my_payload->c_addrtype = c_addrtype;
  my_payload->c_addr = c_addr;
  my_payload->c_addr_multicast_ttl = c_addr_multicast_ttl;
  my_payload->c_addr_multicast_int = c_addr_multicast_int;
  my_payload->a_rtpmap = a_rtpmap;
  osip_list_add (config->video_codec, my_payload, -1);
  return 0;
}

int
osip_negotiation_add_support_for_other_codec (osip_negotiation_t * config,
                                              char *payload,
                                              char *number_of_port,
                                              char *proto, char *c_nettype,
                                              char *c_addrtype, char *c_addr,
                                              char *c_addr_multicast_ttl,
                                              char *c_addr_multicast_int,
                                              char *a_rtpmap)
{
  int i;
  __payload_t *my_payload;

  i = __payload_init (&my_payload);
  if (i != 0)
    return -1;
  my_payload->payload = payload;
  my_payload->number_of_port = number_of_port;
  my_payload->proto = proto;
  my_payload->c_nettype = c_nettype;
  my_payload->c_addrtype = c_addrtype;
  my_payload->c_addr = c_addr;
  my_payload->c_addr_multicast_ttl = c_addr_multicast_ttl;
  my_payload->c_addr_multicast_int = c_addr_multicast_int;
  my_payload->a_rtpmap = a_rtpmap;
  osip_list_add (config->other_codec, my_payload, -1);
  return 0;
}

int
osip_negotiation_remove_audio_payloads (osip_negotiation_t * config)
{
  osip_list_special_free (config->audio_codec,
                          (void *(*)(void *)) &__payload_free);
  config->audio_codec = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init (config->audio_codec);
  return 0;
}

int
osip_negotiation_remove_video_payloads (osip_negotiation_t * config)
{
  osip_list_special_free (config->video_codec,
                          (void *(*)(void *)) &__payload_free);
  config->video_codec = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init (config->video_codec);
  return 0;
}

int
osip_negotiation_remove_other_payloads (osip_negotiation_t * config)
{
  osip_list_special_free (config->other_codec,
                          (void *(*)(void *)) &__payload_free);
  config->other_codec = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init (config->other_codec);
  return 0;
}

static __payload_t *
osip_negotiation_find_audio_payload (osip_negotiation_t * config, char *payload)
{
  __payload_t *my;
  size_t length = strlen (payload);
  int pos = 0;

  if (payload == NULL)
    return NULL;
  while (!osip_list_eol (config->audio_codec, pos))
    {
      my = (__payload_t *) osip_list_get (config->audio_codec, pos);
      if (strlen (my->payload) == length)
        if (0 == strncmp (my->payload, payload, length))
          return my;
      pos++;
    }
  return NULL;
}

static __payload_t *
osip_negotiation_find_video_payload (osip_negotiation_t * config, char *payload)
{
  __payload_t *my;
  size_t length = strlen (payload);
  int pos = 0;

  if (payload == NULL)
    return NULL;
  while (!osip_list_eol (config->video_codec, pos))
    {
      my = (__payload_t *) osip_list_get (config->video_codec, pos);
      if (strlen (my->payload) == length)
        if (0 == strncmp (my->payload, payload, length))
          return my;
      pos++;
    }
  return NULL;
}

static __payload_t *
osip_negotiation_find_other_payload (osip_negotiation_t * config, char *payload)
{
  __payload_t *my;
  size_t length = strlen (payload);
  int pos = 0;

  if (payload == NULL)
    return NULL;
  while (!osip_list_eol (config->other_codec, pos))
    {
      my = (__payload_t *) osip_list_get (config->other_codec, pos);
      if (strlen (my->payload) == length)
        if (0 == strncmp (my->payload, payload, length))
          return my;
      pos++;
    }
  return NULL;
}

int
osip_negotiation_set_fcn_set_info (osip_negotiation_t * config,
                                   int (*fcn) (osip_negotiation_ctx_t *,
                                               sdp_message_t *))
{
  if (config == NULL)
    return -1;
  config->fcn_set_info = (int (*)(void *, sdp_message_t *)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_set_uri (osip_negotiation_t * config,
                                  int (*fcn) (osip_negotiation_ctx_t *,
                                              sdp_message_t *))
{
  if (config == NULL)
    return -1;
  config->fcn_set_uri = (int (*)(void *, sdp_message_t *)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_set_emails (osip_negotiation_t * config,
                                     int (*fcn) (osip_negotiation_ctx_t *,
                                                 sdp_message_t *))
{
  if (config == NULL)
    return -1;
  config->fcn_set_emails = (int (*)(void *, sdp_message_t *)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_set_phones (osip_negotiation_t * config,
                                     int (*fcn) (osip_negotiation_ctx_t *,
                                                 sdp_message_t *))
{
  if (config == NULL)
    return -1;
  config->fcn_set_phones = (int (*)(void *, sdp_message_t *)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_set_attributes (osip_negotiation_t * config,
                                         int (*fcn) (osip_negotiation_ctx_t *,
                                                     sdp_message_t *, int))
{
  if (config == NULL)
    return -1;
  config->fcn_set_attributes = (int (*)(void *, sdp_message_t *, int)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_accept_audio_codec (osip_negotiation_t * config,
                                             int (*fcn)
                                             (osip_negotiation_ctx_t *,
                                              char *, char *, int, char *))
{
  if (config == NULL)
    return -1;
  config->fcn_accept_audio_codec = (int (*)(void *, char *,
                                            char *, int, char *)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_accept_video_codec (osip_negotiation_t * config,
                                             int (*fcn)
                                             (osip_negotiation_ctx_t *,
                                              char *, char *, int, char *))
{
  if (config == NULL)
    return -1;
  config->fcn_accept_video_codec = (int (*)(void *, char *,
                                            char *, int, char *)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_accept_other_codec (osip_negotiation_t * config,
                                             int (*fcn)
                                             (osip_negotiation_ctx_t *,
                                              char *, char *, char *, char *))
{
  if (config == NULL)
    return -1;
  config->fcn_accept_other_codec = (int (*)(void *, char *,
                                            char *, char *, char *)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_get_audio_port (osip_negotiation_t * config,
                                         char *(*fcn) (osip_negotiation_ctx_t
                                                       *, int))
{
  if (config == NULL)
    return -1;
  config->fcn_get_audio_port = (char *(*)(void *, int)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_get_video_port (osip_negotiation_t * config,
                                         char *(*fcn) (osip_negotiation_ctx_t
                                                       *, int))
{
  if (config == NULL)
    return -1;
  config->fcn_get_video_port = (char *(*)(void *, int)) fcn;
  return 0;
}

int
osip_negotiation_set_fcn_get_other_port (osip_negotiation_t * config,
                                         char *(*fcn) (osip_negotiation_ctx_t
                                                       *, int))
{
  if (config == NULL)
    return -1;
  config->fcn_get_other_port = (char *(*)(void *, int)) fcn;
  return 0;
}

static int
sdp_partial_clone (osip_negotiation_t * config,
                   osip_negotiation_ctx_t * con, sdp_message_t * remote,
                   sdp_message_t ** dest)
{
  int i;

  sdp_message_v_version_set (*dest, osip_strdup ("0"));

  /* those fields MUST be set */
  sdp_message_o_origin_set (*dest,
                            osip_strdup (config->o_username),
                            osip_strdup (config->o_session_id),
                            osip_strdup (config->o_session_version),
                            osip_strdup (config->o_nettype),
                            osip_strdup (config->o_addrtype),
                            osip_strdup (config->o_addr));
  sdp_message_s_name_set (*dest, osip_strdup (remote->s_name));
  if (config->fcn_set_info != NULL)
    config->fcn_set_info (con, *dest);
  if (config->fcn_set_uri != NULL)
    config->fcn_set_uri (con, *dest);
  if (config->fcn_set_emails != NULL)
    config->fcn_set_emails (con, *dest);
  if (config->fcn_set_phones != NULL)
    config->fcn_set_phones (con, *dest);
  if (config->c_nettype != NULL)
    sdp_message_c_connection_add (*dest, -1,
                                  osip_strdup (config->c_nettype),
                                  osip_strdup (config->c_addrtype),
                                  osip_strdup (config->c_addr),
                                  osip_strdup (config->c_addr_multicast_ttl),
                                  osip_strdup (config->c_addr_multicast_int));

  {                             /* offer-answer draft says we must copy the "t=" line */
    char *tmp = sdp_message_t_start_time_get (remote, 0);
    char *tmp2 = sdp_message_t_stop_time_get (remote, 0);

    if (tmp == NULL || tmp2 == NULL)
      return -1;                /* no t line?? */
    i =
      sdp_message_t_time_descr_add (*dest, osip_strdup (tmp), osip_strdup (tmp2));
    if (i != 0)
      return -1;
  }
  if (config->fcn_set_attributes != NULL)
    config->fcn_set_attributes (con, *dest, -1);
  return 0;
}

static int
sdp_confirm_media (osip_negotiation_t * config,
                   osip_negotiation_ctx_t * context, sdp_message_t * remote,
                   sdp_message_t ** dest)
{
  char *payload;
  char *tmp, *tmp2, *tmp3, *tmp4;
  int ret;
  int i;
  int k;
  int audio_qty = 0;            /* accepted audio line: do not accept more than one */
  int video_qty = 0;

  i = 0;
  while (!sdp_message_endof_media (remote, i))
    {
      tmp = sdp_message_m_media_get (remote, i);
      tmp2 = sdp_message_m_port_get (remote, i);
      tmp3 = sdp_message_m_number_of_port_get (remote, i);
      tmp4 = sdp_message_m_proto_get (remote, i);

      if (tmp == NULL)
        return -1;
      sdp_message_m_media_add (*dest, osip_strdup (tmp), osip_strdup ("0"),
                               NULL, osip_strdup (tmp4));
      k = 0;
      if (0 == strncmp (tmp, "audio", 5))
        {
          do
            {
              payload = sdp_message_m_payload_get (remote, i, k);
              if (payload != NULL)
                {
                  __payload_t *my_payload =
                    osip_negotiation_find_audio_payload (config, payload);

                  if (my_payload != NULL)       /* payload is supported */
                    {
                      ret = -1; /* somtimes, codec can be refused even if supported */
                      if (config->fcn_accept_audio_codec != NULL)
                        ret = config->fcn_accept_audio_codec (context, tmp2,
                                                              tmp3, audio_qty,
                                                              payload);
                      if (0 == ret)
                        {
                          sdp_message_m_payload_add (*dest, i,
                                                     osip_strdup (payload));
                          if (my_payload->a_rtpmap != NULL)
                            sdp_message_a_attribute_add (*dest, i,
                                                         osip_strdup
                                                         ("rtpmap"),
                                                         osip_strdup
                                                         (my_payload->a_rtpmap));
                          if (my_payload->c_nettype != NULL)
                            {
                              sdp_media_t *med =
                                osip_list_get ((*dest)->m_medias, i);

                              if (osip_list_eol (med->c_connections, 0))
                                sdp_message_c_connection_add (*dest, i,
                                                              osip_strdup
                                                              (my_payload->
                                                               c_nettype),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addrtype),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr_multicast_ttl),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr_multicast_int));
                            }
                        }
                    }
                }
              k++;
            }
          while (payload != NULL);
          if (NULL != sdp_message_m_payload_get (*dest, i, 0))
            audio_qty = 1;
      } else if (0 == strncmp (tmp, "video", 5))
        {
          do
            {
              payload = sdp_message_m_payload_get (remote, i, k);
              if (payload != NULL)
                {
                  __payload_t *my_payload =
                    osip_negotiation_find_video_payload (config, payload);

                  if (my_payload != NULL)       /* payload is supported */
                    {
                      ret = -1;
                      if (config->fcn_accept_video_codec != NULL)
                        ret =
                          config->fcn_accept_video_codec (context, tmp2, tmp3,
                                                          video_qty, payload);
                      if (0 == ret)
                        {
                          sdp_message_m_payload_add (*dest, i,
                                                     osip_strdup (payload));
                          /* TODO  set the attribute list (rtpmap..) */
                          if (my_payload->a_rtpmap != NULL)
                            sdp_message_a_attribute_add (*dest, i,
                                                         osip_strdup
                                                         ("rtpmap"),
                                                         osip_strdup
                                                         (my_payload->a_rtpmap));
                          if (my_payload->c_nettype != NULL)
                            {
                              sdp_media_t *med =
                                osip_list_get ((*dest)->m_medias, i);

                              if (osip_list_eol (med->c_connections, 0))
                                sdp_message_c_connection_add (*dest, i,
                                                              osip_strdup
                                                              (my_payload->
                                                               c_nettype),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addrtype),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr_multicast_ttl),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr_multicast_int));
                            }
                        }
                    }
                }
              k++;
            }
          while (payload != NULL);
          if (NULL != sdp_message_m_payload_get (*dest, i, 0))
            video_qty = 1;
      } else
        {
          do
            {
              payload = sdp_message_m_payload_get (remote, i, k);
              if (payload != NULL)
                {
                  __payload_t *my_payload =
                    osip_negotiation_find_other_payload (config, payload);

                  if (my_payload != NULL)       /* payload is supported */
                    {
                      ret = -1;
                      if (config->fcn_accept_other_codec != NULL)
                        ret =
                          config->fcn_accept_other_codec (context, tmp, tmp2,
                                                          tmp3, payload);
                      if (0 == ret)
                        {
                          sdp_message_m_payload_add (*dest, i,
                                                     osip_strdup (payload));
                          /* rtpmap has no meaning here! */
                          if (my_payload->c_nettype != NULL)
                            {
                              sdp_media_t *med =
                                osip_list_get ((*dest)->m_medias, i);

                              if (osip_list_eol (med->c_connections, 0))
                                sdp_message_c_connection_add (*dest, i,
                                                              osip_strdup
                                                              (my_payload->
                                                               c_nettype),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addrtype),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr_multicast_ttl),
                                                              osip_strdup
                                                              (my_payload->
                                                               c_addr_multicast_int));
                            }
                        }
                    }
                }
              k++;
            }
          while (payload != NULL);
        }
      i++;
    }
  return 0;
}

int
osip_negotiation_ctx_execute_negotiation (osip_negotiation_t * config,
                                          osip_negotiation_ctx_t * context)
{
  int m_lines_that_match = 0;
  sdp_message_t *remote;
  sdp_message_t *local;
  int i;

  if (context == NULL)
    return -1;
  remote = context->remote;
  if (remote == NULL)
    return -1;

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

  if (0 != strncmp (remote->v_version, "0", 1))
    {
      sdp_message_free (local);
      /*      sdp_context->fcn_wrong_version(context); */
      return 406;               /* Not Acceptable */
    }

  i = sdp_partial_clone (config, context, remote, &local);
  if (i != 0)
    {
      sdp_message_free (local);
      return -1;
    }
  i = sdp_confirm_media (config, context, remote, &local);
  if (i != 0)
    {
      sdp_message_free (local);
      return i;
    }

  i = 0;
  while (!sdp_message_endof_media (local, i))
    {
      /* this is to refuse each line with no codec that matches! */
      if (NULL == sdp_message_m_payload_get (local, i, 0))
        {
          sdp_media_t *med = osip_list_get ((local)->m_medias, i);
          char *str = sdp_message_m_payload_get (remote, i, 0);

          sdp_message_m_payload_add (local, i, osip_strdup (str));
          osip_free (med->m_port);
          med->m_port = osip_strdup ("0");      /* refuse this line */
      } else
        {                       /* number of "m" lines that match */
          sdp_media_t *med = osip_list_get (local->m_medias, i);

          m_lines_that_match++;
          osip_free (med->m_port);
          /* AMD: use the correct fcn_get_xxx_port method: */
          if (0 == strcmp (med->m_media, "audio"))
            {
              if (config->fcn_get_audio_port != NULL)
                med->m_port = config->fcn_get_audio_port (context, i);
              else
                med->m_port = osip_strdup ("0");        /* should never happen */
          } else if (0 == strcmp (med->m_media, "video"))
            {
              if (config->fcn_get_video_port != NULL)
                med->m_port = config->fcn_get_video_port (context, i);
              else
                med->m_port = osip_strdup ("0");        /* should never happen */
          } else
            {
              if (config->fcn_get_other_port != NULL)
                med->m_port = config->fcn_get_other_port (context, i);
              else
                med->m_port = osip_strdup ("0");        /* should never happen */
            }
        }
      i++;
    }
  if (m_lines_that_match > 0)
    {
      context->local = local;
      return 200;
  } else
    {
      sdp_message_free (local);
      return 415;
    }

}

int
osip_negotiation_sdp_build_offer (osip_negotiation_t * config,
                                  osip_negotiation_ctx_t * con,
                                  sdp_message_t ** sdp, char *audio_port,
                                  char *video_port)
{
  int i;
  int media_line = 0;

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

  sdp_message_v_version_set (*sdp, osip_strdup ("0"));

  /* those fields MUST be set */
  sdp_message_o_origin_set (*sdp,
                            osip_strdup (config->o_username),
                            osip_strdup (config->o_session_id),
                            osip_strdup (config->o_session_version),
                            osip_strdup (config->o_nettype),
                            osip_strdup (config->o_addrtype),
                            osip_strdup (config->o_addr));
  sdp_message_s_name_set (*sdp, osip_strdup ("A call"));
  if (config->fcn_set_info != NULL)
    config->fcn_set_info (con, *sdp);
  if (config->fcn_set_uri != NULL)
    config->fcn_set_uri (con, *sdp);
  if (config->fcn_set_emails != NULL)
    config->fcn_set_emails (con, *sdp);
  if (config->fcn_set_phones != NULL)
    config->fcn_set_phones (con, *sdp);
  if (config->c_nettype != NULL)
    sdp_message_c_connection_add (*sdp, -1,
                                  osip_strdup (config->c_nettype),
                                  osip_strdup (config->c_addrtype),
                                  osip_strdup (config->c_addr),
                                  osip_strdup (config->c_addr_multicast_ttl),
                                  osip_strdup (config->c_addr_multicast_int));

  i = sdp_message_t_time_descr_add (*sdp, osip_strdup ("0"), osip_strdup ("0"));
  if (i != 0)
    return -1;

  if (config->fcn_set_attributes != NULL)
    config->fcn_set_attributes (con, *sdp, -1);


  /* add all audio codec */
  if (!osip_list_eol (config->audio_codec, 0))
    {
      int pos = 0;
      __payload_t *my = (__payload_t *) osip_list_get (config->audio_codec, pos);

      /* all media MUST have the same PROTO, PORT. */
      sdp_message_m_media_add (*sdp, osip_strdup ("audio"),
                               osip_strdup (audio_port),
                               osip_strdup (my->number_of_port),
                               osip_strdup (my->proto));

      while (!osip_list_eol (config->audio_codec, pos))
        {
          my = (__payload_t *) osip_list_get (config->audio_codec, pos);
          sdp_message_m_payload_add (*sdp, media_line, osip_strdup (my->payload));
          if (my->a_rtpmap != NULL)
            sdp_message_a_attribute_add (*sdp, media_line,
                                         osip_strdup ("rtpmap"),
                                         osip_strdup (my->a_rtpmap));
          pos++;
        }
      media_line++;
    }

  /* add all video codec */
  if (!osip_list_eol (config->video_codec, 0))
    {
      int pos = 0;
      __payload_t *my = (__payload_t *) osip_list_get (config->video_codec, pos);

      /* all media MUST have the same PROTO, PORT. */
      sdp_message_m_media_add (*sdp, osip_strdup ("video"),
                               osip_strdup (video_port),
                               osip_strdup (my->number_of_port),
                               osip_strdup (my->proto));

      while (!osip_list_eol (config->video_codec, pos))
        {
          my = (__payload_t *) osip_list_get (config->video_codec, pos);
          sdp_message_m_payload_add (*sdp, media_line, osip_strdup (my->payload));
          if (my->a_rtpmap != NULL)
            sdp_message_a_attribute_add (*sdp, media_line,
                                         osip_strdup ("rtpmap"),
                                         osip_strdup (my->a_rtpmap));
          pos++;
        }
      media_line++;
    }
  return 0;
}

/* build the SDP packet with only one audio codec and one video codec.
 * - Usefull if you don't want to restrict proposal to one codec only -
 * - Limitation, only one codec will be proposed
 */
int
__osip_negotiation_sdp_build_offer (osip_negotiation_t * config,
                                    osip_negotiation_ctx_t * con,
                                    sdp_message_t ** sdp, char *audio_port,
                                    char *video_port, char *audio_codec,
                                    char *video_codec)
{
  int i;
  int media_line = 0;

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

  sdp_message_v_version_set (*sdp, osip_strdup ("0"));

  /* those fields MUST be set */
  sdp_message_o_origin_set (*sdp,
                            osip_strdup (config->o_username),
                            osip_strdup (config->o_session_id),
                            osip_strdup (config->o_session_version),
                            osip_strdup (config->o_nettype),
                            osip_strdup (config->o_addrtype),
                            osip_strdup (config->o_addr));
  sdp_message_s_name_set (*sdp, osip_strdup ("A call"));
  if (config->fcn_set_info != NULL)
    config->fcn_set_info (con, *sdp);
  if (config->fcn_set_uri != NULL)
    config->fcn_set_uri (con, *sdp);
  if (config->fcn_set_emails != NULL)
    config->fcn_set_emails (con, *sdp);
  if (config->fcn_set_phones != NULL)
    config->fcn_set_phones (con, *sdp);
  if (config->c_nettype != NULL)
    sdp_message_c_connection_add (*sdp, -1,
                                  osip_strdup (config->c_nettype),
                                  osip_strdup (config->c_addrtype),
                                  osip_strdup (config->c_addr),
                                  osip_strdup (config->c_addr_multicast_ttl),
                                  osip_strdup (config->c_addr_multicast_int));

  {                             /* offer-answer draft says we must copy the "t=" line */
    time_t now = time (NULL);
    char *tmp = osip_malloc (15);
    char *tmp2 = osip_malloc (15);

    sprintf (tmp, "%li", now);
    sprintf (tmp2, "%li", now + 3600);

    i = sdp_message_t_time_descr_add (*sdp, tmp, tmp2);
    if (i != 0)
      return -1;
  }
  if (config->fcn_set_attributes != NULL)
    config->fcn_set_attributes (con, *sdp, -1);


  /* add all audio codec */
  if (audio_codec != NULL)
    {
      if (!osip_list_eol (config->audio_codec, 0))
        {
          int pos = 0;
          __payload_t *my =
            (__payload_t *) osip_list_get (config->audio_codec, pos);

          while (!osip_list_eol (config->audio_codec, pos))
            {
              my = (__payload_t *) osip_list_get (config->audio_codec, pos);
              if (0 == strcmp (audio_codec, my->payload))
                {
                  /* all media MUST have the same PROTO, PORT. */
                  sdp_message_m_media_add (*sdp, osip_strdup ("audio"),
                                           osip_strdup (audio_port),
                                           osip_strdup (my->number_of_port),
                                           osip_strdup (my->proto));
                  sdp_message_m_payload_add (*sdp, media_line,
                                             osip_strdup (my->payload));
                  if (my->a_rtpmap != NULL)
                    sdp_message_a_attribute_add (*sdp, media_line,
                                                 osip_strdup ("rtpmap"),
                                                 osip_strdup (my->a_rtpmap));
                  media_line++;
                  break;
                }
              pos++;
            }
        }
    }

  /* add all video codec */
  if (video_codec != NULL)
    {
      if (!osip_list_eol (config->video_codec, 0))
        {
          int pos = 0;
          __payload_t *my =
            (__payload_t *) osip_list_get (config->video_codec, pos);

          while (!osip_list_eol (config->video_codec, pos))
            {
              my = (__payload_t *) osip_list_get (config->video_codec, pos);
              if (0 == strcmp (video_codec, my->payload))
                {
                  /* all media MUST have the same PROTO, PORT. */
                  sdp_message_m_media_add (*sdp, osip_strdup ("video"),
                                           osip_strdup (video_port),
                                           osip_strdup (my->number_of_port),
                                           osip_strdup (my->proto));
                  sdp_message_m_payload_add (*sdp, media_line,
                                             osip_strdup (my->payload));
                  if (my->a_rtpmap != NULL)
                    sdp_message_a_attribute_add (*sdp, media_line,
                                                 osip_strdup ("rtpmap"),
                                                 osip_strdup (my->a_rtpmap));
                  media_line++;
                  break;
                }
              pos++;
            }
        }
    }
  return 0;
}


int
osip_negotiation_sdp_message_put_on_hold (sdp_message_t * sdp)
{
  int pos;
  int pos_media = -1;
  char *rcvsnd;
  int recv_send = -1;

  pos = 0;
  rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
  while (rcvsnd != NULL)
    {
      if (rcvsnd != NULL && 0 == strcmp (rcvsnd, "sendonly"))
        {
          recv_send = 0;
      } else if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "recvonly")
                                    || 0 == strcmp (rcvsnd, "sendrecv")))
        {
          recv_send = 0;
          sprintf (rcvsnd, "sendonly");
        }
      pos++;
      rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
    }

  pos_media = 0;
  while (!sdp_message_endof_media (sdp, pos_media))
    {
      pos = 0;
      rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
      while (rcvsnd != NULL)
        {
          if (rcvsnd != NULL && 0 == strcmp (rcvsnd, "sendonly"))
            {
              recv_send = 0;
          } else if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "recvonly")
                                        || 0 == strcmp (rcvsnd, "sendrecv")))
            {
              recv_send = 0;
              sprintf (rcvsnd, "sendonly");
            }
          pos++;
          rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
        }
      pos_media++;
    }

  if (recv_send == -1)
    {
      /* we need to add a global attribute with a field set to "sendonly" */
      sdp_message_a_attribute_add (sdp, -1, osip_strdup ("sendonly"), NULL);
    }

  return 0;
}

int
osip_negotiation_sdp_message_put_off_hold (sdp_message_t * sdp)
{
  int pos;
  int pos_media = -1;
  char *rcvsnd;

  pos = 0;
  rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
  while (rcvsnd != NULL)
    {
      if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "sendonly")
                             || 0 == strcmp (rcvsnd, "recvonly")))
        {
          sprintf (rcvsnd, "sendrecv");
        }
      pos++;
      rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
    }

  pos_media = 0;
  while (!sdp_message_endof_media (sdp, pos_media))
    {
      pos = 0;
      rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
      while (rcvsnd != NULL)
        {
          if (rcvsnd != NULL && (0 == strcmp (rcvsnd, "sendonly")
                                 || 0 == strcmp (rcvsnd, "recvonly")))
            {
              sprintf (rcvsnd, "sendrecv");
            }
          pos++;
          rcvsnd = sdp_message_a_att_field_get (sdp, pos_media, pos);
        }
      pos_media++;
    }

  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1