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

#include "fsm.h"
#include "xixt.h"

#ifdef OSIP_MT
static struct osip_mutex *ict_fastmutex;
static struct osip_mutex *ist_fastmutex;
static struct osip_mutex *nict_fastmutex;
static struct osip_mutex *nist_fastmutex;
#endif


#include <osip2/osip_dialog.h>
#ifdef OSIP_MT
static struct osip_mutex *ixt_fastmutex;
#endif

static int __osip_global_init (void);
static void __osip_global_free (void);
static int increase_ref_count (void);
static void decrease_ref_count (void);

static int
__osip_global_init ()
{
  /* load the fsm configuration */
#ifndef MINISIZE
  __ict_load_fsm ();
  __ist_load_fsm ();
  __nict_load_fsm ();
  __nist_load_fsm ();
#endif

  /* load the parser configuration */
  parser_init ();

#ifdef OSIP_MT
  ict_fastmutex = osip_mutex_init ();
  ist_fastmutex = osip_mutex_init ();
  nict_fastmutex = osip_mutex_init ();
  nist_fastmutex = osip_mutex_init ();

  ixt_fastmutex = osip_mutex_init ();

#endif
  return 0;
}

static void
__osip_global_free ()
{
#ifndef MINISIZE
  __ict_unload_fsm ();
  __ist_unload_fsm ();
  __nict_unload_fsm ();
  __nist_unload_fsm ();
#endif

#ifdef OSIP_MT
  osip_mutex_destroy (ict_fastmutex);
  osip_mutex_destroy (ist_fastmutex);
  osip_mutex_destroy (nict_fastmutex);
  osip_mutex_destroy (nist_fastmutex);

  osip_mutex_destroy (ixt_fastmutex);
#endif
}

void
osip_response_get_destination (osip_message_t * response, char **address,
                               int *portnum)
{
  osip_via_t *via;
  char *host = NULL;
  int port = 0;

  via = (osip_via_t *) osip_list_get (&response->vias, 0);
  if (via)
    {
      osip_generic_param_t *maddr;
      osip_generic_param_t *received;
      osip_generic_param_t *rport;

      osip_via_param_get_byname (via, "maddr", &maddr);
      osip_via_param_get_byname (via, "received", &received);
      osip_via_param_get_byname (via, "rport", &rport);
      /* 1: user should not use the provided information
         (host and port) if they are using a reliable
         transport. Instead, they should use the already
         open socket attached to this transaction. */
      /* 2: check maddr and multicast usage */
      if (maddr != NULL)
        host = maddr->gvalue;
      /* we should check if this is a multicast address and use
         set the "ttl" in this case. (this must be done in the
         UDP message (not at the SIP layer) */
      else if (received != NULL)
        host = received->gvalue;
      else
        host = via->host;

      if (rport == NULL || rport->gvalue == NULL)
        {
          if (via->port != NULL)
            port = osip_atoi (via->port);
          else
            port = 5060;
      } else
        port = osip_atoi (rport->gvalue);
    }
  *portnum = port;
  if (host != NULL)
    *address = osip_strdup (host);
  else
    *address = NULL;
}


int
osip_ixt_lock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_lock (ixt_fastmutex);
#else
  return 0;
#endif
}

int
osip_ixt_unlock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_unlock (ixt_fastmutex);
#else
  return 0;
#endif
}

/* these are for transactions that would need retransmission not handled by state machines */
void
osip_add_ixt (osip_t * osip, ixt_t * ixt)
{
  /* add in list osip_t->ixt */
  osip_ixt_lock (osip);
  osip_list_add (&osip->ixt_retransmissions, (void *) ixt, 0);
  osip_ixt_unlock (osip);
}

void
osip_remove_ixt (osip_t * osip, ixt_t * ixt)
{
  int i;
  int found = 0;
  ixt_t *tmp;

  /* add in list osip_t->ixt */
  osip_ixt_lock (osip);
  for (i = 0; !osip_list_eol (&osip->ixt_retransmissions, i); i++)
    {
      tmp = (ixt_t *) osip_list_get (&osip->ixt_retransmissions, i);
      if (tmp == ixt)
        {
          osip_list_remove (&osip->ixt_retransmissions, i);
          found = 1;
          break;
        }
    }
  osip_ixt_unlock (osip);
}

int
ixt_init (ixt_t ** ixt)
{
  ixt_t *pixt;

  *ixt = pixt = (ixt_t *) osip_malloc (sizeof (ixt_t));
  if (pixt == NULL)
    return -1;
  pixt->dialog = NULL;
  pixt->msg2xx = NULL;
  pixt->ack = NULL;
  pixt->interval = DEFAULT_T1;
  osip_gettimeofday(&pixt->start, NULL);
  add_gettimeofday(&pixt->start, pixt->interval+10);
  pixt->counter = 10;
  pixt->dest = NULL;
  pixt->port = 5060;
  pixt->sock = -1;
  return 0;
}

void
ixt_free (ixt_t * ixt)
{
  osip_message_free (ixt->ack);
  osip_message_free (ixt->msg2xx);
  osip_free (ixt->dest);
  osip_free (ixt);
}

/* usefull for UAs */
void
osip_start_200ok_retransmissions (osip_t * osip, osip_dialog_t * dialog,
                                  osip_message_t * msg200ok, int sock)
{
  ixt_t *ixt;

  ixt_init (&ixt);
  ixt->dialog = dialog;
  osip_message_clone (msg200ok, &ixt->msg2xx);
  ixt->sock = sock;
  osip_response_get_destination (msg200ok, &ixt->dest, &ixt->port);
  osip_add_ixt (osip, ixt);
}

void
osip_start_ack_retransmissions (osip_t * osip, osip_dialog_t * dialog,
                                osip_message_t * ack, char *dest, int port,
                                int sock)
{
  int i;
  ixt_t *ixt;

  i = ixt_init (&ixt);
  if (i != 0)
    return;
  ixt->dialog = dialog;
  osip_message_clone (ack, &ixt->ack);
  ixt->dest = osip_strdup (dest);
  ixt->port = port;
  ixt->sock = sock;
  osip_add_ixt (osip, ixt);
}

/* we stop the 200ok when receiving the corresponding ack */
struct osip_dialog *
osip_stop_200ok_retransmissions (osip_t * osip, osip_message_t * ack)
{
  osip_dialog_t *dialog = NULL;
  int i;
  ixt_t *ixt;

  osip_ixt_lock (osip);
  for (i = 0; !osip_list_eol (&osip->ixt_retransmissions, i); i++)
    {
      ixt = (ixt_t *) osip_list_get (&osip->ixt_retransmissions, i);
      if (osip_dialog_match_as_uas (ixt->dialog, ack) == 0)
        {
          osip_list_remove (&osip->ixt_retransmissions, i);
          dialog = ixt->dialog;
          ixt_free (ixt);
          break;
        }
    }
  osip_ixt_unlock (osip);
  return dialog;
}

