/*
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/internal.h>
#include <osip2/osip.h>
#include "fsm.h"
osip_statemachine_t *nist_fsm;
osip_statemachine_t *
__nist_get_fsm ()
{
return nist_fsm;
}
void
__nist_unload_fsm ()
{
transition_t *transition;
osip_statemachine_t *statemachine = __nist_get_fsm ();
for (transition = statemachine->transitions; transition != NULL; transition = statemachine->transitions)
{
REMOVE_ELEMENT(statemachine->transitions, transition);
osip_free (transition);
}
osip_free (statemachine->transitions);
osip_free (statemachine);
}
void
__nist_load_fsm ()
{
transition_t *transition;
nist_fsm = (osip_statemachine_t *) osip_malloc (sizeof (osip_statemachine_t));
nist_fsm->transitions = NULL;
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_PRE_TRYING;
transition->type = RCV_REQUEST;
transition->method = (void (*)(void *, void *)) &nist_rcv_request;
ADD_ELEMENT (nist_fsm->transitions, transition);
/* This can be used to announce request but is useless, as
the transaction cannot send any response yet!
transition = (transition_t *) osip_malloc(sizeof(transition_t));
transition->state = NIST_TRYING;
transition->type = RCV_REQUEST;
transition->method = (void(*)(void *,void *))&nist_rcv_request;
osip_list_add(nist_fsm->transitions,transition,-1);
*/
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_TRYING;
transition->type = SND_STATUS_1XX;
transition->method = (void (*)(void *, void *)) &nist_snd_1xx;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_TRYING;
transition->type = SND_STATUS_2XX;
transition->method = (void (*)(void *, void *)) &nist_snd_23456xx;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_TRYING;
transition->type = SND_STATUS_3456XX;
transition->method = (void (*)(void *, void *)) &nist_snd_23456xx;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_PROCEEDING;
transition->type = SND_STATUS_1XX;
transition->method = (void (*)(void *, void *)) &nist_snd_1xx;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_PROCEEDING;
transition->type = SND_STATUS_2XX;
transition->method = (void (*)(void *, void *)) &nist_snd_23456xx;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_PROCEEDING;
transition->type = SND_STATUS_3456XX;
transition->method = (void (*)(void *, void *)) &nist_snd_23456xx;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_PROCEEDING;
transition->type = RCV_REQUEST;
transition->method = (void (*)(void *, void *)) &nist_rcv_request;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_COMPLETED;
transition->type = TIMEOUT_J;
transition->method = (void (*)(void *, void *)) &osip_nist_timeout_j_event;
ADD_ELEMENT (nist_fsm->transitions, transition);
transition = (transition_t *) osip_malloc (sizeof (transition_t));
transition->state = NIST_COMPLETED;
transition->type = RCV_REQUEST;
transition->method = (void (*)(void *, void *)) &nist_rcv_request;
ADD_ELEMENT (nist_fsm->transitions, transition);
}
static void
nist_handle_transport_error (osip_transaction_t * nist, int err)
{
__osip_transport_error_callback (OSIP_NIST_TRANSPORT_ERROR, nist, err);
__osip_transaction_set_state (nist, NIST_TERMINATED);
__osip_kill_transaction_callback (OSIP_NIST_KILL_TRANSACTION, nist);
/* TODO: MUST BE DELETED NOW */
}
void
nist_rcv_request (osip_transaction_t * nist, osip_event_t * evt)
{
int i;
osip_t *osip = (osip_t *) nist->config;
if (nist->state == NIST_PRE_TRYING) /* announce new REQUEST */
{
/* Here we have ist->orig_request == NULL */
nist->orig_request = evt->sip;
if (MSG_IS_REGISTER (evt->sip))
__osip_message_callback (OSIP_NIST_REGISTER_RECEIVED, nist,
nist->orig_request);
else if (MSG_IS_BYE (evt->sip))
__osip_message_callback (OSIP_NIST_BYE_RECEIVED, nist, nist->orig_request);
else if (MSG_IS_OPTIONS (evt->sip))
__osip_message_callback (OSIP_NIST_OPTIONS_RECEIVED, nist,
nist->orig_request);
else if (MSG_IS_INFO (evt->sip))
__osip_message_callback (OSIP_NIST_INFO_RECEIVED, nist,
nist->orig_request);
else if (MSG_IS_CANCEL (evt->sip))
__osip_message_callback (OSIP_NIST_CANCEL_RECEIVED, nist,
nist->orig_request);
else if (MSG_IS_NOTIFY (evt->sip))
__osip_message_callback (OSIP_NIST_NOTIFY_RECEIVED, nist,
nist->orig_request);
else if (MSG_IS_SUBSCRIBE (evt->sip))
__osip_message_callback (OSIP_NIST_SUBSCRIBE_RECEIVED, nist,
nist->orig_request);
else
__osip_message_callback (OSIP_NIST_UNKNOWN_REQUEST_RECEIVED, nist,
nist->orig_request);
} else /* NIST_PROCEEDING or NIST_COMPLETED */
{
/* delete retransmission */
osip_message_free (evt->sip);
__osip_message_callback (OSIP_NIST_REQUEST_RECEIVED_AGAIN, nist,
nist->orig_request);
if (nist->last_response != NULL) /* retransmit last response */
{
osip_via_t *via;
via = (osip_via_t *) osip_list_get (&nist->last_response->vias, 0);
if (via)
{
char *host;
int port;
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);
i = osip->cb_send_message (nist, nist->last_response, host,
port, nist->out_socket);
} else
i = -1;
if (i != 0)
{
nist_handle_transport_error (nist, i);
return;
} else
{
if (MSG_IS_STATUS_1XX (nist->last_response))
__osip_message_callback (OSIP_NIST_STATUS_1XX_SENT, nist,
nist->last_response);
else if (MSG_IS_STATUS_2XX (nist->last_response))
__osip_message_callback (OSIP_NIST_STATUS_2XX_SENT_AGAIN,
nist, nist->last_response);
else
__osip_message_callback (OSIP_NIST_STATUS_3456XX_SENT_AGAIN,
nist, nist->last_response);
return;
}
}
/* we are already in the proper state */
return;
}
/* we come here only if it was the first REQUEST received */
__osip_transaction_set_state (nist, NIST_TRYING);
}
void
nist_snd_1xx (osip_transaction_t * nist, osip_event_t * evt)
{
int i;
osip_via_t *via;
osip_t *osip = (osip_t *) nist->config;
if (nist->last_response != NULL)
{
osip_message_free (nist->last_response);
}
nist->last_response = evt->sip;
via = (osip_via_t *) osip_list_get (&nist->last_response->vias, 0);
if (via)
{
char *host;
int port;
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);
i = osip->cb_send_message (nist, nist->last_response, host,
port, nist->out_socket);
} else
i = -1;
if (i != 0)
{
nist_handle_transport_error (nist, i);
return;
} else
__osip_message_callback (OSIP_NIST_STATUS_1XX_SENT, nist, nist->last_response);
__osip_transaction_set_state (nist, NIST_PROCEEDING);
}
void
nist_snd_23456xx (osip_transaction_t * nist, osip_event_t * evt)
{
int i;
osip_via_t *via;
osip_t *osip = (osip_t *) nist->config;
if (nist->last_response != NULL)
{
osip_message_free (nist->last_response);
}
nist->last_response = evt->sip;
via = (osip_via_t *) osip_list_get (&nist->last_response->vias, 0);
if (via)
{
char *host;
int port;
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);
i = osip->cb_send_message (nist, nist->last_response, host,
port, nist->out_socket);
} else
i = -1;
if (i != 0)
{
nist_handle_transport_error (nist, i);
return;
} else
{
if (EVT_IS_SND_STATUS_2XX (evt))
__osip_message_callback (OSIP_NIST_STATUS_2XX_SENT, nist,
nist->last_response);
else if (MSG_IS_STATUS_3XX (nist->last_response))
__osip_message_callback (OSIP_NIST_STATUS_3XX_SENT, nist,
nist->last_response);
else if (MSG_IS_STATUS_4XX (nist->last_response))
__osip_message_callback (OSIP_NIST_STATUS_4XX_SENT, nist,
nist->last_response);
else if (MSG_IS_STATUS_5XX (nist->last_response))
__osip_message_callback (OSIP_NIST_STATUS_5XX_SENT, nist,
nist->last_response);
else
__osip_message_callback (OSIP_NIST_STATUS_6XX_SENT, nist,
nist->last_response);
}
if (nist->state != NIST_COMPLETED) /* start J timer */
{
osip_gettimeofday (&nist->nist_context->timer_j_start, NULL);
add_gettimeofday (&nist->nist_context->timer_j_start,
nist->nist_context->timer_j_length);
}
__osip_transaction_set_state (nist, NIST_COMPLETED);
}
void
osip_nist_timeout_j_event (osip_transaction_t * nist, osip_event_t * evt)
{
nist->nist_context->timer_j_length = -1;
nist->nist_context->timer_j_start.tv_sec = -1;
__osip_transaction_set_state (nist, NIST_TERMINATED);
__osip_kill_transaction_callback (OSIP_NIST_KILL_TRANSACTION, nist);
}
syntax highlighted by Code2HTML, v. 0.9.1