/*
  eXosip - This is the eXtended osip library.
  Copyright (C) 2002, 2003  Aymeric MOIZARD  - jack@atosc.org
  
  eXosip is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
  
  eXosip 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 General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifdef ENABLE_MPATROL
#include <mpatrol.h>
#endif

#include "eXosip2.h"
#include <eXosip2/eXosip.h>

#include <osip2/osip_mt.h>
#include <osip2/osip_condv.h>

extern eXosip_t eXosip;

int ipv6_enable = 0;

static void *_eXosip_thread (void *arg);
static int _eXosip_execute (void);
static void _eXosip_keep_alive (void);

void
eXosip_enable_ipv6 (int _ipv6_enable)
{
  ipv6_enable = _ipv6_enable;
}

void
eXosip_masquerade_contact (const char *public_address, int port)
{
  if (public_address == NULL || public_address[0] == '\0')
    {
      memset (eXosip.net_interfaces[0].net_firewall_ip, '\0',
              sizeof (eXosip.net_interfaces[0].net_firewall_ip));
      memset (eXosip.net_interfaces[1].net_firewall_ip, '\0',
              sizeof (eXosip.net_interfaces[1].net_firewall_ip));
      memset (eXosip.net_interfaces[2].net_firewall_ip, '\0',
              sizeof (eXosip.net_interfaces[2].net_firewall_ip));
      return;
    }

  snprintf (eXosip.net_interfaces[0].net_firewall_ip,
            sizeof (eXosip.net_interfaces[0].net_firewall_ip), "%s",
            public_address);
  snprintf (eXosip.net_interfaces[1].net_firewall_ip,
            sizeof (eXosip.net_interfaces[1].net_firewall_ip), "%s",
            public_address);
  snprintf (eXosip.net_interfaces[2].net_firewall_ip,
            sizeof (eXosip.net_interfaces[2].net_firewall_ip), "%s",
            public_address);

  if (port > 0)
    {
      snprintf (eXosip.net_interfaces[0].net_port,
                sizeof (eXosip.net_interfaces[0].net_port), "%i", port);
      snprintf (eXosip.net_interfaces[1].net_port,
                sizeof (eXosip.net_interfaces[1].net_port), "%i", port);
      snprintf (eXosip.net_interfaces[2].net_port,
                sizeof (eXosip.net_interfaces[2].net_port), "%i", port);
    }
  return;
}

int
eXosip_force_masquerade_contact (const char *public_address)
{
  if (public_address == NULL || public_address[0] == '\0')
    {
      memset (eXosip.net_interfaces[0].net_firewall_ip, '\0',
              sizeof (eXosip.net_interfaces[0].net_firewall_ip));
      memset (eXosip.net_interfaces[1].net_firewall_ip, '\0',
              sizeof (eXosip.net_interfaces[1].net_firewall_ip));
      memset (eXosip.net_interfaces[2].net_firewall_ip, '\0',
              sizeof (eXosip.net_interfaces[2].net_firewall_ip));
      eXosip.forced_localip = 0;
      return 0;
    }
  eXosip.forced_localip = 1;
  snprintf (eXosip.net_interfaces[0].net_firewall_ip, 50, "%s", public_address);
  snprintf (eXosip.net_interfaces[1].net_firewall_ip, 50, "%s", public_address);
  snprintf (eXosip.net_interfaces[2].net_firewall_ip, 50, "%s", public_address);
  return 0;
}

int
eXosip_guess_localip (int family, char *address, int size)
{
  return eXosip_guess_ip_for_via (family, address, size);
}

int
eXosip_is_public_address (const char *c_address)
{
  return (0 != strncmp (c_address, "192.168", 7)
          && 0 != strncmp (c_address, "10.", 3)
          && 0 != strncmp (c_address, "172.16.", 7)
          && 0 != strncmp (c_address, "172.17.", 7)
          && 0 != strncmp (c_address, "172.18.", 7)
          && 0 != strncmp (c_address, "172.19.", 7)
          && 0 != strncmp (c_address, "172.20.", 7)
          && 0 != strncmp (c_address, "172.21.", 7)
          && 0 != strncmp (c_address, "172.22.", 7)
          && 0 != strncmp (c_address, "172.23.", 7)
          && 0 != strncmp (c_address, "172.24.", 7)
          && 0 != strncmp (c_address, "172.25.", 7)
          && 0 != strncmp (c_address, "172.26.", 7)
          && 0 != strncmp (c_address, "172.27.", 7)
          && 0 != strncmp (c_address, "172.28.", 7)
          && 0 != strncmp (c_address, "172.29.", 7)
          && 0 != strncmp (c_address, "172.30.", 7)
          && 0 != strncmp (c_address, "172.31.", 7)
          && 0 != strncmp (c_address, "169.254", 7));
}

void
eXosip_set_user_agent (const char *user_agent)
{
  osip_free (eXosip.user_agent);
  eXosip.user_agent = osip_strdup (user_agent);
}

void
eXosip_kill_transaction (osip_list_t * transactions)
{
  osip_transaction_t *transaction;

  if (!osip_list_eol (transactions, 0))
    {
      /* some transaction are still used by osip,
         transaction should be released by modules! */
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "module sfp: _osip_kill_transaction transaction should be released by modules!\n"));
    }

  while (!osip_list_eol (transactions, 0))
    {
      transaction = osip_list_get (transactions, 0);

      __eXosip_delete_jinfo (transaction);
      osip_transaction_free (transaction);
    }
}