/* when a dialog is destroyed by the application,
   it is safer to remove all ixt that are related to it */
void
osip_stop_retransmissions_from_dialog (osip_t * osip, osip_dialog_t * dialog)
{
  int i;
  ixt_t *ixt;

  osip_ixt_lock (osip);
  for (i = 0; !osip_list_eol (&osip->ixt_retransmissions, i); i++)
    {
      ixt = (ixt_t *) osip_list_get (&osip->ixt_retransmissions, i);
      if (ixt->dialog == dialog)
        {
          osip_list_remove (&osip->ixt_retransmissions, i);
          ixt_free (ixt);
          i--;
        }
    }
  osip_ixt_unlock (osip);
}

void
ixt_retransmit (osip_t * osip, ixt_t * ixt, struct timeval *current)
{
  if (osip_timercmp(current, &ixt->start, >))
    {
      ixt->interval = ixt->interval * 2;
      if (ixt->interval > 4000)
         ixt->interval = 4000;
      add_gettimeofday (&ixt->start, ixt->interval);
      if (ixt->ack != NULL)
        osip->cb_send_message (NULL, ixt->ack, ixt->dest, ixt->port, ixt->sock);
      else if (ixt->msg2xx != NULL)
        osip->cb_send_message (NULL, ixt->msg2xx, ixt->dest, ixt->port, ixt->sock);
      ixt->counter--;
    }
}

void
osip_retransmissions_execute (osip_t * osip)
{
  int i;
  ixt_t *ixt;
  struct timeval current;

  osip_gettimeofday (&current, NULL);

  osip_ixt_lock (osip);
  for (i = 0; !osip_list_eol (&osip->ixt_retransmissions, i); i++)
    {
      ixt = (ixt_t *) osip_list_get (&osip->ixt_retransmissions, i);
      ixt_retransmit (osip, ixt, &current);
      if (ixt->counter == 0)
        {
          /* remove it */
          osip_list_remove (&osip->ixt_retransmissions, i);
          ixt_free (ixt);
          i--;
        }
    }
  osip_ixt_unlock (osip);
}

int
osip_ict_lock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_lock (ict_fastmutex);
#else
  return 0;
#endif
}

int
osip_ict_unlock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_unlock (ict_fastmutex);
#else
  return 0;
#endif
}

int
osip_ist_lock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_lock (ist_fastmutex);
#else
  return 0;
#endif
}

int
osip_ist_unlock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_unlock (ist_fastmutex);
#else
  return 0;
#endif
}

int
osip_nict_lock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_lock (nict_fastmutex);
#else
  return 0;
#endif
}

int
osip_nict_unlock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_unlock (nict_fastmutex);
#else
  return 0;
#endif
}

int
osip_nist_lock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_lock (nist_fastmutex);
#else
  return 0;
#endif
}

int
osip_nist_unlock (osip_t * osip)
{
#ifdef OSIP_MT
  return osip_mutex_unlock (nist_fastmutex);
#else
  return 0;
#endif
}

#if defined(HAVE_DICT_DICT_H)
#define HSIZE           200

unsigned s_hash (const unsigned char *p);

unsigned
s_hash (const unsigned char *p)
{
  unsigned hash = 0;

  while (*p)
    {
      hash *= 31;
      hash ^= *p++;
    }
  return hash;
}
#endif

int
__osip_add_ict (osip_t * osip, osip_transaction_t * ict)
{
#ifdef OSIP_MT
  osip_mutex_lock (ict_fastmutex);
#endif
#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv = -99;

    osip_via_param_get_byname (ict->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      rv = dict_insert (osip->osip_ict_hastable,
                        b_request->gvalue, (void *) ict, FALSE);
#if 0
    else
      rv = dict_insert (osip->osip_ict_hastable,
                        b_request->gvalue, (void *) ict, FALSE);
#endif

    if (rv == 0)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "New key inserted in ict hastable `%s'\n",
                     b_request->gvalue));
    } else if (rv != -99)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_WARNING, NULL,
                     "already inserted `%s'\n", b_request->gvalue));
      }
  }
#endif
  osip_list_add (&osip->osip_ict_transactions, ict, -1);
#ifdef OSIP_MT
  osip_mutex_unlock (ict_fastmutex);
#endif
  return 0;
}

int
__osip_add_ist (osip_t * osip, osip_transaction_t * ist)
{
#ifdef OSIP_MT
  osip_mutex_lock (ist_fastmutex);
#endif
#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv = -99;

    osip_via_param_get_byname (ist->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      rv = dict_insert (osip->osip_ist_hastable,
                        b_request->gvalue, (void *) ist, FALSE);
    else
#if 0
      rv = dict_insert (osip->osip_ist_hastable,
                        b_request->gvalue, (void *) ist, FALSE);
#endif
    if (rv == 0)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "New key inserted in ist hastable `%s'\n",
                     b_request->gvalue));
    } else if (rv != -99)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_WARNING, NULL,
                     "already inserted `%s'\n", b_request->gvalue));
      }
  }
#endif
  osip_list_add (&osip->osip_ist_transactions, ist, -1);
#ifdef OSIP_MT
  osip_mutex_unlock (ist_fastmutex);
#endif
  return 0;
}

int
__osip_add_nict (osip_t * osip, osip_transaction_t * nict)
{
#ifdef OSIP_MT
  osip_mutex_lock (nict_fastmutex);
#endif
#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv = -99;

    osip_via_param_get_byname (nict->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      rv = dict_insert (osip->osip_nict_hastable,
                        b_request->gvalue, (void *) nict, FALSE);
#if 0
    else
      rv = dict_insert (osip->osip_nict_hastable,
                        b_request->gvalue, (void *) nict, FALSE);
#endif

    if (rv == 0)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "New key inserted in nict hastable `%s'\n",
                     b_request->gvalue));
    } else if (rv != -99)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_WARNING, NULL,
                     "already inserted `%s'\n", b_request->gvalue));
      }
  }
#endif
  osip_list_add (&osip->osip_nict_transactions, nict, -1);
#ifdef OSIP_MT
  osip_mutex_unlock (nict_fastmutex);
#endif
  return 0;
}

