/*
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>
#ifndef WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef __APPLE_CC__
#include <unistd.h>
#endif
#else
#include <windows.h>
#endif
extern eXosip_t eXosip;
extern int ipv6_enable;
/* Private functions */
static void eXosip_send_default_answer (eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt,
int status,
char *reason_phrase,
char *warning, int line);
static void eXosip_process_info (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt);
static void eXosip_process_options (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt);
static void eXosip_process_bye (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt);
static void eXosip_process_refer (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt);
static void eXosip_process_ack (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_event_t * evt);
static void eXosip_process_prack (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt);
static int cancel_match_invite (osip_transaction_t * invite,
osip_message_t * cancel);
static void eXosip_process_cancel (osip_transaction_t * transaction,
osip_event_t * evt);
static osip_event_t *eXosip_process_reinvite (eXosip_call_t * jc,
eXosip_dialog_t * jd,
osip_transaction_t *
transaction, osip_event_t * evt);
static void eXosip_process_new_options (osip_transaction_t * transaction,
osip_event_t * evt);
static void eXosip_process_new_invite (osip_transaction_t * transaction,
osip_event_t * evt);
static void eXosip_process_new_subscribe (osip_transaction_t * transaction,
osip_event_t * evt);
static void eXosip_process_subscribe_within_call (eXosip_notify_t * jn,
eXosip_dialog_t * jd,
osip_transaction_t *
transaction, osip_event_t * evt);
static void eXosip_process_notify_within_dialog (eXosip_subscribe_t * js,
eXosip_dialog_t * jd,
osip_transaction_t *
transaction, osip_event_t * evt);
static int eXosip_match_notify_for_subscribe (eXosip_subscribe_t * js,
osip_message_t * notify);
static void eXosip_process_message_outside_of_dialog (osip_transaction_t * tr,
osip_event_t * evt);
static void eXosip_process_refer_outside_of_dialog (osip_transaction_t * tr,
osip_event_t * evt);
static void eXosip_process_message_within_dialog (eXosip_call_t * jc,
eXosip_dialog_t * jd,
osip_transaction_t *
transaction, osip_event_t * evt);
static void eXosip_process_newrequest (osip_event_t * evt, int socket);
static void eXosip_process_response_out_of_transaction (osip_event_t * evt);
static int eXosip_pendingosip_transaction_exist (eXosip_call_t * jc,
eXosip_dialog_t * jd);
static int eXosip_release_finished_calls (eXosip_call_t * jc,
eXosip_dialog_t * jd);
static int eXosip_release_aborted_calls (eXosip_call_t * jc, eXosip_dialog_t * jd);
static int eXosip_release_finished_transactions (eXosip_call_t *jc, eXosip_dialog_t * jd);
static int eXosip_release_finished_transactions_for_subscription (eXosip_dialog_t *jd);
static void
eXosip_send_default_answer (eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt,
int status,
char *reason_phrase, char *warning, int line)
{
osip_event_t *evt_answer;
osip_message_t *answer;
int i;
/* osip_list_add(eXosip.j_transactions, transaction, 0); */
osip_transaction_set_your_instance (transaction, NULL);
/* THIS METHOD DOES NOT ACCEPT STATUS CODE BETWEEN 101 and 299 */
if (status > 100 && status < 299 && MSG_IS_INVITE (evt->sip))
return;
if (jd != NULL)
i = _eXosip_build_response_default (&answer, jd->d_dialog, status, evt->sip);
else
i = _eXosip_build_response_default (&answer, NULL, status, evt->sip);
if (i != 0 || answer == NULL)
{
return;
}
if (reason_phrase != NULL)
{
char *_reason;
_reason = osip_message_get_reason_phrase (answer);
if (_reason != NULL)
osip_free (_reason);
_reason = osip_strdup (reason_phrase);
osip_message_set_reason_phrase (answer, _reason);
}
osip_message_set_content_length (answer, "0");
if (status == 500)
osip_message_set_retry_after (answer, "10");
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, evt_answer);
__eXosip_wakeup ();
}
static void
eXosip_process_options (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction, osip_event_t * evt)
{
osip_list_add (jd->d_inc_trs, transaction, 0);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
__eXosip_wakeup ();
}
static void
eXosip_process_info (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction, osip_event_t * evt)
{
osip_list_add (jd->d_inc_trs, transaction, 0);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
__eXosip_wakeup ();
}
static void
eXosip_process_bye (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction, osip_event_t * evt)
{
osip_event_t *evt_answer;
osip_message_t *answer;
int i;
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, NULL /*jd */ ,
NULL, NULL));
i = _eXosip_build_response_default (&answer, jd->d_dialog, 200, evt->sip);
if (i != 0)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
return;
}
osip_message_set_content_length (answer, "0");
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_list_add (jd->d_inc_trs, transaction, 0);
/* Release the eXosip_dialog */
osip_dialog_free (jd->d_dialog);
jd->d_dialog = NULL;
osip_transaction_add_event (transaction, evt_answer);
osip_nist_execute (eXosip.j_osip);
report_call_event (EXOSIP_CALL_MESSAGE_NEW, jc, jd, transaction);
report_call_event (EXOSIP_CALL_CLOSED, jc, jd, transaction);
eXosip_update (); /* AMD 30/09/05 */
__eXosip_wakeup ();
}
static void
eXosip_process_refer (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction, osip_event_t * evt)
{
osip_header_t *referto_head = NULL;
osip_contact_t *referto;
int i;
/* check if the refer is valid */
osip_message_header_get_byname (evt->sip, "refer-to", 0, &referto_head);
if (referto_head == NULL || referto_head->hvalue == NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 400,
"Missing Refer-To header",
"Missing Refer-To header", __LINE__);
return;
}
/* check if refer-to is well-formed */
osip_contact_init (&referto);
i = osip_contact_parse (referto, referto_head->hvalue);
if (i != 0)
{
osip_contact_free (referto);
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 400,
"Non valid Refer-To header",
"Non valid Refer-To header", __LINE__);
return;
}
osip_contact_free (referto);
/* check policy so we can decline immediatly the refer */
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
osip_list_add (jd->d_inc_trs, transaction, 0);
__eXosip_wakeup ();
}
static void
eXosip_process_notify_for_refer (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt)
{
#if 0
osip_event_t *evt_answer;
osip_message_t *answer;
int i;
osip_transaction_t *ref;
osip_header_t *event_hdr;
osip_header_t *sub_state;
osip_content_type_t *ctype;
osip_body_t *body = NULL;
/* get the event type and return "489 Bad Event". */
osip_message_header_get_byname (evt->sip, "event", 0, &event_hdr);
if (event_hdr == NULL || event_hdr->hvalue == NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 400,
"Missing Event header in Notify",
"Missing Event header in Notify", __LINE__);
return;
}
if (NULL == strstr (event_hdr->hvalue, "refer"))
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 501,
"Unsupported Event header",
"Unsupported Event header in Notify", __LINE__);
return;
}
osip_message_header_get_byname (evt->sip, "subscription-state", 0, &sub_state);
if (sub_state == NULL || sub_state->hvalue == NULL)
{
#ifndef CISCO_BUG
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 400, "Missing Header",
"Missing subscription-state Header", __LINE__);
return;
#else
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_WARNING, NULL,
"eXosip: Missing subscription-state Header (cisco 7960 bug)\n"));
#endif
}
ctype = osip_message_get_content_type (evt->sip);
if (ctype == NULL || ctype->type == NULL || ctype->subtype == NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 400, "Missing Header",
"Missing Content-Type Header", __LINE__);
return;
}
if (0 != osip_strcasecmp (ctype->type, "message")
|| 0 != osip_strcasecmp (ctype->subtype, "sipfrag"))
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 501,
"Unsupported body type",
"Unsupported body type", __LINE__);
return;
}
osip_message_get_body (evt->sip, 0, &body);
if (body == NULL || body->body == NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 400, "Missing Body",
"Missing Body", __LINE__);
return;
}
#if 0
report_call_event (EXOSIP_CALL_REFER_STATUS, jc, jd, transaction);
#endif
/* check if a refer was sent previously! */
ref = eXosip_find_last_out_transaction (jc, jd, "REFER");
if (ref == NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 481, NULL,
"No associated refer", __LINE__);
return;
}
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
/* for now, send default response of 200ok. eventually, application should
be deciding how to answer NOTIFY messages */
i = _eXosip_build_response_default (&answer, jd->d_dialog, 200, evt->sip);
if (i != 0)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
return;
}
i = complete_answer_that_establish_a_dialog (answer, evt->sip);
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_list_add (jd->d_inc_trs, transaction, 0);
osip_transaction_add_event (transaction, evt_answer);
__eXosip_wakeup ();
#else
osip_list_add (jd->d_inc_trs, transaction, 0);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
__eXosip_wakeup ();
#endif
}
static void
eXosip_process_ack (eXosip_call_t * jc, eXosip_dialog_t * jd, osip_event_t * evt)
{
/* TODO: We should find the matching transaction for this ACK
and also add the ACK in the event. */
eXosip_event_t *je;
int i;
/* stop ACK retransmission, in case there is any */
jd->d_count=0;
osip_message_free(jd->d_200Ok);
jd->d_200Ok=NULL;
je = eXosip_event_init_for_call (EXOSIP_CALL_ACK, jc, jd, NULL);
if (je != NULL)
{
i = osip_message_clone (evt->sip, &je->ack);
if (i != 0)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"failed to clone ACK for event\n"));
} else
report_event (je, NULL);
}
osip_event_free (evt);
}
static void
eXosip_process_prack (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction, osip_event_t * evt)
{
osip_event_t *evt_answer;
osip_message_t *answer;
int i;
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
i = _eXosip_build_response_default (&answer, jd->d_dialog, 200, evt->sip);
if (i != 0)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
return;
}
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_list_add (jd->d_inc_trs, transaction, 0);
osip_transaction_add_event (transaction, evt_answer);
__eXosip_wakeup ();
}
static int
cancel_match_invite (osip_transaction_t * invite, osip_message_t * cancel)
{
osip_generic_param_t *br;
osip_generic_param_t *br2;
osip_via_t *via;
osip_via_param_get_byname (invite->topvia, "branch", &br);
via = osip_list_get (&cancel->vias, 0);
if (via == NULL)
return -1; /* request without via??? */
osip_via_param_get_byname (via, "branch", &br2);
if (br != NULL && br2 == NULL)
return -1;
if (br2 != NULL && br == NULL)
return -1;
if (br2 != NULL && br != NULL) /* compliant UA :) */
{
if (br->gvalue != NULL && br2->gvalue != NULL &&
0 == strcmp (br->gvalue, br2->gvalue))
return 0;
return -1;
}
/* old backward compatibility mechanism */
if (0 != osip_call_id_match (invite->callid, cancel->call_id))
return -1;
if (0 != osip_to_tag_match (invite->to, cancel->to))
return -1;
if (0 != osip_from_tag_match (invite->from, cancel->from))
return -1;
if (0 != osip_via_match (invite->topvia, via))
return -1;
return 0;
}
static void
eXosip_process_cancel (osip_transaction_t * transaction, osip_event_t * evt)
{
osip_transaction_t *tr;
osip_event_t *evt_answer;
osip_message_t *answer;
int i;
eXosip_call_t *jc;
eXosip_dialog_t *jd;
tr = NULL;
jd = NULL;
/* first, look for a Dialog in the map of element */
for (jc = eXosip.j_calls; jc != NULL; jc = jc->next)
{
if (jc->c_inc_tr != NULL)
{
i = cancel_match_invite (jc->c_inc_tr, evt->sip);
if (i == 0)
{
tr = jc->c_inc_tr;
/* fixed */
if (jc->c_dialogs != NULL)
jd = jc->c_dialogs;
break;
}
}
tr = NULL;
for (jd = jc->c_dialogs; jd != NULL; jd = jd->next)
{
int pos = 0;
while (!osip_list_eol (jd->d_inc_trs, pos))
{
tr = osip_list_get (jd->d_inc_trs, pos);
i = cancel_match_invite (tr, evt->sip);
if (i == 0)
break;
tr = NULL;
pos++;
}
}
if (jd != NULL)
break; /* tr has just been found! */
}
if (tr == NULL) /* we didn't found the transaction to cancel */
{
i = _eXosip_build_response_default (&answer, NULL, 481, evt->sip);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot cancel transaction.\n"));
osip_list_add (eXosip.j_transactions, tr, 0);
osip_transaction_set_your_instance (tr, NULL);
return;
}
osip_message_set_content_length (answer, "0");
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, evt_answer);
osip_list_add (eXosip.j_transactions, transaction, 0);
osip_transaction_set_your_instance (transaction, NULL);
__eXosip_wakeup ();
return;
}
if (tr->state == IST_TERMINATED || tr->state == IST_CONFIRMED
|| tr->state == IST_COMPLETED)
{
/* I can't find the status code in the rfc?
(I read I must answer 200? wich I found strange)
I probably misunderstood it... and prefer to send 481
as the transaction has been answered. */
if (jd == NULL)
i = _eXosip_build_response_default (&answer, NULL, 481, evt->sip);
else
i = _eXosip_build_response_default (&answer, jd->d_dialog, 481, evt->sip);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot cancel transaction.\n"));
osip_list_add (eXosip.j_transactions, tr, 0);
osip_transaction_set_your_instance (tr, NULL);
return;
}
osip_message_set_content_length (answer, "0");
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, evt_answer);
if (jd != NULL)
osip_list_add (jd->d_inc_trs, transaction, 0);
else
osip_list_add (eXosip.j_transactions, transaction, 0);
osip_transaction_set_your_instance (transaction, NULL);
__eXosip_wakeup ();
return;
}
{
if (jd == NULL)
i = _eXosip_build_response_default (&answer, NULL, 200, evt->sip);
else
i = _eXosip_build_response_default (&answer, jd->d_dialog, 200, evt->sip);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot cancel transaction.\n"));
osip_list_add (eXosip.j_transactions, tr, 0);
osip_transaction_set_your_instance (tr, NULL);
return;
}
osip_message_set_content_length (answer, "0");
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, evt_answer);
__eXosip_wakeup ();
if (jd != NULL)
osip_list_add (jd->d_inc_trs, transaction, 0);
else
osip_list_add (eXosip.j_transactions, transaction, 0);
osip_transaction_set_your_instance (transaction, NULL);
/* answer transaction to cancel */
if (jd == NULL)
i = _eXosip_build_response_default (&answer, NULL, 487, tr->orig_request);
else
i = _eXosip_build_response_default (&answer, jd->d_dialog, 487,
tr->orig_request);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot cancel transaction.\n"));
osip_list_add (eXosip.j_transactions, tr, 0);
osip_transaction_set_your_instance (tr, NULL);
return;
}
osip_message_set_content_length (answer, "0");
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = tr->transactionid;
osip_transaction_add_event (tr, evt_answer);
__eXosip_wakeup ();
}
}
static osip_event_t *
eXosip_process_reinvite (eXosip_call_t * jc, eXosip_dialog_t * jd,
osip_transaction_t * transaction, osip_event_t * evt)
{
osip_message_t *answer;
osip_event_t *sipevent;
int i;
i = _eXosip_build_response_default (&answer, jd->d_dialog, 100, evt->sip);
if (i != 0)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 500,
"Internal SIP Error",
"Failed to build Answer for INVITE within call",
__LINE__);
return NULL;
}
complete_answer_that_establish_a_dialog (answer, evt->sip);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
sipevent = osip_new_outgoing_sipmessage (answer);
sipevent->transactionid = transaction->transactionid;
osip_list_add (jd->d_inc_trs, transaction, 0);
osip_ist_execute (eXosip.j_osip);
report_call_event (EXOSIP_CALL_REINVITE, jc, jd, transaction);
return sipevent;
}
static void
eXosip_process_new_options (osip_transaction_t * transaction, osip_event_t * evt)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
__eXosip_wakeup (); /* needed? */
}
static void
eXosip_process_new_invite (osip_transaction_t * transaction, osip_event_t * evt)
{
osip_event_t *evt_answer;
int i;
eXosip_call_t *jc;
eXosip_dialog_t *jd;
osip_message_t *answer;
eXosip_call_init (&jc);
ADD_ELEMENT (eXosip.j_calls, jc);
i = _eXosip_build_response_default (&answer, NULL, 101, evt->sip);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot create dialog."));
osip_list_add (eXosip.j_transactions, transaction, 0);
osip_transaction_set_your_instance (transaction, NULL);
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"ERROR: Could not create response for invite\n"));
return;
}
osip_message_set_content_length (answer, "0");
i = complete_answer_that_establish_a_dialog (answer, evt->sip);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot complete answer!\n"));
osip_list_add (eXosip.j_transactions, transaction, 0);
osip_transaction_set_your_instance (transaction, NULL);
osip_message_free (answer);
return;
}
i = eXosip_dialog_init_as_uas (&jd, evt->sip, answer);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot create dialog!\n"));
osip_list_add (eXosip.j_transactions, transaction, 0);
osip_transaction_set_your_instance (transaction, NULL);
osip_message_free (answer);
return;
}
ADD_ELEMENT (jc->c_dialogs, jd);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
eXosip_update ();
jc->c_inc_tr = transaction;
osip_transaction_add_event (transaction, evt_answer);
/* be sure the invite will be processed
before any API call on this dialog */
osip_ist_execute (eXosip.j_osip);
if (transaction->orig_request != NULL)
{
report_call_event (EXOSIP_CALL_INVITE, jc, jd, transaction);
}
__eXosip_wakeup ();
}
static void
eXosip_process_new_subscribe (osip_transaction_t * transaction, osip_event_t * evt)
{
osip_event_t *evt_answer;
eXosip_notify_t *jn;
eXosip_dialog_t *jd;
osip_message_t *answer;
int i;
eXosip_notify_init (&jn, evt->sip);
_eXosip_notify_set_refresh_interval (jn, evt->sip);
i = _eXosip_build_response_default (&answer, NULL, 101, evt->sip);
if (i != 0)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"ERROR: Could not create response for invite\n"));
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_notify_free (jn);
return;
}
i = complete_answer_that_establish_a_dialog (answer, evt->sip);
if (i != 0)
{
osip_message_free (answer);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot complete answer!\n"));
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_notify_free (jn);
return;
}
i = eXosip_dialog_init_as_uas (&jd, evt->sip, answer);
if (i != 0)
{
osip_message_free (answer);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot create dialog!\n"));
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_notify_free (jn);
return;
}
ADD_ELEMENT (jn->n_dialogs, jd);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (NULL, jd, NULL, jn));
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, evt_answer);
ADD_ELEMENT (eXosip.j_notifies, jn);
__eXosip_wakeup ();
jn->n_inc_tr = transaction;
eXosip_update ();
__eXosip_wakeup ();
}
static void
eXosip_process_subscribe_within_call (eXosip_notify_t * jn,
eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt)
{
_eXosip_notify_set_refresh_interval (jn, evt->sip);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (NULL, jd, NULL, jn));
/* if subscribe request contains expires="0", close the subscription */
{
time_t now = time (NULL);
if (jn->n_ss_expires - now <= 0)
{
jn->n_ss_status = EXOSIP_SUBCRSTATE_TERMINATED;
jn->n_ss_reason = TIMEOUT;
}
}
osip_list_add (jd->d_inc_trs, transaction, 0);
__eXosip_wakeup ();
return;
}
static void
eXosip_process_notify_within_dialog (eXosip_subscribe_t * js,
eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt)
{
osip_message_t *answer;
osip_event_t *sipevent;
osip_header_t *sub_state;
#ifdef SUPPORT_MSN
osip_header_t *expires;
#endif
int i;
if (jd == NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 500,
"Internal SIP Error",
"No dialog for this NOTIFY", __LINE__);
return;
}
/* if subscription-state has a reason state set to terminated,
we close the dialog */
#ifndef SUPPORT_MSN
osip_message_header_get_byname (evt->sip, "subscription-state", 0, &sub_state);
if (sub_state == NULL || sub_state->hvalue == NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 400, NULL, NULL, __LINE__);
return;
}
#endif
i = _eXosip_build_response_default (&answer, jd->d_dialog, 200, evt->sip);
if (i != 0)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 500,
"Internal SIP Error",
"Failed to build Answer for NOTIFY", __LINE__);
return;
}
#ifdef SUPPORT_MSN
osip_message_header_get_byname (evt->sip, "expires", 0, &expires);
if (expires != NULL && expires->hvalue != NULL
&& 0 == osip_strcasecmp (expires->hvalue, "0"))
{
/* delete the dialog! */
js->s_ss_status = EXOSIP_SUBCRSTATE_TERMINATED;
{
eXosip_event_t *je;
je = eXosip_event_init_for_subscribe (EXOSIP_SUBSCRIPTION_NOTIFY, js, jd);
eXosip_event_add (je);
}
sipevent = osip_new_outgoing_sipmessage (answer);
sipevent->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, sipevent);
osip_list_add (eXosip.j_transactions, transaction, 0);
REMOVE_ELEMENT (eXosip.j_subscribes, js);
eXosip_subscribe_free (js);
__eXosip_wakeup ();
return;
} else
{
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (NULL, jd, js, NULL));
js->s_ss_status = EXOSIP_SUBCRSTATE_ACTIVE;
}
#else
/* modify the status of user */
if (0 == osip_strncasecmp (sub_state->hvalue, "active", 6))
{
js->s_ss_status = EXOSIP_SUBCRSTATE_ACTIVE;
} else if (0 == osip_strncasecmp (sub_state->hvalue, "pending", 7))
{
js->s_ss_status = EXOSIP_SUBCRSTATE_PENDING;
}
if (0 == osip_strncasecmp (sub_state->hvalue, "terminated", 10))
{
/* delete the dialog! */
js->s_ss_status = EXOSIP_SUBCRSTATE_TERMINATED;
{
eXosip_event_t *je;
je =
eXosip_event_init_for_subscribe (EXOSIP_SUBSCRIPTION_NOTIFY, js, jd,
transaction);
eXosip_event_add (je);
}
sipevent = osip_new_outgoing_sipmessage (answer);
sipevent->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, sipevent);
osip_list_add (eXosip.j_transactions, transaction, 0);
REMOVE_ELEMENT (eXosip.j_subscribes, js);
eXosip_subscribe_free (js);
__eXosip_wakeup ();
return;
} else
{
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (NULL, jd, js, NULL));
}
#endif
osip_list_add (jd->d_inc_trs, transaction, 0);
sipevent = osip_new_outgoing_sipmessage (answer);
sipevent->transactionid = transaction->transactionid;
osip_transaction_add_event (transaction, sipevent);
__eXosip_wakeup ();
return;
}
static int
eXosip_match_notify_for_subscribe (eXosip_subscribe_t * js,
osip_message_t * notify)
{
osip_transaction_t *out_sub;
if (js == NULL)
return -1;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"Trying to match notify with subscribe\n"));
out_sub = eXosip_find_last_out_subscribe (js, NULL);
if (out_sub == NULL || out_sub->orig_request == NULL)
return -1;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"subscribe transaction found\n"));
/* some checks to avoid crashing on bad requests */
if (notify == NULL || notify->cseq == NULL
|| notify->cseq->method == NULL || notify->to == NULL)
return -1;
if (0 != osip_call_id_match (out_sub->callid, notify->call_id))
return -1;
{
/* The From tag of outgoing request must match
the To tag of incoming notify:
*/
osip_generic_param_t *tag_from;
osip_generic_param_t *tag_to;
osip_from_param_get_byname (out_sub->from, "tag", &tag_from);
osip_from_param_get_byname (notify->to, "tag", &tag_to);
if (tag_to == NULL || tag_to->gvalue == NULL)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"Uncompliant user agent: no tag in from of outgoing request\n"));
return -1;
}
if (tag_from == NULL || tag_to->gvalue == NULL)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"Uncompliant user agent: no tag in to of incoming request\n"));
return -1;
}
if (0 != strcmp (tag_from->gvalue, tag_to->gvalue))
return -1;
}
return 0;
}
static void
eXosip_process_message_outside_of_dialog (osip_transaction_t * transaction,
osip_event_t * evt)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
__eXosip_wakeup (); /* needed? */
return;
}
static void
eXosip_process_refer_outside_of_dialog (osip_transaction_t * transaction,
osip_event_t * evt)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
__eXosip_wakeup (); /* needed? */
return;
}
static void
eXosip_process_message_within_dialog (eXosip_call_t * jc,
eXosip_dialog_t * jd,
osip_transaction_t * transaction,
osip_event_t * evt)
{
osip_list_add (jd->d_inc_trs, transaction, 0);
osip_transaction_set_your_instance (transaction,
__eXosip_new_jinfo (jc, jd, NULL, NULL));
__eXosip_wakeup ();
return;
}
static void
eXosip_process_newrequest (osip_event_t * evt, int socket)
{
osip_transaction_t *transaction;
osip_event_t *evt_answer;
osip_message_t *answer;
int i;
int ctx_type;
eXosip_call_t *jc;
eXosip_subscribe_t *js;
eXosip_notify_t *jn;
eXosip_dialog_t *jd;
if (MSG_IS_INVITE (evt->sip))
{
ctx_type = IST;
} else if (MSG_IS_ACK (evt->sip))
{ /* this should be a ACK for 2xx (but could be a late ACK!) */
ctx_type = -1;
} else if (MSG_IS_REQUEST (evt->sip))
{
ctx_type = NIST;
} else
{ /* We should handle late response and 200 OK before coming here. */
ctx_type = -1;
osip_event_free (evt);
return;
}
transaction = NULL;
if (ctx_type != -1)
{
i = _eXosip_transaction_init (&transaction,
(osip_fsm_type_t) ctx_type,
eXosip.j_osip, evt->sip);
if (i != 0)
{
osip_event_free (evt);
return;
}
osip_transaction_set_in_socket (transaction, socket);
osip_transaction_set_out_socket (transaction, socket);
evt->transactionid = transaction->transactionid;
osip_transaction_set_your_instance (transaction, NULL);
osip_transaction_add_event (transaction, evt);
if (ctx_type == IST)
{
i = _eXosip_build_response_default (&answer, NULL, 100, evt->sip);
if (i != 0)
{
__eXosip_delete_jinfo (transaction);
osip_transaction_free (transaction);
return;
}
osip_message_set_content_length (answer, "0");
/* send message to transaction layer */
evt_answer = osip_new_outgoing_sipmessage (answer);
evt_answer->transactionid = transaction->transactionid;
/* add the REQUEST & the 100 Trying */
osip_transaction_add_event (transaction, evt_answer);
__eXosip_wakeup ();
}
}
if (MSG_IS_CANCEL (evt->sip))
{
/* special handling for CANCEL */
/* in the new spec, if the CANCEL has a Via branch, then it
is the same as the one in the original INVITE */
eXosip_process_cancel (transaction, evt);
return;
}
jd = NULL;
/* first, look for a Dialog in the map of element */
for (jc = eXosip.j_calls; jc != NULL; jc = jc->next)
{
for (jd = jc->c_dialogs; jd != NULL; jd = jd->next)
{
if (jd->d_dialog != NULL)
{
if (osip_dialog_match_as_uas (jd->d_dialog, evt->sip) == 0)
break;
}
}
if (jd != NULL)
break;
}
if (jd != NULL)
{
osip_transaction_t *old_trn;
/* it can be:
1: a new INVITE offer.
2: a REFER request from one of the party.
2: a BYE request from one of the party.
3: a REQUEST with a wrong CSeq.
4: a NOT-SUPPORTED method with a wrong CSeq.
*/
if (transaction==NULL)
{
/* cannot answer ACK transaction */
}
else if (!MSG_IS_BYE (evt->sip))
{
/* reject all requests for a closed dialog */
old_trn = eXosip_find_last_inc_transaction (jc, jd, "BYE");
if (old_trn == NULL)
old_trn = eXosip_find_last_out_transaction (jc, jd, "BYE");
if (old_trn != NULL)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 481, NULL,
NULL, __LINE__);
return;
}
}
if (MSG_IS_INVITE (evt->sip))
{
/* the previous transaction MUST be freed */
old_trn = eXosip_find_last_inc_invite (jc, jd);
if (old_trn != NULL && old_trn->state != IST_TERMINATED)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 500,
"Retry Later",
"An INVITE is not terminated", __LINE__);
return;
}
old_trn = eXosip_find_last_out_invite (jc, jd);
if (old_trn != NULL && old_trn->state != ICT_TERMINATED)
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 491, NULL,
NULL, __LINE__);
return;
}
osip_dialog_update_osip_cseq_as_uas (jd->d_dialog, evt->sip);
osip_dialog_update_route_set_as_uas (jd->d_dialog, evt->sip);
eXosip_process_reinvite (jc, jd, transaction, evt);
} else if (MSG_IS_BYE (evt->sip))
{
old_trn = eXosip_find_last_inc_transaction (jc, jd, "BYE");
if (old_trn != NULL) /* && old_trn->state!=NIST_TERMINATED) */
{ /* this situation should NEVER occur?? (we can't receive
two different BYE for one call! */
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 500,
"Call Already Terminated",
"A pending BYE has already terminate this call",
__LINE__);
return;
}
/* osip_transaction_free(old_trn); */
eXosip_process_bye (jc, jd, transaction, evt);
} else if (MSG_IS_ACK (evt->sip))
{
eXosip_process_ack (jc, jd, evt);
} else if (MSG_IS_REFER (evt->sip))
{
eXosip_process_refer (jc, jd, transaction, evt);
} else if (MSG_IS_OPTIONS (evt->sip))
{
eXosip_process_options (jc, jd, transaction, evt);
} else if (MSG_IS_INFO (evt->sip))
{
eXosip_process_info (jc, jd, transaction, evt);
} else if (MSG_IS_NOTIFY (evt->sip))
{
eXosip_process_notify_for_refer (jc, jd, transaction, evt);
} else if (MSG_IS_PRACK (evt->sip))
{
eXosip_process_prack (jc, jd, transaction, evt);
} else if (MSG_IS_MESSAGE (evt->sip))
{
eXosip_process_message_within_dialog (jc, jd, transaction, evt);
} else if (MSG_IS_SUBSCRIBE (evt->sip))
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 489, NULL,
"Bad Event", __LINE__);
} else
{
#if 0
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 405, NULL,
"Method Not Allowed", __LINE__);
#else
eXosip_process_message_within_dialog (jc, jd, transaction, evt);
#endif
}
return;
}
if (MSG_IS_ACK (evt->sip))
{
/* no transaction has been found for this ACK! */
osip_event_free (evt);
return;
}
if (MSG_IS_INFO (evt->sip))
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 481, NULL, NULL, __LINE__);
return; /* fixed */
}
if (MSG_IS_OPTIONS (evt->sip))
{
eXosip_process_new_options (transaction, evt);
return;
} else if (MSG_IS_INVITE (evt->sip))
{
eXosip_process_new_invite (transaction, evt);
return;
} else if (MSG_IS_BYE (evt->sip))
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 481, NULL, NULL, __LINE__);
return;
}
js = NULL;
/* first, look for a Dialog in the map of element */
for (js = eXosip.j_subscribes; js != NULL; js = js->next)
{
for (jd = js->s_dialogs; jd != NULL; jd = jd->next)
{
if (jd->d_dialog != NULL)
{
if (osip_dialog_match_as_uas (jd->d_dialog, evt->sip) == 0)
break;
}
}
if (jd != NULL)
break;
}
if (js != NULL)
{
/* dialog found */
osip_transaction_t *old_trn;
/* it can be:
1: a new INVITE offer.
2: a REFER request from one of the party.
2: a BYE request from one of the party.
3: a REQUEST with a wrong CSeq.
4: a NOT-SUPPORTED method with a wrong CSeq.
*/
if (MSG_IS_MESSAGE (evt->sip))
{
/* eXosip_process_imessage_within_subscribe_dialog(transaction, evt); */
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt,
SIP_NOT_IMPLEMENTED, NULL,
"MESSAGEs within dialogs are not implemented.",
__LINE__);
return;
} else if (MSG_IS_NOTIFY (evt->sip))
{
/* the previous transaction MUST be freed */
old_trn = eXosip_find_last_inc_notify (js, jd);
/* shouldn't we wait for the COMPLETED state? */
if (old_trn != NULL && old_trn->state != NIST_TERMINATED)
{
/* retry later? */
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 500,
"Retry Later",
"A pending NOTIFY is not terminated",
__LINE__);
return;
}
osip_dialog_update_osip_cseq_as_uas (jd->d_dialog, evt->sip);
osip_dialog_update_route_set_as_uas (jd->d_dialog, evt->sip);
eXosip_process_notify_within_dialog (js, jd, transaction, evt);
} else
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 501, NULL,
"Just Not Implemented", __LINE__);
}
return;
}
if (MSG_IS_NOTIFY (evt->sip))
{
/* let's try to check if the NOTIFY is related to an existing
subscribe */
js = NULL;
/* first, look for a Dialog in the map of element */
for (js = eXosip.j_subscribes; js != NULL; js = js->next)
{
if (eXosip_match_notify_for_subscribe (js, evt->sip) == 0)
{
i = eXosip_dialog_init_as_uac (&jd, evt->sip);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"eXosip: cannot establish a dialog\n"));
return;
}
/* update local cseq from subscribe request */
if (js->s_out_tr != NULL && js->s_out_tr->cseq != NULL
&& js->s_out_tr->cseq->number != NULL)
{
jd->d_dialog->local_cseq = atoi (js->s_out_tr->cseq->number);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: local cseq has been updated\n"));
}
ADD_ELEMENT (js->s_dialogs, jd);
eXosip_update ();
eXosip_process_notify_within_dialog (js, jd, transaction, evt);
return;
}
}
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (NULL, transaction, evt, 481, NULL, NULL,
__LINE__);
return;
}
jn = NULL;
/* first, look for a Dialog in the map of element */
for (jn = eXosip.j_notifies; jn != NULL; jn = jn->next)
{
for (jd = jn->n_dialogs; jd != NULL; jd = jd->next)
{
if (jd->d_dialog != NULL)
{
if (osip_dialog_match_as_uas (jd->d_dialog, evt->sip) == 0)
break;
}
}
if (jd != NULL)
break;
}
if (jn != NULL)
{
/* dialog found */
osip_transaction_t *old_trn;
/* it can be:
1: a new INVITE offer.
2: a REFER request from one of the party.
2: a BYE request from one of the party.
3: a REQUEST with a wrong CSeq.
4: a NOT-SUPPORTED method with a wrong CSeq.
*/
if (MSG_IS_MESSAGE (evt->sip))
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt,
SIP_NOT_IMPLEMENTED, NULL,
"MESSAGEs within dialogs are not implemented.",
__LINE__);
return;
} else if (MSG_IS_SUBSCRIBE (evt->sip))
{
/* the previous transaction MUST be freed */
old_trn = eXosip_find_last_inc_subscribe (jn, jd);
/* shouldn't we wait for the COMPLETED state? */
if (old_trn != NULL && old_trn->state != NIST_TERMINATED
&& old_trn->state != NIST_COMPLETED)
{
/* retry later? */
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 500,
"Retry Later",
"A SUBSCRIBE is not terminated",
__LINE__);
return;
}
osip_dialog_update_osip_cseq_as_uas (jd->d_dialog, evt->sip);
osip_dialog_update_route_set_as_uas (jd->d_dialog, evt->sip);
eXosip_process_subscribe_within_call (jn, jd, transaction, evt);
} else
{
osip_list_add (eXosip.j_transactions, transaction, 0);
eXosip_send_default_answer (jd, transaction, evt, 501, NULL, NULL,
__LINE__);
}
return;
}
if (MSG_IS_MESSAGE (evt->sip))
{
eXosip_process_message_outside_of_dialog (transaction, evt);
return;
}
if (MSG_IS_REFER (evt->sip))
{
eXosip_process_refer_outside_of_dialog (transaction, evt);
return;
}
if (MSG_IS_SUBSCRIBE (evt->sip))
{
eXosip_process_new_subscribe (transaction, evt);
return;
}
/* default answer */
osip_list_add (eXosip.j_transactions, transaction, 0);
#if 0
eXosip_send_default_answer (NULL, transaction, evt, 501, NULL, NULL, __LINE__);
#endif
}
static void
eXosip_process_response_out_of_transaction (osip_event_t * evt)
{
eXosip_call_t *jc = NULL;
eXosip_dialog_t *jd = NULL;
time_t now;
now = time (NULL);
if (evt->sip==NULL
|| evt->sip->cseq==NULL
|| evt->sip->cseq->number==NULL
|| evt->sip->to==NULL
|| evt->sip->from==NULL)
{
osip_event_free (evt);
return;
}
/* search for existing dialog: match branch & to tag */
for (jc = eXosip.j_calls; jc != NULL; jc = jc->next)
{
/* search for calls with only ONE outgoing transaction */
if (jc->c_id >= 1 && jc->c_dialogs != NULL && jc->c_out_tr!=NULL)
{
for (jd = jc->c_dialogs; jd != NULL; jd = jd->next)
{
/* only initial request are concerned with this */
if (jd->d_id >=1 && jd->d_dialog != NULL)
{
/* match answer with dialog */
osip_generic_param_t *tag;
osip_from_get_tag (evt->sip->to, &tag);
if (jd->d_dialog->remote_tag == NULL || tag == NULL)
continue;
if (jd->d_dialog->remote_tag != NULL && tag != NULL
&& tag->gvalue != NULL
&& 0 == strcmp (jd->d_dialog->remote_tag, tag->gvalue))
break;
}
}
if (jd!=NULL)
break; /* found a matching dialog! */
/* check if the transaction match this from tag */
if (jc->c_out_tr->orig_request!=NULL && jc->c_out_tr->orig_request->from!=NULL)
{
osip_generic_param_t *tag_invite;
osip_generic_param_t *tag;
osip_from_get_tag (jc->c_out_tr->orig_request->from, &tag_invite);
osip_from_get_tag (evt->sip->from, &tag);
if (tag_invite == NULL || tag == NULL)
continue;
if (tag_invite->gvalue != NULL && tag->gvalue != NULL
&& 0 == strcmp (tag_invite->gvalue, tag->gvalue))
break;
}
}
}
if (jc==NULL)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Incoming 2xx has no relations with current calls: Message discarded.\r\n"));
osip_event_free (evt);
return;
}
if (jc!=NULL && jd!=NULL)
{
/* we have to restransmit the ACK (if already available) */
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"2xx restransmission receveid.\r\n"));
/* check if the 2xx is for the same ACK */
if (jd->d_ack!=NULL && jd->d_ack->cseq!=NULL
&& jd->d_ack->cseq->number!=NULL)
{
if (0==osip_strcasecmp(jd->d_ack->cseq->number, evt->sip->cseq->number))
{
cb_snd_message (NULL, jd->d_ack, NULL,0, -1);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"ACK restransmission sent.\r\n"));
}
}
osip_event_free (evt);
return;
}
if (jc!=NULL)
{
/* match answer with dialog */
osip_dialog_t *dlg;
osip_transaction_t *last_tr;
int i;
/* we match an existing dialog: send a retransmission of ACK */
i = osip_dialog_init_as_uac (&dlg, evt->sip);
if (i != 0 || dlg==NULL)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Cannot build dialog for 200ok.\r\n"));
osip_event_free (evt);
return;
}
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"sending ACK for 2xx out of transaction.\r\n"));
{
char *transport;
osip_message_t *ack;
osip_message_t *bye;
transport = NULL;
transport = _eXosip_transport_protocol (evt->sip);
if (transport == NULL)
i = _eXosip_build_request_within_dialog (&ack, "ACK", dlg, "UDP");
else
i = _eXosip_build_request_within_dialog (&ack, "ACK", dlg, transport);
if (i != 0)
{
osip_dialog_free(dlg);
osip_event_free (evt);
return;
}
/* copy all credentials from INVITE! */
last_tr = jc->c_out_tr;
if (last_tr!=NULL)
{
int pos = 0;
int i;
osip_proxy_authorization_t *pa = NULL;
i = osip_message_get_proxy_authorization (last_tr->orig_request, pos, &pa);
while (i == 0 && pa != NULL)
{
osip_proxy_authorization_t *pa2;
i = osip_proxy_authorization_clone (pa, &pa2);
if (i != 0)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"Error in credential from INVITE\n"));
break;
}
osip_list_add (&ack->proxy_authorizations, pa2, -1);
pa = NULL;
pos++;
i = osip_message_get_proxy_authorization (last_tr->orig_request, pos, &pa);
}
}
cb_snd_message (NULL, ack, NULL,0, -1);
osip_message_free(ack);
/* ready to send a BYE */
transport = NULL;
if (last_tr != NULL && last_tr->orig_request != NULL)
transport = _eXosip_transport_protocol (last_tr->orig_request);
if (transport == NULL)
i = generating_bye (&bye, dlg, "UDP");
else
i = generating_bye (&bye, dlg, "UDP");
cb_snd_message (NULL, bye, NULL,0, -1);
osip_message_free(bye);
}
osip_dialog_free(dlg);
osip_event_free (evt);
return;
}
/* we don't match any existing dialog: send a ACK & send a BYE */
osip_event_free (evt);
}
static int _eXosip_handle_incoming_message (char *buf, size_t len, int socket,
char *host, int port);
static int
_eXosip_handle_incoming_message (char *buf, size_t len, int socket,
char *host, int port)
{
osip_transaction_t *transaction = NULL;
osip_event_t *sipevent;
int i;
sipevent = osip_parse (buf, len);
transaction = NULL;
if (sipevent != NULL && sipevent->sip != NULL)
{
} else
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Could not parse SIP message\n"));
osip_event_free (sipevent);
return -1;
}
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Message received from: %s:%i\n", host, port));
osip_message_fix_last_via_header (sipevent->sip, host, port);
i = osip_find_transaction_and_add_event (eXosip.j_osip, sipevent);
if (i != 0)
{
/* this event has no transaction, */
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"This is a request\n", buf));
eXosip_lock ();
if (MSG_IS_REQUEST (sipevent->sip))
eXosip_process_newrequest (sipevent, socket);
else if (MSG_IS_RESPONSE (sipevent->sip))
eXosip_process_response_out_of_transaction (sipevent);
eXosip_unlock ();
} else
{
/* handled by oSIP ! */
return 0;
}
return 0;
}
#if defined (WIN32) || defined (_WIN32_WCE)
#define eXFD_SET(A, B) FD_SET((unsigned int) A, B)
#else
#define eXFD_SET(A, B) FD_SET(A, B)
#endif
/* if second==-1 && useconds==-1 -> wait for ever
if max_message_nb<=0 -> infinite loop.... */
int
eXosip_read_message (int max_message_nb, int sec_max, int usec_max)
{
fd_set osip_fdset;
struct timeval tv;
char *buf;
tv.tv_sec = sec_max;
tv.tv_usec = usec_max;
buf = (char *) osip_malloc (SIP_MESSAGE_MAX_LENGTH * sizeof (char) + 1);
while (max_message_nb != 0 && eXosip.j_stop_ua == 0)
{
int i;
int max=0;
int wakeup_socket = jpipe_get_read_descr (eXosip.j_socketctl);
FD_ZERO (&osip_fdset);
if (eXosip.net_interfaces[0].net_socket > 0)
{
eXFD_SET (eXosip.net_interfaces[0].net_socket, &osip_fdset);
max = eXosip.net_interfaces[0].net_socket;
}
if (eXosip.net_interfaces[1].net_socket > 0)
{
int pos;
struct eXosip_net *net = &eXosip.net_interfaces[1];
eXFD_SET (net->net_socket, &osip_fdset);
if (net->net_socket > max)
max = net->net_socket;
for (pos = 0; pos < EXOSIP_MAX_SOCKETS; pos++)
{
if (net->net_socket_tab[pos].socket != 0)
{
eXFD_SET (net->net_socket_tab[pos].socket, &osip_fdset);
if (net->net_socket_tab[pos].socket > max)
max = net->net_socket_tab[pos].socket;
}
}
}
if (eXosip.net_interfaces[2].net_socket > 0)
{
eXFD_SET (eXosip.net_interfaces[2].net_socket, &osip_fdset);
if (eXosip.net_interfaces[2].net_socket > max)
max = eXosip.net_interfaces[2].net_socket;
}
eXFD_SET (wakeup_socket, &osip_fdset);
if (wakeup_socket > max)
max = wakeup_socket;
if ((sec_max == -1) || (usec_max == -1))
i = select (max + 1, &osip_fdset, NULL, NULL, NULL);
else
i = select (max + 1, &osip_fdset, NULL, NULL, &tv);
#if defined (_WIN32_WCE)
/* TODO: fix me for wince */
/* if (i == -1)
continue; */
#else
if ((i == -1) && (errno == EINTR || errno == EAGAIN))
continue;
#endif
if ((i > 0) && FD_ISSET (wakeup_socket, &osip_fdset))
{
char buf2[500];
jpipe_read (eXosip.j_socketctl, buf2, 499);
}
if (0 == i || eXosip.j_stop_ua != 0)
{
} else if (-1 == i)
{
#if !defined (_WIN32_WCE) /* TODO: fix me for wince */
osip_free (buf);
return -2; /* error */
#endif
} else if (FD_ISSET (eXosip.net_interfaces[1].net_socket, &osip_fdset))
{
/* accept incoming connection */
char src6host[NI_MAXHOST];
int recvport = 0;
struct sockaddr_storage sa;
int sock;
int i;
int pos;
#ifdef __linux
socklen_t slen;
#else
int slen;
#endif
if (ipv6_enable == 0)
slen = sizeof (struct sockaddr_in);
else
slen = sizeof (struct sockaddr_in6);
for (pos = 0; pos < EXOSIP_MAX_SOCKETS; pos++)
{
if (eXosip.net_interfaces[1].net_socket_tab[pos].socket == 0)
break;
}
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO3, NULL,
"creating TCP socket at index: %i\n", pos));
sock =
accept (eXosip.net_interfaces[1].net_socket,
(struct sockaddr *) &sa, &slen);
if (sock < 0)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"Error accepting TCP socket\n"));
break;
}
eXosip.net_interfaces[1].net_socket_tab[pos].socket = sock;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"New TCP connection accepted\n"));
memset (src6host, 0, sizeof (src6host));
if (ipv6_enable == 0)
recvport = ntohs (((struct sockaddr_in *) &sa)->sin_port);
else
recvport = ntohs (((struct sockaddr_in6 *) &sa)->sin6_port);
i = getnameinfo ((struct sockaddr *) &sa, slen,
src6host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Message received from: %s:%i Error with getnameinfo\n",
src6host, recvport));
} else
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Message received from: %s:%i\n", src6host, recvport));
osip_strncpy (eXosip.net_interfaces[1].net_socket_tab[pos].
remote_ip, src6host,
sizeof (eXosip.net_interfaces[1].
net_socket_tab[pos].remote_ip));
eXosip.net_interfaces[1].net_socket_tab[pos].remote_port = recvport;
}
} else if (FD_ISSET (eXosip.net_interfaces[0].net_socket, &osip_fdset))
{
/*AMDstruct sockaddr_in sa; */
struct sockaddr_storage sa;
#ifdef __linux
socklen_t slen;
#else
int slen;
#endif
if (ipv6_enable == 0)
slen = sizeof (struct sockaddr_in);
else
slen = sizeof (struct sockaddr_in6);
i =
_eXosip_recvfrom (eXosip.net_interfaces[0].net_socket, buf,
SIP_MESSAGE_MAX_LENGTH, 0,
(struct sockaddr *) &sa, &slen);
if (i > 5) /* we expect at least one byte, otherwise there's no doubt that it is not a sip message ! */
{
/* Message might not end with a "\0" but we know the number of */
/* char received! */
osip_transaction_t *transaction = NULL;
osip_event_t *sipevent;
osip_strncpy (buf + i, "\0", 1);
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"Received message: \n%s\n", buf));
#ifdef WIN32
if (strlen (buf) > 412)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Message suite: \n%s\n", buf + 412));
}
#endif
sipevent = osip_parse (buf, i);
transaction = NULL;
if (sipevent != NULL && sipevent->sip != NULL)
{
if (!eXosip.http_port)
{
char src6host[NI_MAXHOST];
char src6buf[NI_MAXSERV];
int recvport = 0;
memset (src6host, 0, sizeof (src6host));
memset (src6buf, 0, sizeof (src6buf));
if (ipv6_enable == 0)
recvport = ntohs (((struct sockaddr_in *) &sa)->sin_port);
else
recvport =
ntohs (((struct sockaddr_in6 *) &sa)->sin6_port);
i = getnameinfo ((struct sockaddr *) &sa, slen,
src6host, NI_MAXHOST,
NULL, 0, NI_NUMERICHOST);
if (i != 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Message received from: %s:%i (serv=%s) Error with getnameinfo\n",
src6host, recvport, src6buf));
} else
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Message received from: %s:%i (serv=%s)\n",
src6host, recvport, src6buf));
}
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Message received from: %s:%i (serv=%s)\n",
src6host, recvport, src6buf));
osip_message_fix_last_via_header (sipevent->sip,
src6host, recvport);
}
i =
osip_find_transaction_and_add_event (eXosip.j_osip, sipevent);
if (i != 0)
{
/* this event has no transaction, */
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"This is a request\n", buf));
eXosip_lock ();
if (MSG_IS_REQUEST (sipevent->sip))
eXosip_process_newrequest (sipevent, 0);
else if (MSG_IS_RESPONSE (sipevent->sip))
eXosip_process_response_out_of_transaction (sipevent);
eXosip_unlock ();
} else
{
/* handled by oSIP ! */
}
} else
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Could not parse SIP message\n"));
osip_event_free (sipevent);
}
} else if (i < 0)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_ERROR, NULL,
"Could not read socket\n"));
} else
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"Dummy SIP message received\n"));
}
} else
{
/* loop over all TCP socket */
int pos = 0;
for (pos = 0; pos < EXOSIP_MAX_SOCKETS; pos++)
{
if (eXosip.net_interfaces[1].net_socket_tab[pos].socket > 0
&& FD_ISSET (eXosip.net_interfaces[1].net_socket_tab[pos].
socket, &osip_fdset))
{
i =
recv (eXosip.net_interfaces[1].net_socket_tab[pos].socket,
buf, SIP_MESSAGE_MAX_LENGTH, 0);
if (i > 5)
{
osip_strncpy (buf + i, "\0", 1);
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Received TCP message: \n%s\n", buf));
#ifdef WIN32
if (strlen (buf) > 412)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Message suite: \n%s\n", buf + 412));
}
#endif
_eXosip_handle_incoming_message (buf, i,
eXosip.
net_interfaces[1].
net_socket_tab[pos].
socket,
eXosip.
net_interfaces[1].
net_socket_tab[pos].
remote_ip,
eXosip.
net_interfaces[1].
net_socket_tab[pos].
remote_port);
} else if (i < 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_ERROR, NULL,
"Could not read socket - close it\n"));
close (eXosip.net_interfaces[1].net_socket_tab[pos].socket);
memset (&(eXosip.net_interfaces[1].net_socket_tab[pos]),
0, sizeof (struct eXosip_socket));
} else if (i == 0)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"End of stream (read 0 byte from %s:%i)\n",
eXosip.net_interfaces[1].
net_socket_tab[pos].remote_ip,
eXosip.net_interfaces[1].
net_socket_tab[pos].remote_port));
close (eXosip.net_interfaces[1].net_socket_tab[pos].socket);
memset (&(eXosip.net_interfaces[1].net_socket_tab[pos]),
0, sizeof (struct eXosip_socket));
} else
{
/* we expect at least one byte, otherwise there's no doubt that it is not a sip message ! */
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"Dummy SIP message received (size=%i)\n", i));
}
}
}
}
max_message_nb--;
}
osip_free (buf);
return 0;
}
void
eXosip_release_unused_transactions(void)
{
eXosip_dialog_t *jd;
eXosip_dialog_t *jdnext;
eXosip_subscribe_t *js;
eXosip_subscribe_t *jsnext;
eXosip_notify_t *jn;
eXosip_notify_t *jnnext;
for (js = eXosip.j_subscribes; js != NULL;)
{
jsnext = js->next;
for (jd = js->s_dialogs; jd != NULL;)
{
jdnext = jd->next;
eXosip_release_finished_transactions_for_subscription (jd);
jd = jdnext;
}
js = jsnext;
}
for (jn = eXosip.j_notifies; jn != NULL;)
{
jnnext = jn->next;
for (jd = jn->n_dialogs; jd != NULL;)
{
jdnext = jd->next;
eXosip_release_finished_transactions_for_subscription (jd);
jd = jdnext;
}
jn = jnnext;
}
}
static int
eXosip_pendingosip_transaction_exist (eXosip_call_t * jc, eXosip_dialog_t * jd)
{
osip_transaction_t *tr;
time_t now = time (NULL);
tr = eXosip_find_last_inc_transaction (jc, jd, "BYE");
if (tr != NULL && tr->state != NIST_TERMINATED)
{ /* Don't want to wait forever on broken transaction!! */
if (tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
/* remove the transaction from oSIP: */
osip_remove_transaction (eXosip.j_osip, tr);
eXosip_remove_transaction_from_call (tr, jc);
osip_list_add (eXosip.j_transactions, tr, 0);
} else
return 0;
}
tr = eXosip_find_last_out_transaction (jc, jd, "BYE");
if (tr != NULL && tr->state != NICT_TERMINATED)
{ /* Don't want to wait forever on broken transaction!! */
if (tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
/* remove the transaction from oSIP: */
osip_remove_transaction (eXosip.j_osip, tr);
eXosip_remove_transaction_from_call (tr, jc);
osip_list_add (eXosip.j_transactions, tr, 0);
} else
return 0;
}
tr = eXosip_find_last_inc_invite (jc, jd);
if (tr != NULL && tr->state != IST_TERMINATED)
{ /* Don't want to wait forever on broken transaction!! */
if (tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
/* remove the transaction from oSIP: */
/* osip_remove_transaction(eXosip.j_osip, tr);
eXosip_remove_transaction_from_call(tr, jc);
osip_transaction_free(tr); */
} else
return 0;
}
tr = eXosip_find_last_out_invite (jc, jd);
if (tr != NULL && tr->state != ICT_TERMINATED)
{ /* Don't want to wait forever on broken transaction!! */
if (tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
/* remove the transaction from oSIP: */
/* osip_remove_transaction(eXosip.j_osip, tr);
eXosip_remove_transaction_from_call(tr, jc);
osip_transaction_free(tr); */
} else
return 0;
}
tr = eXosip_find_last_inc_transaction (jc, jd, "REFER");
if (tr != NULL && tr->state != IST_TERMINATED)
{ /* Don't want to wait forever on broken transaction!! */
if (tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
/* remove the transaction from oSIP: */
osip_remove_transaction (eXosip.j_osip, tr);
eXosip_remove_transaction_from_call (tr, jc);
osip_list_add (eXosip.j_transactions, tr, 0);
} else
return 0;
}
tr = eXosip_find_last_out_transaction (jc, jd, "REFER");
if (tr != NULL && tr->state != NICT_TERMINATED)
{ /* Don't want to wait forever on broken transaction!! */
if (tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
/* remove the transaction from oSIP: */
osip_remove_transaction (eXosip.j_osip, tr);
eXosip_remove_transaction_from_call (tr, jc);
osip_list_add (eXosip.j_transactions, tr, 0);
} else
return 0;
}
return -1;
}
static int
eXosip_release_finished_transactions_for_subscription (eXosip_dialog_t *jd)
{
time_t now = time (NULL);
osip_transaction_t *inc_tr;
osip_transaction_t *out_tr;
int skip_first=0;
int pos;
int ret;
ret = -1;
if (jd != NULL)
{
/* go through all incoming transactions of this dialog */
pos = 0;
while (!osip_list_eol (jd->d_inc_trs, pos))
{
inc_tr = osip_list_get (jd->d_inc_trs, pos);
if (0 != osip_strcasecmp (inc_tr->cseq->method, "INVITE"))
{
/* remove, if transaction too old, independent of the state */
if ((skip_first==1)
&& (inc_tr->state == NIST_TERMINATED)
&& (inc_tr->birth_time + 30 < now)) /* keep it for 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: releaase non-INVITE server transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, inc_tr);
osip_list_remove (jd->d_inc_trs, pos);
osip_list_add (eXosip.j_transactions, inc_tr, 0);
ret = 0;
break;
}
}
else
{
/* remove, if transaction too old, independent of the state */
if ((inc_tr->state == IST_TERMINATED) &&
(inc_tr->birth_time + 30 < now)) /* Wait a max of 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: release INVITE server transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, inc_tr);
osip_list_remove (jd->d_inc_trs, pos);
osip_list_add (eXosip.j_transactions, inc_tr, 0);
ret = 0;
break;
}
}
if (0 == osip_strcasecmp (inc_tr->cseq->method, "SUBSCRIBE"))
skip_first=1;
if (0 == osip_strcasecmp (inc_tr->cseq->method, "NOTIFY"))
skip_first=1;
pos++;
}
skip_first=0;
/* go through all outgoing transactions of this dialog */
pos = 0;
while (!osip_list_eol (jd->d_out_trs, pos))
{
out_tr = osip_list_get (jd->d_out_trs, pos);
if (0 != osip_strcasecmp (out_tr->cseq->method, "INVITE"))
{
/* remove, if transaction too old, independent of the state */
if ((skip_first==1)
&& (out_tr->state == NICT_TERMINATED)
&& (out_tr->birth_time + 30 < now)) /* Wait a max of 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: release non INVITE client transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, out_tr);
osip_list_remove (jd->d_out_trs, pos);
osip_list_add (eXosip.j_transactions, out_tr, 0);
ret = 0;
break;
}
}
else
{
/* remove, if transaction too old, independent of the state */
if ((out_tr->state == ICT_TERMINATED) &&
(out_tr->birth_time + 30 < now)) /* Wait a max of 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: release INVITE client transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, out_tr);
osip_list_remove (jd->d_out_trs, pos);
osip_list_add (eXosip.j_transactions, out_tr, 0);
ret = 0;
break;
}
}
if (0 == osip_strcasecmp (out_tr->cseq->method, "SUBSCRIBE"))
skip_first=1;
if (0 == osip_strcasecmp (out_tr->cseq->method, "NOTIFY"))
skip_first=1;
pos++;
}
}
return ret;
}
static int
eXosip_release_finished_transactions (eXosip_call_t *jc, eXosip_dialog_t *jd)
{
time_t now = time (NULL);
osip_transaction_t *inc_tr;
osip_transaction_t *out_tr;
osip_transaction_t *last_invite;
int pos;
int ret;
ret = -1;
last_invite = eXosip_find_last_inc_invite (jc, jd);
if (jd != NULL)
{
/* go through all incoming transactions of this dialog */
pos = 1;
while (!osip_list_eol (jd->d_inc_trs, pos))
{
inc_tr = osip_list_get (jd->d_inc_trs, pos);
if (0 != osip_strcasecmp (inc_tr->cseq->method, "INVITE"))
{
/* remove, if transaction too old, independent of the state */
if ((inc_tr->state == NIST_TERMINATED) &&
(inc_tr->birth_time + 30 < now)) /* Wait a max of 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: releaase non-INVITE server transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, inc_tr);
osip_list_remove (jd->d_inc_trs, pos);
osip_list_add (eXosip.j_transactions, inc_tr, 0);
ret = 0;
break;
}
}
else
{
/* remove, if transaction too old, independent of the state */
if (last_invite!=inc_tr && (inc_tr->state == IST_TERMINATED) &&
(inc_tr->birth_time + 30 < now)) /* Wait a max of 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: release INVITE server transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, inc_tr);
osip_list_remove (jd->d_inc_trs, pos);
osip_list_add (eXosip.j_transactions, inc_tr, 0);
ret = 0;
break;
}
}
pos++;
}
last_invite = eXosip_find_last_out_invite (jc, jd);
/* go through all outgoing transactions of this dialog */
pos = 1;
while (!osip_list_eol (jd->d_out_trs, pos))
{
out_tr = osip_list_get (jd->d_out_trs, pos);
if (0 != osip_strcasecmp (out_tr->cseq->method, "INVITE"))
{
/* remove, if transaction too old, independent of the state */
if ((out_tr->state == NICT_TERMINATED) &&
(out_tr->birth_time + 30 < now)) /* Wait a max of 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: release non INVITE client transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, out_tr);
osip_list_remove (jd->d_out_trs, pos);
osip_list_add (eXosip.j_transactions, out_tr, 0);
ret = 0;
break;
}
}
else
{
/* remove, if transaction too old, independent of the state */
if (last_invite!=out_tr
&& (out_tr->state == ICT_TERMINATED) &&
(out_tr->birth_time + 30 < now)) /* Wait a max of 30 seconds */
{
/* remove the transaction from oSIP */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: release INVITE client transaction (did=%i)\n",
jd->d_id));
osip_remove_transaction (eXosip.j_osip, out_tr);
osip_list_remove (jd->d_out_trs, pos);
osip_list_add (eXosip.j_transactions, out_tr, 0);
ret = 0;
break;
}
}
pos++;
}
}
return ret;
}
static int
eXosip_release_finished_calls (eXosip_call_t * jc, eXosip_dialog_t * jd)
{
osip_transaction_t *tr;
tr = eXosip_find_last_inc_transaction (jc, jd, "BYE");
if (tr == NULL)
tr = eXosip_find_last_out_transaction (jc, jd, "BYE");
if (tr != NULL && (tr->state == NIST_TERMINATED || tr->state == NICT_TERMINATED))
{
int did = -2;
if (jd != NULL)
did = jd->d_id;
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_finished_calls remove a dialog (cid=%i did=%i)\n",
jc->c_id, did));
/* Remove existing reference to the dialog from transactions! */
__eXosip_call_remove_dialog_reference_in_call (jc, jd);
REMOVE_ELEMENT (jc->c_dialogs, jd);
eXosip_dialog_free (jd);
return 0;
}
return -1;
}
static void
__eXosip_release_call (eXosip_call_t * jc, eXosip_dialog_t * jd)
{
REMOVE_ELEMENT (eXosip.j_calls, jc);
report_call_event (EXOSIP_CALL_RELEASED, jc, jd, NULL);
eXosip_call_free (jc);
__eXosip_wakeup ();
}
static int
eXosip_release_aborted_calls (eXosip_call_t * jc, eXosip_dialog_t * jd)
{
time_t now = time (NULL);
osip_transaction_t *tr;
#if 0
tr = eXosip_find_last_inc_invite (jc, jd);
if (tr == NULL)
tr = eXosip_find_last_out_invite (jc, jd);
#else
/* close calls only when the initial INVITE failed */
tr = jc->c_inc_tr;
if (tr == NULL)
tr = jc->c_out_tr;
#endif
if (tr == NULL)
{
if (jd != NULL)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls remove an empty dialog\n"));
__eXosip_call_remove_dialog_reference_in_call (jc, jd);
REMOVE_ELEMENT (jc->c_dialogs, jd);
eXosip_dialog_free (jd);
return 0;
}
return -1;
}
if (tr != NULL && tr->state != IST_TERMINATED && tr->state != ICT_TERMINATED && tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
if (jd != NULL)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls remove a dialog for an unfinished transaction\n"));
__eXosip_call_remove_dialog_reference_in_call (jc, jd);
REMOVE_ELEMENT (jc->c_dialogs, jd);
report_call_event (EXOSIP_CALL_NOANSWER, jc, jd, NULL);
eXosip_dialog_free (jd);
__eXosip_wakeup ();
return 0;
}
}
if (tr != NULL && (tr->state == IST_TERMINATED || tr->state == ICT_TERMINATED))
{
if (tr == jc->c_inc_tr)
{
if (jc->c_inc_tr->last_response == NULL)
{
/* OSIP_TRACE(osip_trace(__FILE__,__LINE__,OSIP_INFO2,NULL,
"eXosip: eXosip_release_aborted_calls transaction with no answer\n")); */
} else if (MSG_IS_STATUS_3XX (jc->c_inc_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls answered with a 3xx\n"));
__eXosip_release_call (jc, jd);
return 0;
} else if (MSG_IS_STATUS_4XX (jc->c_inc_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls answered with a 4xx\n"));
__eXosip_release_call (jc, jd);
return 0;
} else if (MSG_IS_STATUS_5XX (jc->c_inc_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls answered with a 5xx\n"));
__eXosip_release_call (jc, jd);
return 0;
} else if (MSG_IS_STATUS_6XX (jc->c_inc_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls answered with a 6xx\n"));
__eXosip_release_call (jc, jd);
return 0;
}
} else if (tr == jc->c_out_tr)
{
if (jc->c_out_tr->last_response == NULL)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls completed with no answer\n"));
__eXosip_release_call (jc, jd);
return 0;
} else if (MSG_IS_STATUS_3XX (jc->c_out_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls completed answered with 3xx\n"));
__eXosip_release_call (jc, jd);
return 0;
} else if (MSG_IS_STATUS_4XX (jc->c_out_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls completed answered with 4xx\n"));
__eXosip_release_call (jc, jd);
return 0;
} else if (MSG_IS_STATUS_5XX (jc->c_out_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls completed answered with 5xx\n"));
__eXosip_release_call (jc, jd);
return 0;
} else if (MSG_IS_STATUS_6XX (jc->c_out_tr->last_response))
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_aborted_calls completed answered with 6xx\n"));
__eXosip_release_call (jc, jd);
return 0;
}
}
}
return -1;
}
void
eXosip_release_terminated_calls (void)
{
eXosip_dialog_t *jd;
eXosip_dialog_t *jdnext;
eXosip_call_t *jc;
eXosip_call_t *jcnext;
time_t now = time (NULL);
int pos;
for (jc = eXosip.j_calls; jc != NULL;)
{
jcnext = jc->next;
/* free call terminated with a BYE */
for (jd = jc->c_dialogs; jd != NULL;)
{
jdnext = jd->next;
if (0 == eXosip_pendingosip_transaction_exist (jc, jd))
{
} else if (0 == eXosip_release_finished_transactions (jc, jd))
{
} else if (0 == eXosip_release_finished_calls (jc, jd))
{
jd = jc->c_dialogs;
} else if (0 == eXosip_release_aborted_calls (jc, jd))
{
jdnext = NULL;
} else if (jd->d_id == -1)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO2, NULL,
"eXosip: eXosip_release_terminated_calls delete a removed dialog (cid=%i did=%i)\n",
jc->c_id, jd->d_id));
/* Remove existing reference to the dialog from transactions! */
__eXosip_call_remove_dialog_reference_in_call (jc, jd);
REMOVE_ELEMENT (jc->c_dialogs, jd);
eXosip_dialog_free (jd);
jd = jc->c_dialogs;
}
jd = jdnext;
}
jc = jcnext;
}
for (jc = eXosip.j_calls; jc != NULL;)
{
jcnext = jc->next;
if (jc->c_dialogs == NULL)
{
/* release call for options requests */
if (jc->c_inc_options_tr != NULL)
{
if (jc->c_inc_options_tr->state == NIST_TERMINATED)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"eXosip: remove an incoming OPTIONS with no final answer\n"));
__eXosip_release_call (jc, NULL);
} else if (jc->c_inc_options_tr->state != NIST_TERMINATED
&& jc->c_inc_options_tr->birth_time + 180 < now)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"eXosip: remove an incoming OPTIONS with no final answer\n"));
__eXosip_release_call (jc, NULL);
}
} else if (jc->c_out_options_tr != NULL)
{
if (jc->c_out_options_tr->state == NICT_TERMINATED)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"eXosip: remove an outgoing OPTIONS with no final answer\n"));
__eXosip_release_call (jc, NULL);
} else if (jc->c_out_options_tr->state != NIST_TERMINATED
&& jc->c_out_options_tr->birth_time + 180 < now)
{
OSIP_TRACE (osip_trace
(__FILE__, __LINE__, OSIP_INFO1, NULL,
"eXosip: remove an outgoing OPTIONS with no final answer\n"));
__eXosip_release_call (jc, NULL);
}
} else if (jc->c_inc_tr != NULL
&& jc->c_inc_tr->state != IST_TERMINATED
&& jc->c_inc_tr->birth_time + 180 < now)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"eXosip: remove an incoming call with no final answer\n"));
__eXosip_release_call (jc, NULL);
} else if (jc->c_out_tr != NULL
&& jc->c_out_tr->state != ICT_TERMINATED
&& jc->c_out_tr->birth_time + 180 < now)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"eXosip: remove an outgoing call with no final answer\n"));
__eXosip_release_call (jc, NULL);
} else if (jc->c_inc_tr != NULL && jc->c_inc_tr->state != IST_TERMINATED)
{
} else if (jc->c_out_tr != NULL && jc->c_out_tr->state != ICT_TERMINATED)
{
} else /* no active pending transaction */
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"eXosip: remove a call\n"));
__eXosip_release_call (jc, NULL);
}
}
jc = jcnext;
}
pos = 0;
while (!osip_list_eol (eXosip.j_transactions, pos))
{
osip_transaction_t *tr =
(osip_transaction_t *) osip_list_get (eXosip.j_transactions, pos);
if (tr->state == IST_TERMINATED || tr->state == ICT_TERMINATED
|| tr->state == NICT_TERMINATED || tr->state == NIST_TERMINATED)
{ /* free (transaction is already removed from the oSIP stack) */
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO3, NULL,
"Release a terminated transaction\n"));
osip_list_remove (eXosip.j_transactions, pos);
__eXosip_delete_jinfo (tr);
osip_transaction_free (tr);
} else if (tr->birth_time + 180 < now) /* Wait a max of 2 minutes */
{
osip_list_remove (eXosip.j_transactions, pos);
__eXosip_delete_jinfo (tr);
osip_transaction_free (tr);
} else
pos++;
}
}
void
eXosip_release_terminated_registrations (void)
{
eXosip_reg_t *jr;
eXosip_reg_t *jrnext;
time_t now = time (NULL);
for (jr = eXosip.j_reg; jr != NULL;)
{
jrnext = jr->next;
if (jr->r_reg_period == 0 && jr->r_last_tr != NULL)
{
if (now - jr->r_last_tr->birth_time > 60)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"Release a terminated registration\n"));
REMOVE_ELEMENT (eXosip.j_reg, jr);
eXosip_reg_free (jr);
} else if (jr->r_last_tr->last_response != NULL
&& jr->r_last_tr->last_response->status_code >= 200
&& jr->r_last_tr->last_response->status_code <= 299)
{
OSIP_TRACE (osip_trace (__FILE__, __LINE__, OSIP_INFO1, NULL,
"Release a terminated registration with 2xx\n"));
REMOVE_ELEMENT (eXosip.j_reg, jr);
eXosip_reg_free (jr);
}
}
jr = jrnext;
}
return;
}
syntax highlighted by Code2HTML, v. 0.9.1