void
eXosip_quit (void)
{
  jauthinfo_t *jauthinfo;
  eXosip_call_t *jc;
  eXosip_notify_t *jn;
  eXosip_subscribe_t *js;
  eXosip_reg_t *jreg;
  eXosip_pub_t *jpub;
  int i;
  int pos;

  if (eXosip.j_stop_ua==-1)
  {
		OSIP_TRACE (osip_trace
					(__FILE__, __LINE__, OSIP_WARNING, NULL,
					"eXosip: already stopped!\n"));
	  return;
  }

  eXosip.j_stop_ua = 1;         /* ask to quit the application */
  __eXosip_wakeup ();
  __eXosip_wakeup_event ();

  if (eXosip.j_thread!=NULL)
  {
    i = osip_thread_join ((struct osip_thread *) eXosip.j_thread);
    if (i != 0)
        {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_ERROR, NULL,
                    "eXosip: can't terminate thread!\n"));
        }
    osip_free ((struct osip_thread *) eXosip.j_thread);
  }

  jpipe_close (eXosip.j_socketctl);
  jpipe_close (eXosip.j_socketctl_event);

  osip_free (eXosip.user_agent);

  for (jc = eXosip.j_calls; jc != NULL; jc = eXosip.j_calls)
    {
      REMOVE_ELEMENT (eXosip.j_calls, jc);
      eXosip_call_free (jc);
    }

  for (js = eXosip.j_subscribes; js != NULL; js = eXosip.j_subscribes)
    {
      REMOVE_ELEMENT (eXosip.j_subscribes, js);
      eXosip_subscribe_free (js);
    }

  for (jn = eXosip.j_notifies; jn != NULL; jn = eXosip.j_notifies)
    {
      REMOVE_ELEMENT (eXosip.j_notifies, jn);
      eXosip_notify_free (jn);
    }

  osip_mutex_destroy ((struct osip_mutex *) eXosip.j_mutexlock);
#if !defined (_WIN32_WCE)
  osip_cond_destroy ((struct osip_cond *) eXosip.j_cond);