int
__osip_add_nist (osip_t * osip, osip_transaction_t * nist)
{
#ifdef OSIP_MT
  osip_mutex_lock (nist_fastmutex);
#endif
#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv = -99;

    osip_via_param_get_byname (nist->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      rv = dict_insert (osip->osip_nist_hastable,
                        b_request->gvalue, (void *) nist, FALSE);
#if 0
    else
      rv = dict_insert (osip->osip_nist_hastable,
                        b_request->gvalue, (void *) nist, FALSE);
#endif
    if (rv == 0)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_INFO1, NULL,
                     "New key inserted in ict hastable `%s'\n",
                     b_request->gvalue));
    } else if (rv != -99)
      {
        OSIP_TRACE (osip_trace
                    (__FILE__, __LINE__, OSIP_WARNING, NULL,
                     "already inserted `%s'\n", b_request->gvalue));
      }
  }
#endif
  osip_list_add (&osip->osip_nist_transactions, nist, -1);
#ifdef OSIP_MT
  osip_mutex_unlock (nist_fastmutex);
#endif
  return 0;
}

int
osip_remove_transaction (osip_t * osip, osip_transaction_t * tr)
{
  int i = -1;

  if (tr == NULL)
    return -1;
  if (tr->ctx_type == ICT)
    i = __osip_remove_ict_transaction (osip, tr);
  else if (tr->ctx_type == IST)
    i = __osip_remove_ist_transaction (osip, tr);
  else if (tr->ctx_type == NICT)
    i = __osip_remove_nict_transaction (osip, tr);
  else if (tr->ctx_type == NIST)
    i = __osip_remove_nist_transaction (osip, tr);
  else
    return -1;
  return i;
}

int
__osip_remove_ict_transaction (osip_t * osip, osip_transaction_t * ict)
{
  osip_list_iterator_t iterator;
  osip_transaction_t *tmp;

#ifdef OSIP_MT
  osip_mutex_lock (ict_fastmutex);
#endif

#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv;

    osip_via_param_get_byname (ict->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      {
        rv = dict_remove (osip->osip_ict_hastable, b_request->gvalue, TRUE);
        if (rv == 0)
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_INFO1, NULL,
                         "New key deleted in ict hastable `%s'\n",
                         b_request->gvalue));
        } else
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_WARNING, NULL,
                         "key not removed `%s'\n", b_request->gvalue));
          }
      }
  }
#endif

  tmp =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (tmp->transactionid == ict->transactionid)
        {
          osip_list_iterator_remove (&iterator);
#ifdef OSIP_MT
          osip_mutex_unlock (ict_fastmutex);
#endif
          return 0;
        }
      tmp = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ict_fastmutex);
#endif
  return -1;
}

int
__osip_remove_ist_transaction (osip_t * osip, osip_transaction_t * ist)
{
  osip_list_iterator_t iterator;
  osip_transaction_t *tmp;

#ifdef OSIP_MT
  osip_mutex_lock (ist_fastmutex);
#endif

#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv;

    osip_via_param_get_byname (ist->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      {
        rv = dict_remove (osip->osip_ist_hastable, b_request->gvalue, TRUE);

        if (rv == 0)
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_INFO1, NULL,
                         "New key deleted in ist hastable `%s'\n",
                         b_request->gvalue));
        } else
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_WARNING, NULL,
                         "key not removed `%s'\n", b_request->gvalue));
          }
      }
  }
#endif

  tmp =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (tmp->transactionid == ist->transactionid)
        {
          osip_list_iterator_remove (&iterator);
#ifdef OSIP_MT
          osip_mutex_unlock (ist_fastmutex);
#endif
          return 0;
        }
      tmp = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ist_fastmutex);
#endif
  return -1;
}

int
__osip_remove_nict_transaction (osip_t * osip, osip_transaction_t * nict)
{
  osip_list_iterator_t iterator;
  osip_transaction_t *tmp;

#ifdef OSIP_MT
  osip_mutex_lock (nict_fastmutex);
#endif

#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv;

    osip_via_param_get_byname (nict->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      {
        rv = dict_remove (osip->osip_nict_hastable, b_request->gvalue, TRUE);

        if (rv == 0)
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_INFO1, NULL,
                         "New key deleted in nict hastable `%s'\n",
                         b_request->gvalue));
        } else
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_WARNING, NULL,
                         "key not removed `%s'\n", b_request->gvalue));
          }
      }
  }
#endif

  tmp =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (tmp->transactionid == nict->transactionid)
        {
          osip_list_iterator_remove (&iterator);
#ifdef OSIP_MT
          osip_mutex_unlock (nict_fastmutex);
#endif
          return 0;
        }
      tmp = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nict_fastmutex);
#endif
  return -1;
}

int
__osip_remove_nist_transaction (osip_t * osip, osip_transaction_t * nist)
{
  osip_list_iterator_t iterator;
  osip_transaction_t *tmp;

#ifdef OSIP_MT
  osip_mutex_lock (nist_fastmutex);
#endif

#if defined(HAVE_DICT_DICT_H)
  {
    osip_generic_param_t *b_request = NULL;
    int rv;

    osip_via_param_get_byname (nist->topvia, "branch", &b_request);
    if (b_request != NULL && b_request->gvalue != NULL)
      {
        rv = dict_remove (osip->osip_nist_hastable, b_request->gvalue, TRUE);

        if (rv == 0)
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_INFO1, NULL,
                         "New key deleted in ict hastable `%s'\n",
                         b_request->gvalue));
        } else
          {
            OSIP_TRACE (osip_trace
                        (__FILE__, __LINE__, OSIP_WARNING, NULL,
                         "key not removed `%s'\n", b_request->gvalue));
          }
      }
  }
#endif

  tmp =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (tmp->transactionid == nist->transactionid)
        {
          osip_list_iterator_remove (&iterator);
#ifdef OSIP_MT
          osip_mutex_unlock (nist_fastmutex);
#endif
          return 0;
        }
      tmp = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nist_fastmutex);
#endif
  return -1;
}

#if 0
/* this method is made obsolete because it contains bugs and is also
   too much limited.
   any call to this method should be replace this way:

   //osip_distribute(osip, evt);
   int i = osip_find_transaction_and_add_event(osip, evt);

   if (i!=0) // in case it's a new request
     {
        if (evt is an ACK)
            evt could be an ACK for INVITE (not handled by oSIP)
        else if ( evt is a 200 for INVITE)
           evt could be a retransmission of a 200 for INVITE (not handled by oSIP)
        else if (evt is a new request)  == not a ACK and not a response
	  {
           transaction = osip_create_transaction(osip, evt);
           if (transaction==NULL)
             printf("failed to create a transaction\");
          }
    }
    else
    {
    // here, the message as been taken by the stack.
    }
*/