#endif

  if (eXosip.net_interfaces[0].net_socket)
    {
      close (eXosip.net_interfaces[0].net_socket);
      eXosip.net_interfaces[0].net_socket = -1;
    }
  if (eXosip.net_interfaces[1].net_socket)
    {
      close (eXosip.net_interfaces[1].net_socket);
      eXosip.net_interfaces[1].net_socket = -1;
    }
  if (eXosip.net_interfaces[2].net_socket)
    {
      close (eXosip.net_interfaces[2].net_socket);
      eXosip.net_interfaces[2].net_socket = -1;
    }

  for (pos = 0; pos < EXOSIP_MAX_SOCKETS; pos++)
    {
      if (eXosip.net_interfaces[0].net_socket_tab[pos].socket != 0)
        close (eXosip.net_interfaces[0].net_socket_tab[pos].socket);
      if (eXosip.net_interfaces[1].net_socket_tab[pos].socket != 0)
        close (eXosip.net_interfaces[1].net_socket_tab[pos].socket);
      if (eXosip.net_interfaces[2].net_socket_tab[pos].socket != 0)
        close (eXosip.net_interfaces[2].net_socket_tab[pos].socket);
    }

  for (jreg = eXosip.j_reg; jreg != NULL; jreg = eXosip.j_reg)
    {
      REMOVE_ELEMENT (eXosip.j_reg, jreg);
      eXosip_reg_free (jreg);
    }

  for (jpub = eXosip.j_pub; jpub != NULL; jpub = eXosip.j_pub)
    {
      REMOVE_ELEMENT (eXosip.j_pub, jpub);
      _eXosip_pub_free (jpub);
    }

  while (!osip_list_eol (eXosip.j_transactions, 0))
    {
      osip_transaction_t *tr =
        (osip_transaction_t *) osip_list_get (eXosip.j_transactions, 0);
      if (tr->state == IST_TERMINATED || tr->state == ICT_TERMINATED
          || tr->state == NICT_TERMINATED || tr->state == NIST_TERMINATED)
        {
          OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
                                  "Release a terminated transaction\n"));
          osip_list_remove (eXosip.j_transactions, 0);
          __eXosip_delete_jinfo (tr);
          osip_transaction_free (tr);
      } else
        {
          osip_list_remove (eXosip.j_transactions, 0);
          __eXosip_delete_jinfo (tr);
          osip_transaction_free (tr);
        }
    }

  osip_free (eXosip.j_transactions);

  eXosip_kill_transaction (&eXosip.j_osip->osip_ict_transactions);
  eXosip_kill_transaction (&eXosip.j_osip->osip_nict_transactions);
  eXosip_kill_transaction (&eXosip.j_osip->osip_ist_transactions);
  eXosip_kill_transaction (&eXosip.j_osip->osip_nist_transactions);
  osip_release (eXosip.j_osip);

  {
    eXosip_event_t *ev;

    for (ev = osip_fifo_tryget (eXosip.j_events); ev != NULL;
         ev = osip_fifo_tryget (eXosip.j_events))
      eXosip_event_free (ev);
  }

  osip_fifo_free (eXosip.j_events);

  for (jauthinfo = eXosip.authinfos; jauthinfo != NULL;
       jauthinfo = eXosip.authinfos)
    {
      REMOVE_ELEMENT (eXosip.authinfos, jauthinfo);
      osip_free (jauthinfo);
    }

  memset (&eXosip, 0, sizeof (eXosip));
  eXosip.j_stop_ua = -1;
  return;
}

int
eXosip_set_socket (int transport, int socket, int port)
{
  if (eXosip.net_interfaces[0].net_socket > 0)
    close (eXosip.net_interfaces[0].net_socket);

  eXosip.net_interfaces[0].net_protocol = transport;
  eXosip.net_interfaces[0].net_ip_family = AF_INET;
  eXosip.net_interfaces[0].net_socket = socket;
  snprintf (eXosip.net_interfaces[0].net_port,
            sizeof (eXosip.net_interfaces[0].net_port), "%i", port);

  eXosip.j_thread = (void *) osip_thread_create (20000, _eXosip_thread, NULL);
  if (eXosip.j_thread == NULL)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: Cannot start thread!\n"));
      return -1;
    }
  return 0;
}

#ifdef IPV6_V6ONLY
int
setsockopt_ipv6only (int sock)
{
  int on = 1;

  return setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, sizeof (on));
}
#endif /* IPV6_V6ONLY */

int
eXosip_listen_addr (int transport, const char *addr, int port, int family,
                    int secure)
{
  int res;
  struct addrinfo *addrinfo = NULL;
  struct addrinfo *curinfo;
  const char *node = addr;
  int sock = -1;
  struct eXosip_net *net_int;
  char localip[256];

  if (transport == IPPROTO_UDP)
    net_int = &eXosip.net_interfaces[0];
  else if (transport == IPPROTO_TCP)
    net_int = &eXosip.net_interfaces[1];
  else if (transport == IPPROTO_TCP && secure != 0)
    net_int = &eXosip.net_interfaces[2];
  else
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: unknown protocol (use IPPROTO_UDP or IPPROTO_TCP!\n"));
      return -1;
    }

  if (eXosip.http_port)
    {
      /* USE TUNNEL CAPABILITY */
      transport = IPPROTO_TCP;
    }

  if (port < 0)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: port must be higher than 0!\n"));
      return -1;
    }

  net_int->net_ip_family = family;
  if (family == AF_INET6)
    {
      ipv6_enable = 1;
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_INFO1, NULL,
                   "IPv6 is enabled. Pls report bugs\n"));
    }

  eXosip_guess_localip (net_int->net_ip_family, localip, sizeof (localip));
  if (localip[0] == '\0')
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: No ethernet interface found!\n"));
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: using 127.0.0.1 (debug mode)!\n"));
      /* we should always fallback on something. The linphone user will surely
         start linphone BEFORE setting its dial up connection. */
    }

  if (!node)
    {
      node = ipv6_enable ? "::" : "0.0.0.0";
    }


  res = eXosip_get_addrinfo (&addrinfo, node, port, transport);
  if (res)
    return -1;

  for (curinfo = addrinfo; curinfo; curinfo = curinfo->ai_next)
    {
      socklen_t len;

      if (curinfo->ai_protocol && curinfo->ai_protocol != transport)
        {
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_INFO3, NULL,
                       "eXosip: Skipping protocol %d\n", curinfo->ai_protocol));
          continue;
        }

      sock = (int) socket (curinfo->ai_family, curinfo->ai_socktype,
                           curinfo->ai_protocol);
      if (sock < 0)
        {
#if !defined(_WIN32_WCE)
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_ERROR, NULL,
                       "eXosip: Cannot create socket!\n", strerror (errno)));