/* finds the transaction context and add the sipevent in its fifo. */
/* USED ONLY BY THE TRANSPORT LAYER.                               */
/* INPUT : osip_t *osip | osip. contains the list of tr. context*/
/* INPUT : osip_event_t* sipevent | event to dispatch.               */
osip_transaction_t *
osip_distribute_event (osip_t * osip, osip_event_t * evt)
{
  osip_transaction_t *transaction = NULL;
  int i;
  osip_fsm_type_t ctx_type;

  if (EVT_IS_INCOMINGMSG (evt))
    {
      /* event is for ict */
      if (MSG_IS_REQUEST (evt->sip))
        {
          if (0 == strcmp (evt->sip->cseq->method, "INVITE")
              || 0 == strcmp (evt->sip->cseq->method, "ACK"))
            {
#ifdef OSIP_MT
              osip_mutex_lock (ist_fastmutex);
#endif
              transaction =
                osip_transaction_find (osip->osip_ist_transactions, evt);
#ifdef OSIP_MT
              osip_mutex_unlock (ist_fastmutex);
#endif
          } else
            {
#ifdef OSIP_MT
              osip_mutex_lock (nist_fastmutex);
#endif
              transaction =
                osip_transaction_find (osip->osip_nist_transactions, evt);
#ifdef OSIP_MT
              osip_mutex_unlock (nist_fastmutex);
#endif
            }
      } else
        {
          if (0 == strcmp (evt->sip->cseq->method, "INVITE")
              || 0 == strcmp (evt->sip->cseq->method, "ACK"))
            {
#ifdef OSIP_MT
              osip_mutex_lock (ict_fastmutex);
#endif
              transaction =
                osip_transaction_find (osip->osip_ict_transactions, evt);
#ifdef OSIP_MT
              osip_mutex_unlock (ict_fastmutex);
#endif
          } else
            {
#ifdef OSIP_MT
              osip_mutex_lock (nict_fastmutex);
#endif
              transaction =
                osip_transaction_find (osip->osip_nict_transactions, evt);
#ifdef OSIP_MT
              osip_mutex_unlock (nict_fastmutex);
#endif
            }
        }
      if (transaction == NULL)
        {
          if (EVT_IS_RCV_STATUS_1XX (evt)
              || EVT_IS_RCV_STATUS_2XX (evt)
              || EVT_IS_RCV_STATUS_3456XX (evt) || EVT_IS_RCV_ACK (evt))
            {                   /* event MUST be ignored! */
              /* EXCEPT FOR 2XX THAT MUST BE GIVEN TO THE CORE LAYER!!! */

              /* TODO */

              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_WARNING, NULL,
                           "transaction does not yet exist... %x callid:%s\n",
                           evt, evt->sip->call_id->number));
              osip_message_free (evt->sip);
              osip_free (evt);  /* transaction thread will not delete it */
              return NULL;
            }

          /* we create a new context for this incoming request */
          if (0 == strcmp (evt->sip->cseq->method, "INVITE"))
            ctx_type = IST;
          else
            ctx_type = NIST;

          i = osip_transaction_init (&transaction, ctx_type, osip, evt->sip);
          if (i == -1)
            {
              osip_message_free (evt->sip);
              osip_free (evt);  /* transaction thread will not delete it */
              return NULL;
            }
        }
      evt->transactionid = transaction->transactionid;

      evt->transactionid = transaction->transactionid;
      osip_fifo_add (transaction->transactionff, evt);
      return transaction;
  } else
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_BUG, NULL,
                   "wrong event type %x\n", evt));
      return NULL;
    }
}
#endif

int
osip_find_transaction_and_add_event (osip_t * osip, osip_event_t * evt)
{
  osip_transaction_t *transaction = __osip_find_transaction (osip, evt, 1);

  if (transaction == NULL)
    return -1;
  return 0;
}

#ifndef OSIP_MT
osip_transaction_t *
osip_find_transaction (osip_t * osip, osip_event_t * evt)
{
  return __osip_find_transaction (osip, evt, 0);
}
#endif

osip_transaction_t *
__osip_find_transaction (osip_t * osip, osip_event_t * evt, int consume)
{
  osip_transaction_t *transaction = NULL;
  osip_list_t *transactions = NULL;

#ifdef OSIP_MT
  struct osip_mutex *mut = NULL;
#endif

  if (evt == NULL || evt->sip == NULL || evt->sip->cseq == NULL)
    return NULL;

  if (EVT_IS_INCOMINGMSG (evt))
    {
      if (MSG_IS_REQUEST (evt->sip))
        {
          if (0 == strcmp (evt->sip->cseq->method, "INVITE")
              || 0 == strcmp (evt->sip->cseq->method, "ACK"))
            {
              transactions = &osip->osip_ist_transactions;
#ifdef OSIP_MT
              mut = ist_fastmutex;
#endif
          } else
            {
              transactions = &osip->osip_nist_transactions;
#ifdef OSIP_MT
              mut = nist_fastmutex;
#endif
            }
      } else
        {
          if (0 == strcmp (evt->sip->cseq->method, "INVITE"))
            {
              transactions = &osip->osip_ict_transactions;
#ifdef OSIP_MT
              mut = ict_fastmutex;
#endif
          } else
            {
              transactions = &osip->osip_nict_transactions;
#ifdef OSIP_MT
              mut = nict_fastmutex;
#endif
            }
        }
  } else if (EVT_IS_OUTGOINGMSG (evt))
    {
      if (MSG_IS_RESPONSE (evt->sip))
        {
          if (0 == strcmp (evt->sip->cseq->method, "INVITE"))
            {
              transactions = &osip->osip_ist_transactions;
#ifdef OSIP_MT
              mut = ist_fastmutex;
#endif
          } else
            {
              transactions = &osip->osip_nist_transactions;
#ifdef OSIP_MT
              mut = nist_fastmutex;
#endif
            }
      } else
        {
          if (0 == strcmp (evt->sip->cseq->method, "INVITE")
              || 0 == strcmp (evt->sip->cseq->method, "ACK"))
            {
              transactions = &osip->osip_ict_transactions;
#ifdef OSIP_MT
              mut = ict_fastmutex;
#endif
          } else
            {
              transactions = &osip->osip_nict_transactions;
#ifdef OSIP_MT
              mut = nict_fastmutex;
#endif
            }
        }
    }
  if (transactions == NULL)
    return NULL;                /* not a message??? */

#ifdef OSIP_MT
  osip_mutex_lock (mut);
#endif
  transaction = osip_transaction_find (transactions, evt);
  if (consume == 1)
    {                           /* we add the event before releasing the mutex!! */
      if (transaction != NULL)
        {
          osip_transaction_add_event (transaction, evt);
#ifdef OSIP_MT
          osip_mutex_unlock (mut);
#endif
          return transaction;
        }
    }
#ifdef OSIP_MT
  osip_mutex_unlock (mut);
#endif

  return transaction;
}

osip_transaction_t *
osip_create_transaction (osip_t * osip, osip_event_t * evt)
{
  osip_transaction_t *transaction;
  int i;
  osip_fsm_type_t ctx_type;

  if (evt == NULL)
    return NULL;
  if (evt->sip == NULL)
    return NULL;

  /* make sure the request's method reflect the cseq value. */
  if (MSG_IS_REQUEST (evt->sip))
    {
      /* delete request where cseq method does not match
         the method in request-line */
      if (evt->sip->cseq == NULL
          || evt->sip->cseq->method == NULL || evt->sip->sip_method == NULL)
        {
          return NULL;
        }
      if (0 != strcmp (evt->sip->cseq->method, evt->sip->sip_method))
        {
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_WARNING, NULL,
                       "core module: Discard invalid message with method!=cseq!\n"));
          return NULL;
        }
    }

  if (MSG_IS_ACK (evt->sip))    /* ACK never create transactions */
    return NULL;

  if (EVT_IS_INCOMINGREQ (evt))
    {
      /* we create a new context for this incoming request */
      if (0 == strcmp (evt->sip->cseq->method, "INVITE"))
        ctx_type = IST;
      else
        ctx_type = NIST;
  } else if (EVT_IS_OUTGOINGREQ (evt))
    {
      if (0 == strcmp (evt->sip->cseq->method, "INVITE"))
        ctx_type = ICT;
      else
        ctx_type = NICT;
  } else
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "Cannot build a transction for this message!\n"));
      return NULL;
    }

  i = osip_transaction_init (&transaction, ctx_type, osip, evt->sip);
  if (i == -1)
    {
      return NULL;
    }
  evt->transactionid = transaction->transactionid;
  return transaction;
}

osip_transaction_t *
osip_transaction_find (osip_list_t * transactions, osip_event_t * evt)
{
  osip_list_iterator_t iterator;
  osip_transaction_t *transaction;
  osip_t *osip = NULL;

  transaction =
    (osip_transaction_t *) osip_list_get_first (transactions, &iterator);
  if (transaction != NULL)
    osip = (osip_t *) transaction->config;
  if (osip == NULL)
    return NULL;

  if (EVT_IS_INCOMINGREQ (evt))
    {
#ifdef HAVE_DICT_DICT_H
      /* search in hastable! */
      osip_generic_param_t *b_request;
      osip_via_t *topvia_request;

      topvia_request = osip_list_get (evt->sip->vias, 0);
      if (topvia_request == NULL)
        {
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_ERROR, NULL,
                       "Remote UA is not compliant: missing a Via header!\n"));
          return NULL;
        }
      osip_via_param_get_byname (topvia_request, "branch", &b_request);
      if (b_request != NULL && b_request->gvalue != NULL)
        {
          if (MSG_IS_INVITE (evt->sip) || MSG_IS_ACK (evt->sip))
            {
              transaction =
                (osip_transaction_t *) dict_search (osip->osip_ist_hastable,
                                                    b_request->gvalue);
              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_INFO2, NULL,
                           "Find matching Via header for INVITE(ACK) REQUEST!\n"));
              if (transaction != NULL)
                return transaction;
          } else
            {
              transaction =
                (osip_transaction_t *) dict_search (osip->osip_nist_hastable,
                                                    b_request->gvalue);
              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_INFO2, NULL,
                           "Find matching Via header for NON-INVITE REQUEST!\n"));
              if (transaction != NULL)
                return transaction;
            }
        }
#endif

      transaction =
        (osip_transaction_t *) osip_list_get_first (transactions, &iterator);
      while (osip_list_iterator_has_elem (iterator))
        {
          if (0 ==
              __osip_transaction_matching_request_osip_to_xist_17_2_3
              (transaction, evt->sip))
            return transaction;
          transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
        }
  } else if (EVT_IS_INCOMINGRESP (evt))
    {
#ifdef HAVE_DICT_DICT_H
      /* search in hastable! */
      osip_generic_param_t *b_request;
      osip_via_t *topvia_request;

      topvia_request = osip_list_get (evt->sip->vias, 0);
      if (topvia_request == NULL)
        {
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_ERROR, NULL,
                       "Remote UA is not compliant: missing a Via header!\n"));
          return NULL;
        }
      osip_via_param_get_byname (topvia_request, "branch", &b_request);
      if (b_request != NULL && b_request->gvalue != NULL)
        {
          if (MSG_IS_RESPONSE_FOR (evt->sip, "INVITE"))
            {
              transaction =
                (osip_transaction_t *) dict_search (osip->osip_ict_hastable,
                                                    b_request->gvalue);
              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_INFO2, NULL,
                           "Find matching Via header for INVITE ANSWER!\n"));
              if (transaction != NULL)
                return transaction;
          } else
            {
              transaction =
                (osip_transaction_t *) dict_search (osip->osip_nict_hastable,
                                                    b_request->gvalue);
              OSIP_TRACE (osip_trace
                          (__FILE__, __LINE__, OSIP_INFO2, NULL,
                           "Find matching Via header for NON-INVITE ANSWER!\n"));
              if (transaction != NULL)
                return transaction;
            }
        }
#endif

      transaction =
        (osip_transaction_t *) osip_list_get_first (transactions, &iterator);
      while (osip_list_iterator_has_elem (iterator))
        {
          if (0 ==
              __osip_transaction_matching_response_osip_to_xict_17_1_3
              (transaction, evt->sip))
            return transaction;
          transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
        }
  } else                        /* handle OUTGOING message */
    {                           /* THE TRANSACTION ID MUST BE SET */
      transaction =
        (osip_transaction_t *) osip_list_get_first (transactions, &iterator);
      while (osip_list_iterator_has_elem (iterator))
        {
          if (transaction->transactionid == evt->transactionid)
            return transaction;
          transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
        }
    }
  return NULL;
}

static int ref_count = 0;

#ifdef OSIP_MT
static struct osip_mutex *ref_mutex = NULL;
#endif