#endif

          continue;
        }

      if (eXosip.http_port)
        {
          break;
        }

      if (curinfo->ai_family == AF_INET6)
        {
#ifdef IPV6_V6ONLY
          if (setsockopt_ipv6only (sock))
            {
              close (sock);
              sock = -1;
              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_ERROR, NULL,
                           "eXosip: Cannot set socket option!\n",
                           strerror (errno)));
              continue;
            }
#endif /* IPV6_V6ONLY */
        }

      res = bind (sock, curinfo->ai_addr, curinfo->ai_addrlen);
      if (res < 0)
        {
#if !defined(_WIN32_WCE)

          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_ERROR, NULL,
                       "eXosip: Cannot bind socket node:%s family:%d %s\n",
                       node, curinfo->ai_family, strerror (errno)));
#endif

          close (sock);
          sock = -1;
          continue;
        }
      len = sizeof (net_int->ai_addr);
      res = getsockname (sock, (struct sockaddr *) &net_int->ai_addr, &len);
      if (res != 0)
        {
#if !defined(_WIN32_WCE)

          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_ERROR, NULL,
                       "eXosip: Cannot get socket name (%s)\n", strerror (errno)));
#endif

          memcpy (&net_int->ai_addr, curinfo->ai_addr, curinfo->ai_addrlen);
        }

      if (transport != IPPROTO_UDP)
        {
          res = listen (sock, SOMAXCONN);
          if (res < 0)
            {
#if !defined(_WIN32_WCE)

              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_ERROR, NULL,
                           "eXosip: Cannot bind socket node:%s family:%d %s\n",
                           node, curinfo->ai_family, strerror (errno)));
#endif

              close (sock);
              sock = -1;
              continue;
            }
        }

      break;
    }

  freeaddrinfo (addrinfo);

  if (sock < 0)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: Cannot bind on port: %i\n", port));
      return -1;
    }

  if (eXosip.http_port)
    net_int->net_protocol = IPPROTO_UDP;
  else
    net_int->net_protocol = transport;
  net_int->net_socket = sock;

  if (port == 0)
    {
      /* get port number from socket */
      if (ipv6_enable == 0)
        port = ntohs (((struct sockaddr_in *) &net_int->ai_addr)->sin_port);
      else
        port = ntohs (((struct sockaddr_in6 *) &net_int->ai_addr)->sin6_port);
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_INFO1, NULL,
                   "eXosip: Binding on port %i!\n", port));
    }

  snprintf (net_int->net_port, sizeof (net_int->net_port) - 1, "%i", port);



  if (eXosip.http_port)
    {
      /* only ipv4 */
      struct sockaddr_in _addr;
      char http_req[2048];
      char http_reply[2048];
      int len;

      _addr.sin_port = (unsigned short) htons (eXosip.http_port);
      _addr.sin_addr.s_addr = inet_addr (eXosip.http_proxy);
      _addr.sin_family = PF_INET;

      if (connect
          (net_int->net_socket, (struct sockaddr *) &_addr, sizeof (_addr)) == -1)
        {
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_ERROR, NULL,
                       "eXosip: Failed to connect to http server on %s:%i!\n",
                       eXosip.http_proxy, port));
          return -1;
        }

      sprintf (http_req, "GET / HTTP/1.1\r\nUdpHost: %s:%d\r\n\r\n",
               eXosip.http_outbound_proxy, 5060);

      len = send (net_int->net_socket, http_req, (int) strlen (http_req), 0);

      if (len < 0)
        return -1;

      osip_usleep (50000);

      if ((len =
           recv (net_int->net_socket, http_reply, sizeof (http_reply), 0)) > 0)
        http_reply[len] = '\0';
      else
        return -1;

      if (strncmp (http_reply, "HTTP/1.0 200 OK\r\n", 17) == 0
          || strncmp (http_reply, "HTTP/1.1 200 OK\r\n", 17) == 0)
        {
      } else
        return -1;
    }

  eXosip.j_thread = (void *) osip_thread_create (20000, _eXosip_thread, NULL);
  if (eXosip.j_thread == NULL)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: Cannot start thread!\n"));
      return -1;
    }
  return 0;
}