static int
increase_ref_count (void)
{
#ifdef OSIP_MT
  if (ref_count == 0)
    ref_mutex = osip_mutex_init ();
  /* Here we should assert() that the mutex was really generated. */
  osip_mutex_lock (ref_mutex);
#endif
  if (ref_count == 0)
    __osip_global_init ();
  ref_count++;
#ifdef OSIP_MT
  osip_mutex_unlock (ref_mutex);
#endif

  return 0;
}

static void
decrease_ref_count (void)
{
#ifdef OSIP_MT
  osip_mutex_lock (ref_mutex);
#endif
  /* assert (ref_count > 0); */
  ref_count--;
  if (ref_count == 0)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (ref_mutex);
      osip_mutex_destroy (ref_mutex);
#endif
      __osip_global_free ();
      return;
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ref_mutex);
#endif
}

int
osip_init (osip_t ** osip)
{
  if (increase_ref_count () != 0)
    return -1;

  *osip = (osip_t *) osip_malloc (sizeof (osip_t));
  if (*osip == NULL)
    return -1;                  /* allocation failed */

  memset (*osip, 0, sizeof (osip_t));

  osip_list_init (&(*osip)->osip_ict_transactions);
  osip_list_init (&(*osip)->osip_ist_transactions);
  osip_list_init (&(*osip)->osip_nict_transactions);
  osip_list_init (&(*osip)->osip_nist_transactions);

  osip_list_init (&(*osip)->ixt_retransmissions);

#if defined(HAVE_DICT_DICT_H)
  (*osip)->osip_ict_hastable = hashtable_dict_new ((dict_cmp_func) strcmp,
                                                   (dict_hsh_func) s_hash,
                                                   NULL, NULL, HSIZE);
  (*osip)->osip_ist_hastable = hashtable_dict_new ((dict_cmp_func) strcmp,
                                                   (dict_hsh_func) s_hash,
                                                   NULL, NULL, HSIZE);
  (*osip)->osip_nict_hastable = hashtable_dict_new ((dict_cmp_func) strcmp,
                                                    (dict_hsh_func) s_hash,
                                                    NULL, NULL, HSIZE);
  (*osip)->osip_nist_hastable = hashtable_dict_new ((dict_cmp_func) strcmp,
                                                    (dict_hsh_func) s_hash,
                                                    NULL, NULL, HSIZE);
#elif defined(HAVE_DICT_DICT_H_HASHTABLE)
  (*osip)->osip_ict_hastable = rb_tree_new ((dict_cmp_func) strcmp, NULL, NULL);
  (*osip)->osip_ist_hastable = rb_tree_new ((dict_cmp_func) strcmp, NULL, NULL);
  (*osip)->osip_nict_hastable = rb_tree_new ((dict_cmp_func) strcmp, NULL, NULL);
  (*osip)->osip_nist_hastable = rb_tree_new ((dict_cmp_func) strcmp, NULL, NULL);
#endif

  return 0;
}

void
osip_release (osip_t * osip)
{
  osip_free (osip);
  decrease_ref_count ();
}


void
osip_set_application_context (osip_t * osip, void *pointer)
{
  osip->application_context = pointer;
}

void *
osip_get_application_context (osip_t * osip)
{
  if (osip == NULL)
    return NULL;
  return osip->application_context;
}

int
osip_ict_execute (osip_t * osip)
{
  osip_transaction_t *transaction;
  osip_event_t *se;
  int more_event;
  osip_list_iterator_t iterator;
  void **array;
  int len;
  int index = 0;

  /* list must be copied because osip_transaction_execute() may change it */
#ifdef OSIP_MT
  osip_mutex_lock (ict_fastmutex);
#endif
  len = osip_list_size (&osip->osip_ict_transactions);
  if (0 >= len)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (ict_fastmutex);
#endif
      return 0;
    }
  array = osip_malloc (sizeof (void *) * len);
  if (array==NULL)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (ict_fastmutex);
#endif
      return 0;
    }
  transaction =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      array[index++] = transaction;
      transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ict_fastmutex);
#endif

  for (index = 0; index < len; ++index)
    {
      transaction = (osip_transaction_t *) array[index];
      more_event = 1;
      do
        {
          se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff);
          if (se == NULL)       /* no more event for this transaction */
            more_event = 0;
          else
            osip_transaction_execute (transaction, se);
        }
      while (more_event == 1);
    }

  osip_free (array);

  return 0;
}

int
osip_ist_execute (osip_t * osip)
{
  osip_transaction_t *transaction;
  osip_event_t *se;
  int more_event;
  osip_list_iterator_t iterator;
  void **array;
  int len;
  int index = 0;

  /* list must be copied because osip_transaction_execute() may change it */
#ifdef OSIP_MT
  osip_mutex_lock (ist_fastmutex);
#endif
  len = osip_list_size (&osip->osip_ist_transactions);
  if (0 >= len)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (ist_fastmutex);
#endif
      return 0;
    }
  array = osip_malloc (sizeof (void *) * len);
  if (array==NULL)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (ist_fastmutex);
#endif
      return 0;
    }
  transaction =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      array[index++] = transaction;
      transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ist_fastmutex);
#endif

  for (index = 0; index < len; ++index)
    {
      transaction = (osip_transaction_t *) array[index];
      more_event = 1;
      do
        {
          se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff);
          if (se == NULL)       /* no more event for this transaction */
            more_event = 0;
          else
            osip_transaction_execute (transaction, se);
        }
      while (more_event == 1);

    }

  osip_free (array);

  return 0;
}

int
osip_nict_execute (osip_t * osip)
{
  osip_transaction_t *transaction;
  osip_event_t *se;
  int more_event;
  osip_list_iterator_t iterator;
  void **array;
  int len;
  int index = 0;

  /* list must be copied because osip_transaction_execute() may change it */
#ifdef OSIP_MT
  osip_mutex_lock (nict_fastmutex);
#endif
  len = osip_list_size (&osip->osip_nict_transactions);
  if (0 >= len)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (nict_fastmutex);
#endif
      return 0;
    }
  array = osip_malloc (sizeof (void *) * len);
  if (array==NULL)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (nict_fastmutex);
#endif
      return 0;
    }
  transaction =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      array[index++] = transaction;
      transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nict_fastmutex);
#endif

  for (index = 0; index < len; ++index)
    {
      transaction = (osip_transaction_t *) array[index];
      more_event = 1;
      do
        {
          se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff);
          if (se == NULL)       /* no more event for this transaction */
            more_event = 0;
          else
            osip_transaction_execute (transaction, se);
        }
      while (more_event == 1);
    }

  osip_free (array);

  return 0;
}