int
eXosip_init (void)
{
  osip_t *osip;

  memset (&eXosip, 0, sizeof (eXosip));

  eXosip.remove_preloadedroute=1;
  snprintf(eXosip.ipv4_for_gateway, 256, "%s", "217.12.3.11");

#ifdef WIN32
  /* Initializing windows socket library */
  {
    WORD wVersionRequested;
    WSADATA wsaData;
    int i;

    wVersionRequested = MAKEWORD (1, 1);
    i = WSAStartup (wVersionRequested, &wsaData);
    if (i != 0)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_WARNING, NULL,
                     "eXosip: Unable to initialize WINSOCK, reason: %d\n", i));
        /* return -1; It might be already initilized?? */
      }
  }
#endif

  eXosip.user_agent = osip_strdup ("eXosip/" EXOSIP_VERSION);

  eXosip.j_calls = NULL;
  eXosip.j_stop_ua = 0;
  eXosip.j_thread = NULL;
  eXosip.j_transactions = (osip_list_t *) osip_malloc (sizeof (osip_list_t));
  osip_list_init (eXosip.j_transactions);
  eXosip.j_reg = NULL;

#if !defined (_WIN32_WCE)
  eXosip.j_cond = (struct osip_cond *) osip_cond_init ();
#endif

  eXosip.j_mutexlock = (struct osip_mutex *) osip_mutex_init ();

  if (-1 == osip_init (&osip))
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "eXosip: Cannot initialize osip!\n"));
      return -1;
    }

  osip_set_application_context (osip, &eXosip);

  eXosip_set_callbacks (osip);

  eXosip.j_osip = osip;

  /* open a TCP socket to wake up the application when needed. */
  eXosip.j_socketctl = jpipe ();
  if (eXosip.j_socketctl == NULL)
    return -1;

  eXosip.j_socketctl_event = jpipe ();
  if (eXosip.j_socketctl_event == NULL)
    return -1;

  /* To be changed in osip! */
  eXosip.j_events = (osip_fifo_t *) osip_malloc (sizeof (osip_fifo_t));
  osip_fifo_init (eXosip.j_events);

  eXosip.use_rport = 1;
  return 0;
}


static int
_eXosip_execute (void)
{
  struct timeval lower_tv;
  int i;

  osip_timers_gettimeout (eXosip.j_osip, &lower_tv);
  if (lower_tv.tv_sec > 15)
    {
      lower_tv.tv_sec = 15;
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_INFO2, NULL,
                   "eXosip: Reseting timer to 15s before waking up!\n"));
  } else
    {
      /*  add a small amount of time on windows to avoid
         waking up too early. (probably a bad time precision) */
      if (lower_tv.tv_usec < 900000)
        lower_tv.tv_usec = 100000;      /* add 10ms */
      else
        {
          lower_tv.tv_usec = 10000;     /* add 10ms */
          lower_tv.tv_sec++;
        }
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_INFO2, NULL,
                   "eXosip: timer sec:%i usec:%i!\n",
                   lower_tv.tv_sec, lower_tv.tv_usec));
    }
  i = eXosip_read_message (1, lower_tv.tv_sec, lower_tv.tv_usec);

  if (i == -2)
    {
      return -2;
    }

  eXosip_lock ();
  osip_timers_ict_execute (eXosip.j_osip);
  osip_timers_nict_execute (eXosip.j_osip);
  osip_timers_ist_execute (eXosip.j_osip);
  osip_timers_nist_execute (eXosip.j_osip);

  osip_ict_execute (eXosip.j_osip);
  osip_nict_execute (eXosip.j_osip);
  osip_ist_execute (eXosip.j_osip);
  osip_nist_execute (eXosip.j_osip);

  /* free all Calls that are in the TERMINATED STATE? */
  eXosip_release_terminated_calls ();
  eXosip_release_terminated_registrations ();
  eXosip_release_unused_transactions();

  eXosip_unlock ();


  if (eXosip.keep_alive > 0)
    {
      _eXosip_keep_alive ();
    }

  return 0;
}