int
osip_nist_execute (osip_t * osip)
{
  osip_transaction_t *transaction;
  osip_event_t *se;
  int more_event;
  osip_list_iterator_t iterator;
  void **array;
  int len;
  int index = 0;

  /* list must be copied because osip_transaction_execute() may change it */
#ifdef OSIP_MT
  osip_mutex_lock (nist_fastmutex);
#endif
  len = osip_list_size (&osip->osip_nist_transactions);
  if (0 >= len)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (nist_fastmutex);
#endif
      return 0;
    }
  array = osip_malloc (sizeof (void *) * len);
  if (array==NULL)
    {
#ifdef OSIP_MT
      osip_mutex_unlock (nist_fastmutex);
#endif
      return 0;
    }
  transaction =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      array[index++] = transaction;
      transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nist_fastmutex);
#endif

  for (index = 0; index < len; ++index)
    {
      transaction = (osip_transaction_t *) array[index];
      more_event = 1;
      do
        {
          se = (osip_event_t *) osip_fifo_tryget (transaction->transactionff);
          if (se == NULL)       /* no more event for this transaction */
            more_event = 0;
          else
            osip_transaction_execute (transaction, se);
        }
      while (more_event == 1);
    }

  osip_free (array);

  return 0;
}

void
osip_timers_gettimeout (osip_t * osip, struct timeval *lower_tv)
{
  struct timeval now;
  osip_transaction_t *tr;
  osip_list_iterator_t iterator;

  osip_gettimeofday (&now, NULL);
  lower_tv->tv_sec = now.tv_sec + 3600 * 24 * 365;      /* wake up evry year :-) */
  lower_tv->tv_usec = now.tv_usec;

#ifdef OSIP_MT
  osip_mutex_lock (ict_fastmutex);
#endif
  /* handle ict timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (1 <= osip_fifo_size (tr->transactionff))
        {
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_INFO4, NULL,
                       "1 Pending event already in transaction !\n"));
          lower_tv->tv_sec = 0;
          lower_tv->tv_usec = 0;
#ifdef OSIP_MT
          osip_mutex_unlock (ict_fastmutex);
#endif
          return;
      } else
        {
          if (tr->state == ICT_CALLING)
            min_timercmp (lower_tv, &tr->ict_context->timer_b_start);
          if (tr->state == ICT_CALLING)
            min_timercmp (lower_tv, &tr->ict_context->timer_a_start);
          if (tr->state == ICT_COMPLETED)
            min_timercmp (lower_tv, &tr->ict_context->timer_d_start);
          if (osip_timercmp (&now, lower_tv, >))
            {
              lower_tv->tv_sec = 0;
              lower_tv->tv_usec = 0;
#ifdef OSIP_MT
              osip_mutex_unlock (ict_fastmutex);
#endif
              return;
            }
        }
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ict_fastmutex);
#endif

#ifdef OSIP_MT
  osip_mutex_lock (ist_fastmutex);
#endif
  /* handle ist timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (tr->state == IST_CONFIRMED)
        min_timercmp (lower_tv, &tr->ist_context->timer_i_start);
      if (tr->state == IST_COMPLETED)
        min_timercmp (lower_tv, &tr->ist_context->timer_h_start);
      if (tr->state == IST_COMPLETED)
        min_timercmp (lower_tv, &tr->ist_context->timer_g_start);
      if (osip_timercmp (&now, lower_tv, >))
        {
          lower_tv->tv_sec = 0;
          lower_tv->tv_usec = 0;
#ifdef OSIP_MT
          osip_mutex_unlock (ist_fastmutex);
#endif
          return;
        }
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ist_fastmutex);
#endif

#ifdef OSIP_MT
  osip_mutex_lock (nict_fastmutex);
#endif
  /* handle nict timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (tr->state == NICT_COMPLETED)
        min_timercmp (lower_tv, &tr->nict_context->timer_k_start);
      if (tr->state == NICT_PROCEEDING || tr->state == NICT_TRYING)
        min_timercmp (lower_tv, &tr->nict_context->timer_f_start);
      if (tr->state == NICT_PROCEEDING || tr->state == NICT_TRYING)
        min_timercmp (lower_tv, &tr->nict_context->timer_e_start);
      if (osip_timercmp (&now, lower_tv, >))
        {
          lower_tv->tv_sec = 0;
          lower_tv->tv_usec = 0;
#ifdef OSIP_MT
          osip_mutex_unlock (nict_fastmutex);
#endif
          return;
        }
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nict_fastmutex);
#endif

#ifdef OSIP_MT
  osip_mutex_lock (nist_fastmutex);
#endif
  /* handle nist timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      if (tr->state == NIST_COMPLETED)
        min_timercmp (lower_tv, &tr->nist_context->timer_j_start);
      if (osip_timercmp (&now, lower_tv, >))
        {
          lower_tv->tv_sec = 0;
          lower_tv->tv_usec = 0;
#ifdef OSIP_MT
          osip_mutex_unlock (nist_fastmutex);
#endif
          return;
        }
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nist_fastmutex);
#endif

#ifdef OSIP_MT
  osip_mutex_lock (ixt_fastmutex);
#endif
  {
    ixt_t *ixt;

    ixt = (ixt_t *) osip_list_get_first (&osip->ixt_retransmissions, &iterator);
    while (osip_list_iterator_has_elem (iterator))
      {
        min_timercmp (lower_tv, &ixt->start);
	if (osip_timercmp (&now, lower_tv, >))
	  {
	    lower_tv->tv_sec = 0;
	    lower_tv->tv_usec = 0;
#ifdef OSIP_MT
	    osip_mutex_unlock (ixt_fastmutex);
#endif
	    return;
	  }

        ixt = (ixt_t *) osip_list_get_next (&iterator);
      }
  }
#ifdef OSIP_MT
  osip_mutex_unlock (ixt_fastmutex);
#endif

  lower_tv->tv_sec = lower_tv->tv_sec - now.tv_sec;
  lower_tv->tv_usec = lower_tv->tv_usec - now.tv_usec;

  /* just make sure the value is correct! */
  if (lower_tv->tv_usec < 0)
    {
      lower_tv->tv_usec = lower_tv->tv_usec + 1000000;
      lower_tv->tv_sec--;
    }
  if (lower_tv->tv_sec < 0)
    {
      lower_tv->tv_sec = 0;
      lower_tv->tv_usec = 0;
    }
  if (lower_tv->tv_usec > 1000000)
    {
      lower_tv->tv_usec = lower_tv->tv_usec - 1000000;
      lower_tv->tv_sec++;
    }
  return;
}