int
eXosip_set_option (eXosip_option opt, const void *value)
{
  int val;
  char *tmp;

  switch (opt)
    {
      case EXOSIP_OPT_UDP_KEEP_ALIVE:
        val = *((int *) value);
        eXosip.keep_alive = val;        /* value in ms */
        break;
      case EXOSIP_OPT_UDP_LEARN_PORT:
        val = *((int *) value);
        eXosip.learn_port = val;        /* 1 to learn port */
        break;
      case EXOSIP_OPT_SET_HTTP_TUNNEL_PORT:
        val = *((int *) value);
        eXosip.http_port = val; /* value in ms */
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "eXosip option set: http_port:%i!\n", eXosip.http_port));
        break;
      case EXOSIP_OPT_SET_HTTP_TUNNEL_PROXY:
        tmp = (char *) value;
        memset (eXosip.http_proxy, '\0', sizeof (eXosip.http_proxy));
        if (tmp != NULL && tmp[0] != '\0')
          strncpy (eXosip.http_proxy, tmp, sizeof (eXosip.http_proxy)); /* value in proxy:port */
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "eXosip option set: http_proxy:%s!\n", eXosip.http_proxy));
        break;
      case EXOSIP_OPT_SET_HTTP_OUTBOUND_PROXY:
        tmp = (char *) value;
        memset (eXosip.http_outbound_proxy, '\0',
                sizeof (eXosip.http_outbound_proxy));
        if (tmp != NULL && tmp[0] != '\0')
          strncpy (eXosip.http_outbound_proxy, tmp, sizeof (eXosip.http_outbound_proxy));       /* value in proxy:port */
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "eXosip option set: http_outbound_proxy:%s!\n",
                     eXosip.http_outbound_proxy));
        break;

      case EXOSIP_OPT_DONT_SEND_101:
        val = *((int *) value);
        eXosip.dontsend_101 = val;        /* 0 to disable */
        break;

      case EXOSIP_OPT_USE_RPORT:
        val = *((int *) value);
        eXosip.use_rport = val;       /* 0 to disable (for broken NAT only?) */
        break;

      case EXOSIP_OPT_SET_IPV4_FOR_GATEWAY:
        tmp = (char *) value;
        memset (eXosip.ipv4_for_gateway, '\0',
                sizeof (eXosip.ipv4_for_gateway));
        if (tmp != NULL && tmp[0] != '\0')
          strncpy (eXosip.ipv4_for_gateway, tmp, sizeof (eXosip.ipv4_for_gateway));
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "eXosip option set: ipv4_for_gateway:%s!\n",
                     eXosip.ipv4_for_gateway));
        break;

    }
  return 0;
}

static void
_eXosip_keep_alive (void)
{
  static struct timeval mtimer = { 0, 0 };

  eXosip_reg_t *jr;
  struct eXosip_net *net;
  char buf[4] = "jaK";
  struct timeval now;

  osip_gettimeofday (&now, NULL);

  if (mtimer.tv_sec == 0 && mtimer.tv_usec == 0)
    {
      /* first init */
      osip_gettimeofday (&mtimer, NULL);
      add_gettimeofday (&mtimer, eXosip.keep_alive);
    }

  if (osip_timercmp (&now, &mtimer, <))
    {
      return;                   /* not yet time */
    }

  /* reset timer */
  osip_gettimeofday (&mtimer, NULL);
  add_gettimeofday (&mtimer, eXosip.keep_alive);

  net = &eXosip.net_interfaces[0];
  if (net == NULL)
    {
      return;
    }

  for (jr = eXosip.j_reg; jr != NULL; jr = jr->next)
    {
      if (jr->len > 0)
        {
          if (sendto (net->net_socket, (const void *) buf, 4, 0,
                      (struct sockaddr *) &(jr->addr), jr->len) > 0)
            {
              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_INFO1, NULL,
                           "eXosip: Keep Alive sent on UDP!\n"));
            }
        }
    }
}

void *
_eXosip_thread (void *arg)
{
  int i;

  while (eXosip.j_stop_ua == 0)
    {
      i = _eXosip_execute ();
      if (i == -2)
        osip_thread_exit ();
    }
  osip_thread_exit ();
  return NULL;
}


syntax highlighted by Code2HTML, v. 0.9.1