void
osip_timers_ict_execute (osip_t * osip)
{
  osip_transaction_t *tr;
  osip_list_iterator_t iterator;

#ifdef OSIP_MT
  osip_mutex_lock (ict_fastmutex);
#endif
  /* handle ict timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      osip_event_t *evt;

      if (1 <= osip_fifo_size (tr->transactionff))
        {
          OSIP_TRACE (osip_trace
                      (__FILE__, __LINE__, OSIP_INFO4, NULL,
                       "1 Pending event already in transaction !\n"));
      } else
        {
          evt = __osip_ict_need_timer_b_event (tr->ict_context, tr->state,
                                               tr->transactionid);
          if (evt != NULL)
            osip_fifo_add (tr->transactionff, evt);
          else
            {
              evt = __osip_ict_need_timer_a_event (tr->ict_context, tr->state,
                                                   tr->transactionid);
              if (evt != NULL)
                osip_fifo_add (tr->transactionff, evt);
              else
                {
                  evt =
                    __osip_ict_need_timer_d_event (tr->ict_context, tr->state,
                                                   tr->transactionid);
                  if (evt != NULL)
                    osip_fifo_add (tr->transactionff, evt);
                }
            }
        }
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ict_fastmutex);
#endif
}

void
osip_timers_ist_execute (osip_t * osip)
{
  osip_transaction_t *tr;
  osip_list_iterator_t iterator;

#ifdef OSIP_MT
  osip_mutex_lock (ist_fastmutex);
#endif
  /* handle ist timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_ist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      osip_event_t *evt;

      evt = __osip_ist_need_timer_i_event (tr->ist_context, tr->state,
                                           tr->transactionid);
      if (evt != NULL)
        osip_fifo_add (tr->transactionff, evt);
      else
        {
          evt = __osip_ist_need_timer_h_event (tr->ist_context, tr->state,
                                               tr->transactionid);
          if (evt != NULL)
            osip_fifo_add (tr->transactionff, evt);
          else
            {
              evt = __osip_ist_need_timer_g_event (tr->ist_context, tr->state,
                                                   tr->transactionid);
              if (evt != NULL)
                osip_fifo_add (tr->transactionff, evt);
            }
        }
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (ist_fastmutex);
#endif
}

void
osip_timers_nict_execute (osip_t * osip)
{
  osip_transaction_t *tr;
  osip_list_iterator_t iterator;

#ifdef OSIP_MT
  osip_mutex_lock (nict_fastmutex);
#endif
  /* handle nict timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nict_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      osip_event_t *evt;

      evt = __osip_nict_need_timer_k_event (tr->nict_context, tr->state,
                                            tr->transactionid);
      if (evt != NULL)
        osip_fifo_add (tr->transactionff, evt);
      else
        {
          evt = __osip_nict_need_timer_f_event (tr->nict_context, tr->state,
                                                tr->transactionid);
          if (evt != NULL)
            osip_fifo_add (tr->transactionff, evt);
          else
            {
              evt =
                __osip_nict_need_timer_e_event (tr->nict_context, tr->state,
                                                tr->transactionid);
              if (evt != NULL)
                osip_fifo_add (tr->transactionff, evt);
            }
        }
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nict_fastmutex);
#endif
}


void
osip_timers_nist_execute (osip_t * osip)
{
  osip_transaction_t *tr;
  osip_list_iterator_t iterator;

#ifdef OSIP_MT
  osip_mutex_lock (nist_fastmutex);
#endif
  /* handle nist timers */
  tr =
    (osip_transaction_t *) osip_list_get_first (&osip->osip_nist_transactions,
                                                &iterator);
  while (osip_list_iterator_has_elem (iterator))
    {
      osip_event_t *evt;

      evt = __osip_nist_need_timer_j_event (tr->nist_context, tr->state,
                                            tr->transactionid);
      if (evt != NULL)
        osip_fifo_add (tr->transactionff, evt);
      tr = (osip_transaction_t *) osip_list_get_next (&iterator);
    }
#ifdef OSIP_MT
  osip_mutex_unlock (nist_fastmutex);
#endif
}

void
osip_set_cb_send_message (osip_t * cf,
                          int (*cb) (osip_transaction_t *, osip_message_t *,
                                     char *, int, int))
{
  cf->cb_send_message = cb;
}

void
__osip_message_callback (int type, osip_transaction_t * tr, osip_message_t * msg)
{
  osip_t *config = tr->config;

  if (type >= OSIP_MESSAGE_CALLBACK_COUNT)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_BUG, NULL,
                   "invalid callback type %d\n", type));
      return;
    }
  if (config->msg_callbacks[type] == NULL)
    return;
  config->msg_callbacks[type] (type, tr, msg);
}

void
__osip_kill_transaction_callback (int type, osip_transaction_t * tr)
{
  osip_t *config = tr->config;

  if (type >= OSIP_KILL_CALLBACK_COUNT)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_BUG, NULL,
                   "invalid callback type %d\n", type));
      return;
    }
  tr->completed_time = time (NULL);
  if (config->kill_callbacks[type] == NULL)
    return;
  config->kill_callbacks[type] (type, tr);
}

void
__osip_transport_error_callback (int type, osip_transaction_t * tr, int error)
{
  osip_t *config = tr->config;

  if (type >= OSIP_TRANSPORT_ERROR_CALLBACK_COUNT)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_BUG, NULL,
                   "invalid callback type %d\n", type));
      return;
    }
  if (config->tp_error_callbacks[type] == NULL)
    return;
  config->tp_error_callbacks[type] (type, tr, error);
}


int
osip_set_message_callback (osip_t * config, int type, osip_message_cb_t cb)
{
  if (type >= OSIP_MESSAGE_CALLBACK_COUNT)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "invalid callback type %d\n", type));
      return -1;
    }
  config->msg_callbacks[type] = cb;

  return 0;
}

int
osip_set_kill_transaction_callback (osip_t * config, int type,
                                    osip_kill_transaction_cb_t cb)
{
  if (type >= OSIP_KILL_CALLBACK_COUNT)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "invalid callback type %d\n", type));
      return -1;
    }
  config->kill_callbacks[type] = cb;
  return 0;
}

int
osip_set_transport_error_callback (osip_t * config, int type,
                                   osip_transport_error_cb_t cb)
{
  if (type >= OSIP_TRANSPORT_ERROR_CALLBACK_COUNT)
    {
      OSIP_TRACE (osip_trace
                  (__FILE__, __LINE__, OSIP_ERROR, NULL,
                   "invalid callback type %d\n", type));
      return -1;
    }
  config->tp_error_callbacks[type] = cb;
  return 0;
}
