summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsip-ua
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2005-11-21 01:55:47 +0000
committerBenny Prijono <bennylp@teluu.com>2005-11-21 01:55:47 +0000
commit5f1de1bbb341ea1dc1d27d9bf35764b643ef904a (patch)
treed18b0365b69b8b488a0b2b2bd715e9f14f77b505 /pjsip/src/pjsip-ua
parent9f4da35e676737f830a90a18de08440cf0f6cdf9 (diff)
Set svn:eol-style property
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@65 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src/pjsip-ua')
-rw-r--r--pjsip/src/pjsip-ua/sip_dialog.c3604
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c1022
-rw-r--r--pjsip/src/pjsip-ua/sip_ua.c1012
-rw-r--r--pjsip/src/pjsip-ua/sip_ua_private.h72
4 files changed, 2855 insertions, 2855 deletions
diff --git a/pjsip/src/pjsip-ua/sip_dialog.c b/pjsip/src/pjsip-ua/sip_dialog.c
index ac110412..bb4861b8 100644
--- a/pjsip/src/pjsip-ua/sip_dialog.c
+++ b/pjsip/src/pjsip-ua/sip_dialog.c
@@ -1,1802 +1,1802 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program 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.
- *
- * This program 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
- */
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_ua.h>
-#include <pjsip_mod_ua/sip_ua_private.h>
-#include <pjsip/sip_transport.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_uri.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_parser.h>
-#include <pj/string.h>
-#include <pj/log.h>
-#include <pj/os.h>
-#include <pj/guid.h>
-#include <pj/except.h>
-#include <pj/pool.h>
-
-/* TLS to keep dialog lock record (initialized by UA) */
-int pjsip_dlg_lock_tls_id;
-
-struct dialog_lock_data
-{
- struct dialog_lock_data *prev;
- pjsip_dlg *dlg;
- int is_alive;
-};
-
-/*
- * Static function prototypes.
- */
-static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
- pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq );
-static int dlg_on_all_state_pre( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_all_state_post( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_null( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_incoming( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_calling( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_connecting( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_established( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_disconnected( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-static int dlg_on_state_terminated( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-
-/*
- * Dialog state handlers.
- */
-static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *,
- pjsip_event *) =
-{
- &dlg_on_state_null,
- &dlg_on_state_incoming,
- &dlg_on_state_calling,
- &dlg_on_state_proceeding,
- &dlg_on_state_connecting,
- &dlg_on_state_established,
- &dlg_on_state_disconnected,
- &dlg_on_state_terminated,
-};
-
-/*
- * Dialog state names.
- */
-static const char* dlg_state_names[] =
-{
- "STATE_NULL",
- "STATE_INCOMING",
- "STATE_CALLING",
- "STATE_PROCEEDING",
- "STATE_CONNECTING",
- "STATE_ESTABLISHED",
- "STATE_DISCONNECTED",
- "STATE_TERMINATED",
-};
-
-
-/*
- * Get the dialog string state, normally for logging purpose.
- */
-const char *pjsip_dlg_state_str(pjsip_dlg_state_e state)
-{
- return dlg_state_names[state];
-}
-
-/* Lock dialog mutex. */
-static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
-{
- struct dialog_lock_data *prev;
-
- pj_mutex_lock(dlg->mutex);
- prev = pj_thread_local_get(pjsip_dlg_lock_tls_id);
- lck->prev = prev;
- lck->dlg = dlg;
- lck->is_alive = 1;
- pj_thread_local_set(pjsip_dlg_lock_tls_id, lck);
-}
-
-/* Carefully unlock dialog mutex, protect against situation when the dialog
- * has already been destroyed.
- */
-static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
-{
- pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck);
- pj_assert(dlg == lck->dlg);
-
- pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev);
- if (lck->is_alive)
- pj_mutex_unlock(dlg->mutex);
-
- return lck->is_alive ? 0 : -1;
-}
-
-/*
- * This is called by dialog's FSM to change dialog's state.
- */
-static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event);
-
- PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)",
- pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state),
- event ? pjsip_event_str(event->type) : "",
- event ? pjsip_event_str(event->src_type) : "",
- event ? event->src.data : NULL));
-
- dlg->state = state;
- dlg->handle_tsx_event = dlg_state_handlers[state];
-}
-
-/*
- * Invoke dialog's callback.
- * This function is called by dialog's FSM, and interpret the event and call
- * the corresponding callback registered by application.
- */
-static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
- pjsip_event *event )
-{
- pjsip_dlg_callback *cb = dlg->ua->dlg_cb;
- if (!cb) {
- PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered)."));
- return;
- }
-
- /* Low level event: call the all-events callback. */
- if (cb->on_all_events) {
- (*cb->on_all_events)(dlg, dlg_event, event);
- }
-
- /* Low level event: call the tx/rx callback if this is a tx/rx event. */
- if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx)
- {
- (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data);
- }
- else if (event->type == PJSIP_EVENT_TX_MSG &&
- event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg)
- {
- (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata);
- }
- else if (event->type == PJSIP_EVENT_RX_MSG &&
- event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) {
- (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata);
- }
-
- /* Now trigger high level events.
- * High level event should only occurs when dialog's state has changed,
- * except for on_provisional, which may be called multiple times whenever
- * response message is sent
- */
- if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING &&
- (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) &&
- event->obj.tsx == dlg->invite_tsx)
- {
- /* Sent/received provisional responses. */
- if (cb->on_provisional)
- (*cb->on_provisional)(dlg, event->obj.tsx, event);
- }
-
- if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) {
- if (cb->on_mid_call_events)
- (*cb->on_mid_call_events)(dlg, event);
- return;
- }
-
- if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED)
- return;
-
- if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) {
-
- /* New incoming dialog. */
- pj_assert (event->src_type == PJSIP_EVENT_RX_MSG);
- (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) {
-
- /* Dialog has just sent the first INVITE. */
- if (cb->on_calling) {
- (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata);
- }
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) {
-
- if (cb->on_disconnected)
- (*cb->on_disconnected)(dlg, event);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) {
-
- if (cb->on_terminated)
- (*cb->on_terminated)(dlg);
-
- pjsip_ua_destroy_dialog(dlg);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) {
-
- if (cb->on_connecting)
- (*cb->on_connecting)(dlg, event);
-
- } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
-
- if (cb->on_established)
- (*cb->on_established)(dlg, event);
- }
-}
-
-/*
- * This callback receives event from the transaction layer (via User Agent),
- * or from dialog timer (for 200/INVITE or ACK retransmission).
- */
-void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int status = 0;
- struct dialog_lock_data lck;
-
- PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)",
- pjsip_dlg_state_str(dlg->state),
- pjsip_event_str(event->type),
- pjsip_event_str(event->src_type)));
-
-
- lock_dialog(dlg, &lck);
-
- status = dlg_on_all_state_pre( dlg, tsx, event);
-
- if (status==0) {
- status = (*dlg->handle_tsx_event)(dlg, tsx, event);
- }
- if (status==0) {
- status = dlg_on_all_state_post( dlg, tsx, event);
- }
-
- unlock_dialog(dlg, &lck);
-}
-
-/*
- * This function contains common processing in all states, and it is called
- * before the FSM is invoked.
- */
-static int dlg_on_all_state_pre( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event)
-
- if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
- return 0;
-
- if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING ||
- tsx->state==PJSIP_TSX_STATE_TRYING))
- {
- ++dlg->pending_tsx_count;
-
- } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED)
- {
- --dlg->pending_tsx_count;
- if (tsx == dlg->invite_tsx)
- dlg->invite_tsx = NULL;
- }
-
- if (tsx->method.id == PJSIP_INVITE_METHOD) {
- tsx->handle_ack = 1;
- }
- return 0;
-}
-
-
-/*
- * This function contains common processing in all states, and it is called
- * after the FSM is invoked.
- */
-static int dlg_on_all_state_post( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(event)
-
- if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) {
- if (dlg->pending_tsx_count == 0 &&
- dlg->state != PJSIP_DIALOG_STATE_CONNECTING &&
- dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED &&
- dlg->state != PJSIP_DIALOG_STATE_TERMINATED)
- {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- return -1;
- }
- }
-
- return 0;
-}
-
-
-/*
- * Internal function to initialize dialog, contains common initialization
- * for both UAS and UAC dialog.
- */
-static pj_status_t dlg_init( pjsip_dlg *dlg )
-{
- /* Init mutex. */
- dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
- if (!dlg->mutex) {
- PJ_PERROR((dlg->obj_name, "pj_mutex_create()"));
- return -1;
- }
-
- /* Init route-set (Initially empty) */
- pj_list_init(&dlg->route_set);
-
- /* Init auth credential list. */
- pj_list_init(&dlg->auth_sess);
-
- return PJ_SUCCESS;
-}
-
-/*
- * This one is called just before dialog is destroyed.
- * It is called while mutex is held.
- */
-PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg )
-{
- struct dialog_lock_data *lck;
-
- //PJ_TODO(CHECK_THIS);
- pj_assert(dlg->pending_tsx_count == 0);
-
- /* Mark dialog as dead. */
- lck = pj_thread_local_get(pjsip_dlg_lock_tls_id);
- while (lck) {
- if (lck->dlg == dlg)
- lck->is_alive = 0;
- lck = lck->prev;
- }
-}
-
-/*
- * Initialize dialog from the received request.
- * This is an internal function which is called by the User Agent (sip_ua.c),
- * and it will initialize most of dialog's properties with values from the
- * received message.
- */
-pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata )
-{
- pjsip_msg *msg = rdata->msg;
- pjsip_to_hdr *to;
- pjsip_contact_hdr *contact;
- pjsip_name_addr *name_addr;
- pjsip_url *url;
- unsigned flag;
- pjsip_event event;
-
- pj_assert(dlg && rdata);
-
- PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata));
-
- /* Must be an INVITE request. */
- pj_assert(msg->type == PJSIP_REQUEST_MSG &&
- msg->line.req.method.id == PJSIP_INVITE_METHOD);
-
- /* Init general dialog data. */
- if (dlg_init(dlg) != PJ_SUCCESS) {
- return -1;
- }
-
- /* Get the To header. */
- to = rdata->to;
-
- /* Copy URI in the To header as our local URI. */
- dlg->local.info = pjsip_hdr_clone( dlg->pool, to);
-
- /* Set tag in the local info. */
- dlg->local.info->tag = dlg->local.tag;
-
- /* Create local Contact to be advertised in the response.
- * At the moment, just copy URI from the local URI as our contact.
- */
- dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->local.contact->star = 0;
- name_addr = (pjsip_name_addr *)dlg->local.info->uri;
- dlg->local.contact->uri = (pjsip_uri*) name_addr;
- url = (pjsip_url*) name_addr->uri;
- //url->port = rdata->via->sent_by.port;
- //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) );
-
- /* Save remote URI. */
- dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from );
- pjsip_fromto_set_to( dlg->remote.info );
- pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag );
-
- /* Save remote Contact. */
- contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
- } else {
- PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s",
- pj_sockaddr_get_str_addr(&rdata->addr)));
- dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->remote.contact->uri = dlg->remote.info->uri;
- }
-
- /* Save Call-ID. */
- dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
- pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id );
-
- /* Initialize local CSeq and save remote CSeq.*/
- dlg->local.cseq = rdata->timestamp.sec & 0xFFFF;
- dlg->remote.cseq = rdata->cseq->cseq;
-
- /* Secure? */
- flag = pjsip_transport_get_flag(rdata->transport);
- dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
-
- /* Initial state is NULL. */
- event.type = event.src_type = PJSIP_EVENT_RX_MSG;
- event.src.rdata = rdata;
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
-
- PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata));
- return PJ_SUCCESS;
-}
-
-/*
- * Set the contact details.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
- const pj_str_t *contact )
-{
- pjsip_uri *local_uri;
- pj_str_t tmp;
-
- pj_strdup_with_null(dlg->pool, &tmp, contact);
- local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (local_uri == NULL) {
- PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI"));
- return -1;
- }
-
- dlg->local.contact->star = 0;
- dlg->local.contact->uri = local_uri;
- return 0;
-}
-
-/*
- * Set route set.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
- const pjsip_route_hdr *route_set )
-{
- pjsip_route_hdr *hdr;
-
- pj_list_init(&dlg->route_set);
- hdr = route_set->next;
- while (hdr != route_set) {
- pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr);
- pj_list_insert_before( &dlg->route_set, cloned);
- hdr = hdr->next;
- }
- return 0;
-}
-
-/*
- * Set route set without cloning the header.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
- pjsip_route_hdr *route_set)
-{
- pjsip_route_hdr *hdr;
-
- pj_list_init(&dlg->route_set);
- hdr = route_set->next;
- while (hdr != route_set) {
- pj_list_insert_before( &dlg->route_set, hdr);
- hdr = hdr->next;
- }
- return 0;
-}
-
-/*
- * Application calls this function when it wants to initiate an outgoing
- * dialog (incoming dialogs are created automatically by UA when it receives
- * INVITE, by calling pjsip_dlg_init_from_rdata()).
- * This function should initialize most of the dialog's properties.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
- const pj_str_t *c_local_info,
- const pj_str_t *c_remote_info,
- const pj_str_t *c_target)
-{
- pj_time_val tv;
- pjsip_event event;
- pj_str_t buf;
-
- if (!dlg || !c_local_info || !c_remote_info) {
- pj_assert(dlg && c_local_info && c_remote_info);
- return -1;
- }
-
- PJ_LOG(5, (dlg->obj_name, "initializing"));
-
- /* Init general dialog */
- if (dlg_init(dlg) != PJ_SUCCESS) {
- return -1;
- }
-
- /* Duplicate local info. */
- pj_strdup_with_null( dlg->pool, &buf, c_local_info);
-
- /* Build local URI. */
- dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (dlg->local.target == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid local URI %s", buf.ptr));
- return -1;
- }
-
- /* Set local URI. */
- dlg->local.info = pjsip_from_hdr_create(dlg->pool);
- dlg->local.info->uri = dlg->local.target;
- dlg->local.info->tag = dlg->local.tag;
-
- /* Create local Contact to be advertised in the response. */
- dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
- dlg->local.contact->star = 0;
- dlg->local.contact->uri = dlg->local.target;
-
- /* Set remote URI. */
- dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
-
- /* Duplicate to buffer. */
- pj_strdup_with_null( dlg->pool, &buf, c_remote_info);
-
- /* Build remote info. */
- dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (dlg->remote.info->uri == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid remote URI %s", buf.ptr));
- return -1;
- }
-
- /* Set remote Contact initially equal to the remote URI. */
- dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
- dlg->remote.contact->star = 0;
- dlg->remote.contact->uri = dlg->remote.info->uri;
-
- /* Set initial remote target. */
- if (c_target != NULL) {
- pj_strdup_with_null( dlg->pool, &buf, c_target);
- dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0);
- if (dlg->remote.target == NULL) {
- PJ_LOG(2, (dlg->obj_name,
- "pjsip_dlg_init: invalid remote target %s", buf.ptr));
- return -1;
- }
- } else {
- dlg->remote.target = dlg->remote.info->uri;
- }
-
- /* Create globally unique Call-ID */
- dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
- pj_create_unique_string( dlg->pool, &dlg->call_id->id );
-
- /* Local and remote CSeq */
- pj_gettimeofday(&tv);
- dlg->local.cseq = tv.sec & 0xFFFF;
- dlg->remote.cseq = 0;
-
- /* Initial state is NULL. */
- event.type = event.src_type = PJSIP_EVENT_TX_MSG;
- event.src.data = NULL;
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
-
- /* Done. */
- PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s",
- pjsip_role_name(dlg->role),
- c_local_info->slen, c_local_info->ptr,
- c_remote_info->slen, c_remote_info->ptr));
-
- return PJ_SUCCESS;
-}
-
-/*
- * Set credentials.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
- int count,
- const pjsip_cred_info *cred)
-{
- if (count > 0) {
- dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info));
- pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info));
- }
- dlg->cred_count = count;
- return 0;
-}
-
-/*
- * Create a new request within dialog (i.e. after the dialog session has been
- * established). The construction of such requests follows the rule in
- * RFC3261 section 12.2.1.
- */
-static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
- pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq )
-{
- pjsip_tx_data *tdata;
- pjsip_contact_hdr *contact;
- pjsip_route_hdr *route, *end_list;
-
- /* Contact Header field.
- * Contact can only be present in requests that establish dialog (in the
- * core SIP spec, only INVITE).
- */
- if (method->id == PJSIP_INVITE_METHOD)
- contact = dlg->local.contact;
- else
- contact = NULL;
-
- tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt,
- method,
- dlg->remote.target,
- dlg->local.info,
- dlg->remote.info,
- contact,
- dlg->call_id,
- cseq,
- NULL);
- if (!tdata) {
- PJ_THROW(1);
- return;
- }
-
- /* Just copy dialog route-set to Route header.
- * The transaction will do the processing as specified in Section 12.2.1
- * of RFC 3261 in function tsx_process_route() in sip_transaction.c.
- */
- route = dlg->route_set.next;
- end_list = &dlg->route_set;
- for (; route != end_list; route = route->next ) {
- pjsip_route_hdr *r;
- r = pjsip_hdr_shallow_clone( tdata->pool, route );
- pjsip_routing_hdr_set_route(r);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r);
- }
-
- /* Copy authorization headers. */
- pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info);
-
- *p_tdata = tdata;
-}
-
-
-/*
- * This function is called by application to create new outgoing request
- * message for this dialog. After the request is created, application can
- * modify the message (such adding headers), and eventually send the request.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
- const pjsip_method *method,
- int cseq)
-{
- PJ_USE_EXCEPTION;
- struct dialog_lock_data lck;
- pjsip_tx_data *tdata = NULL;
-
- pj_assert(dlg != NULL && method != NULL);
- if (!dlg || !method) {
- return NULL;
- }
-
- PJ_LOG(5, (dlg->obj_name, "Creating request"));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Use outgoing CSeq and increment it by one. */
- if (cseq < 0)
- cseq = dlg->local.cseq + 1;
-
- PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d",
- method->name.slen, method->name.ptr, cseq));
-
- /* Create the request. */
- PJ_TRY {
- dlg_create_request_throw(&tdata, dlg, method, cseq);
- PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name));
- }
- PJ_DEFAULT {
- /* Failed! Delete transmit data. */
- if (tdata) {
- pjsip_tx_data_dec_ref( tdata );
- tdata = NULL;
- }
- }
- PJ_END;
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * Sends request.
- * Select the transport for the request message
- */
-static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata )
-{
- pjsip_transaction *tsx;
- pj_status_t status = PJ_SUCCESS;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL && tdata != NULL);
- if (!dlg || !tdata) {
- return -1;
- }
-
- PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Create a new transaction. */
- tsx = pjsip_endpt_create_tsx( dlg->ua->endpt );
- if (!tsx) {
- unlock_dialog(dlg, &lck);
- return -1;
- }
-
- PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name));
-
- /* Initialize transaction */
- tsx->module_data[dlg->ua->mod_id] = dlg;
- status = pjsip_tsx_init_uac( tsx, tdata );
- if (status != PJ_SUCCESS) {
- unlock_dialog(dlg, &lck);
- pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx );
- return -1;
- }
- pjsip_endpt_register_tsx( dlg->ua->endpt, tsx );
-
- /* Start the transaction. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return status;
-}
-
-/*
- * This function can be called by application to send ANY outgoing message
- * to remote party.
- */
-PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata )
-{
- pj_status_t status;
- int tsx_status;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL && tdata != NULL);
- if (!dlg || !tdata) {
- return -1;
- }
-
- lock_dialog(dlg, &lck);
-
- if (tdata->msg->type == PJSIP_REQUEST_MSG) {
- int request_cseq;
- pjsip_msg *msg = tdata->msg;
- pjsip_cseq_hdr *cseq_hdr;
-
- switch (msg->line.req.method.id) {
- case PJSIP_CANCEL_METHOD:
-
- /* Check the INVITE transaction state. */
- tsx_status = dlg->invite_tsx->status_code;
- if (tsx_status >= 200) {
- /* Already terminated. Can't cancel. */
- status = -1;
- goto on_return;
- }
-
- /* If we've got provisional response, then send CANCEL and wait for
- * the response to INVITE to arrive. Otherwise just send CANCEL and
- * terminate the INVITE.
- */
- if (tsx_status < 100) {
- pjsip_tsx_terminate( dlg->invite_tsx,
- PJSIP_SC_REQUEST_TERMINATED);
- status = 0;
- goto on_return;
- }
-
- status = 0;
- request_cseq = dlg->invite_tsx->cseq;
- break;
-
- case PJSIP_ACK_METHOD:
- /* Sending ACK outside of transaction is not supported at present! */
- pj_assert(0);
- status = 0;
- request_cseq = dlg->local.cseq;
- break;
-
- case PJSIP_INVITE_METHOD:
- /* For an initial INVITE, reset dialog state to NULL so we get
- * 'normal' UAC notifications such as on_provisional(), etc.
- * Initial INVITE is the request that is sent when the dialog has
- * not been established yet. It's not necessarily the first INVITE
- * sent, as when the Authorization fails, subsequent INVITE are also
- * considered as an initial INVITE.
- */
- if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
- /* Set state to NULL. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL);
-
- } else {
- /* This is a re-INVITE */
- }
- status = 0;
- request_cseq = dlg->local.cseq + 1;
- break;
-
- default:
- status = 0;
- request_cseq = dlg->local.cseq + 1;
- break;
- }
-
- if (status != 0)
- goto on_return;
-
- /* Update dialog's local CSeq, if necessary. */
- if (request_cseq != dlg->local.cseq)
- dlg->local.cseq = request_cseq;
-
- /* Update CSeq header in the request. */
- cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
- PJSIP_H_CSEQ, NULL);
- pj_assert(cseq_hdr != NULL);
-
- /* Update the CSeq */
- cseq_hdr->cseq = request_cseq;
-
- /* Force the whole message to be re-printed. */
- pjsip_tx_data_invalidate_msg( tdata );
-
- /* Now send the request. */
- status = dlg_send_request(dlg, tdata);
-
- } else {
- /*
- * This is only valid for sending response to INVITE!
- */
- pjsip_cseq_hdr *cseq_hdr;
-
- if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) {
- status = -1;
- goto on_return;
- }
-
- cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
- PJSIP_H_CSEQ, NULL);
- pj_assert(cseq_hdr);
-
- if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) {
- status = -1;
- goto on_return;
- }
-
- pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq);
-
- pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata);
- status = 0;
- }
-
-on_return:
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- /* Whatever happen delete the message. */
- pjsip_tx_data_dec_ref( tdata );
-
- return status;
-}
-
-/*
- * Sends outgoing invitation.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg )
-{
- pjsip_method method;
- struct dialog_lock_data lck;
- const pjsip_allow_hdr *allow_hdr;
- pjsip_tx_data *tdata;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to send invitation"));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Create request. */
- pjsip_method_set( &method, PJSIP_INVITE_METHOD);
- tdata = pjsip_dlg_create_request( dlg, &method, -1 );
- if (tdata == NULL) {
- unlock_dialog(dlg, &lck);
- return NULL;
- }
-
- /* Invite SHOULD contain "Allow" header. */
- allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
- if (allow_hdr) {
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
- }
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * Cancel pending outgoing dialog invitation.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg )
-{
- pjsip_tx_data *tdata = NULL;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to cancel invitation"));
-
- lock_dialog(dlg, &lck);
-
- /* Check the INVITE transaction. */
- if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
- "no INVITE transaction found"));
- goto on_return;
- }
-
- /* Construct the CANCEL request. */
- tdata = pjsip_endpt_create_cancel( dlg->ua->endpt,
- dlg->invite_tsx->last_tx );
- if (tdata == NULL) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
- "unable to construct request"));
- goto on_return;
- }
-
- /* Add reference counter to tdata. */
- pjsip_tx_data_add_ref(tdata);
-
-on_return:
- unlock_dialog(dlg, &lck);
- return tdata;
-}
-
-
-/*
- * Answer incoming dialog invitation, with either provisional responses
- * or a final response.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code )
-{
- pjsip_tx_data *tdata = NULL;
- pjsip_msg *msg;
- struct dialog_lock_data lck;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code));
-
- /* Lock dialog. */
- lock_dialog(dlg, &lck);
-
- /* Must have pending INVITE. */
- if (dlg->invite_tsx == NULL) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found"));
- goto on_return;
- }
- /* Must be UAS. */
- if (dlg->role != PJSIP_ROLE_UAS) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS"));
- goto on_return;
- }
- /* Must have not answered with final response before. */
- if (dlg->invite_tsx->status_code >= 200) {
- PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated "
- "with status %d", dlg->invite_tsx->status_code));
- goto on_return;
- }
-
- /* Get transmit data and the message.
- * We will rewrite the message with a new status code.
- */
- only if tdata is not pending!!!
- tdata = dlg->invite_tsx->last_tx;
- msg = tdata->msg;
-
- /* Set status code and reason phrase. */
- if (code < 100 || code >= 700) code = 500;
- msg->line.status.code = code;
- msg->line.status.reason = *pjsip_get_status_text(code);
-
- /* For 2xx response, Contact and Record-Route must be added. */
- if (PJSIP_IS_STATUS_IN_CLASS(code,200)) {
- const pjsip_allow_hdr *allow_hdr;
-
- if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) {
- pjsip_contact_hdr *contact;
- contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact);
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact );
- }
-
- /* 2xx response MUST contain "Allow" header. */
- allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
- if (allow_hdr) {
- pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
- }
- }
-
- /* for all but 100 responses, To-tag must be set. */
- if (code != 100) {
- pjsip_to_hdr *to;
- to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
- to->tag = dlg->local.tag;
- }
-
- /* Reset packet buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
-
- /* Add reference counter */
- pjsip_tx_data_add_ref(tdata);
-
-on_return:
-
- /* Unlock dialog. */
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-
-/*
- * Send BYE request to terminate the dialog's session.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg )
-{
- pjsip_method method;
- struct dialog_lock_data lck;
- pjsip_tx_data *tdata;
-
- if (!dlg) {
- pj_assert(dlg != NULL);
- return NULL;
- }
-
- PJ_LOG(4, (dlg->obj_name, "request to terminate session"));
-
- lock_dialog(dlg, &lck);
-
- pjsip_method_set( &method, PJSIP_BYE_METHOD);
- tdata = pjsip_dlg_create_request( dlg, &method, -1 );
-
- unlock_dialog(dlg, &lck);
-
- return tdata;
-}
-
-/*
- * High level function to disconnect dialog's session. Depending on dialog's
- * state, this function will either send CANCEL, final response, or BYE to
- * trigger the disconnection. A status code must be supplied, which will be
- * sent if dialog will be transmitting a final response to INVITE.
- */
-PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg,
- int status_code )
-{
- pjsip_tx_data *tdata = NULL;
-
- pj_assert(dlg != NULL);
- if (!dlg) {
- return NULL;
- }
-
- switch (dlg->state) {
- case PJSIP_DIALOG_STATE_INCOMING:
- tdata = pjsip_dlg_answer(dlg, status_code);
- break;
-
- case PJSIP_DIALOG_STATE_CALLING:
- tdata = pjsip_dlg_cancel(dlg);
- break;
-
- case PJSIP_DIALOG_STATE_PROCEEDING:
- if (dlg->role == PJSIP_ROLE_UAC) {
- tdata = pjsip_dlg_cancel(dlg);
- } else {
- tdata = pjsip_dlg_answer(dlg, status_code);
- }
- break;
-
- case PJSIP_DIALOG_STATE_ESTABLISHED:
- tdata = pjsip_dlg_bye(dlg);
- break;
-
- default:
- PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()",
- dlg_state_names[dlg->state]));
- break;
- }
-
- return tdata;
-}
-
-/*
- * Handling of the receipt of 2xx/INVITE response.
- */
-static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg,
- pjsip_event *event )
-{
- pjsip_msg *msg;
- pjsip_contact_hdr *contact;
- pjsip_hdr *hdr, *end_hdr;
- pjsip_method method;
- pjsip_tx_data *ack_tdata;
-
- /* Get the message */
- msg = event->src.rdata->msg;
-
- /* Update remote's tag information. */
- pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag);
-
- /* Copy Contact information in the 2xx/INVITE response to dialog's.
- * remote contact
- */
- contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
- } else {
- /* duplicate contact from "From" header (?) */
- PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!"));
- dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
- dlg->remote.contact->uri = dlg->remote.info->uri;
- }
-
- /* Copy Record-Route header (in reverse order) as dialog's route-set,
- * overwriting previous route-set, if any, even if the received route-set
- * is empty.
- */
- pj_list_init(&dlg->route_set);
- end_hdr = &msg->hdr;
- for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) {
- if (hdr->type == PJSIP_H_RECORD_ROUTE) {
- pjsip_route_hdr *r;
- r = pjsip_hdr_clone(dlg->pool, hdr);
- pjsip_routing_hdr_set_route(r);
- pj_list_insert_before(&dlg->route_set, r);
- }
- }
-
- /* On receipt of 200/INVITE response, send ACK.
- * This ack must be saved and retransmitted whenever we receive
- * 200/INVITE retransmission, until 64*T1 seconds elapsed.
- */
- pjsip_method_set( &method, PJSIP_ACK_METHOD);
- ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq);
- if (ack_tdata == NULL) {
- //PJ_TODO(HANDLE_CREATE_ACK_FAILURE)
- PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request"));
- return;
- }
-
- /* Send with the transaction. */
- pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata);
-
- /* Decrement reference counter because pjsip_dlg_create_request
- * automatically increments the request.
- */
- pjsip_tx_data_dec_ref( ack_tdata );
-}
-
-/*
- * State NULL, before any events have been received.
- */
-static int dlg_on_state_null( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG)
- {
- pjsip_hdr *hdr, *hdr_list;
-
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Save the INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Change state to INCOMING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event);
-
- /* Create response buffer. */
- tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100);
- pjsip_tx_data_add_ref(tsx->last_tx);
-
- /* Copy the Record-Route headers into dialog's route_set, maintaining
- * the order.
- */
- pj_list_init(&dlg->route_set);
- hdr_list = &event->src.rdata->msg->hdr;
- hdr = hdr_list->next;
- while (hdr != hdr_list) {
- if (hdr->type == PJSIP_H_RECORD_ROUTE) {
- pjsip_route_hdr *route;
- route = pjsip_hdr_clone(dlg->pool, hdr);
- pjsip_routing_hdr_set_route(route);
- pj_list_insert_before(&dlg->route_set, route);
- }
- hdr = hdr->next;
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_TX_MSG)
- {
- pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
-
- /* Save the INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Change state to CALLING. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-/*
- * State INCOMING is after the (callee) dialog has been initialized with
- * the incoming request, but before any responses is sent by the dialog.
- */
-static int dlg_on_state_incoming( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- return dlg_on_state_proceeding_callee( dlg, tsx, event );
-}
-
-/*
- * State CALLING is after the (caller) dialog has sent outgoing invitation
- * but before any responses are received.
- */
-static int dlg_on_state_calling( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (tsx == dlg->invite_tsx) {
- return dlg_on_state_proceeding_caller( dlg, tsx, event );
- }
- return 0;
-}
-
-/*
- * State PROCEEDING is after provisional response is received.
- * Since the processing is similar to state CALLING, this function is also
- * called for CALLING state.
- */
-static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int dlg_is_terminated = 0;
-
- /* We only care about our INVITE transaction.
- * Ignore other transaction progression (such as CANCEL).
- */
- if (tsx != dlg->invite_tsx) {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- return 0;
- }
-
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) {
- switch (tsx->state) {
- case PJSIP_TSX_STATE_PROCEEDING:
- if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) {
- /* Change state to PROCEEDING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- } else {
- /* Also notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- break;
-
- case PJSIP_TSX_STATE_COMPLETED:
- /* Change dialog state. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- /* Update remote target, take it from the contact hdr. */
- pjsip_contact_hdr *contact;
- contact = pjsip_msg_find_hdr(event->src.rdata->msg,
- PJSIP_H_CONTACT, NULL);
- if (contact) {
- dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri);
- } else {
- PJ_LOG(4,(dlg->obj_name,
- "Warning: found no Contact hdr in 200/OK"));
- }
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
- } else if (tsx->status_code==401 || tsx->status_code==407) {
- /* Handle Authentication challenge. */
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
- dlg->pool, &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- /* Re-use original request, with a new transaction.
- * Need not to worry about CSeq, dialog will take care.
- */
- pjsip_dlg_send_msg(dlg, tdata);
- return 0;
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* Send ACK when dialog is connected. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- pj_assert(event->src_type == PJSIP_EVENT_RX_MSG);
- dlg_on_recv_2xx_invite(dlg, event);
- }
- break;
-
- case PJSIP_TSX_STATE_TERMINATED:
- /*
- * Transaction is terminated because of timeout or transport error.
- * To let the application go to normal state progression, call the
- * callback twice. First is to emulate disconnection, and then call
- * again (with state TERMINATED) to destroy the dialog.
- */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* The INVITE transaction will be destroyed, so release reference
- * to it.
- */
- dlg->invite_tsx = NULL;
-
- /* We should terminate the dialog now.
- * But it's possible that we have other pending transactions (for
- * example, outgoing CANCEL is in progress).
- * So destroy the dialog only if there's no other transaction.
- */
- if (dlg->pending_tsx_count == 0) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- dlg_is_terminated = 1;
- }
- break;
-
- default:
- pj_assert(0);
- break;
- }
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- return dlg_is_terminated ? -1 : 0;
-}
-
-/*
- * State PROCEEDING for UAS is after the callee send provisional response.
- * This function is also called for INCOMING state.
- */
-static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- int dlg_is_terminated = 0;
-
- pj_assert( dlg->invite_tsx != NULL );
-
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_TX_MSG &&
- tsx == dlg->invite_tsx)
- {
- switch (tsx->state) {
- case PJSIP_TSX_STATE_PROCEEDING:
- /* Change state to PROCEEDING */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- break;
-
- case PJSIP_TSX_STATE_COMPLETED:
- case PJSIP_TSX_STATE_TERMINATED:
- /* Change dialog state. */
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
- } else {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* If transaction is terminated in non-2xx situation,
- * terminate dialog as well. This happens when something unexpected
- * occurs, such as transport error.
- */
- if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
- !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200))
- {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- dlg_is_terminated = 1;
- }
- break;
-
- default:
- pj_assert(0);
- break;
- }
-
- } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->method.id == PJSIP_CANCEL_METHOD)
- {
- pjsip_tx_data *tdata;
-
- /* Check if sequence number matches the pending INVITE. */
- if (dlg->invite_tsx==NULL ||
- pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0)
- {
- PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE"));
-
- /* No matching INVITE transaction found. */
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST );
- pjsip_tsx_on_tx_msg(tsx, tdata);
- return 0;
- }
-
- /* Always respond the CANCEL with 200/CANCEL no matter what. */
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 200 );
- pjsip_tsx_on_tx_msg( tsx, tdata );
-
- /* Respond the INVITE transaction with 487, only if transaction has
- * not completed.
- */
- if (dlg->invite_tsx->last_tx) {
- if (dlg->invite_tsx->status_code < 200) {
- tdata = dlg->invite_tsx->last_tx;
- tdata->msg->line.status.code = 487;
- tdata->msg->line.status.reason = *pjsip_get_status_text(487);
- /* Reset packet buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
- pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
- } else {
- PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, "
- "Transaction already terminated "
- "with status %d",
- dlg->invite_tsx->status_code));
- }
- } else {
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 487);
- pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
- }
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return dlg_is_terminated ? -1 : 0;
-}
-
-static int dlg_on_state_proceeding( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (dlg->role == PJSIP_ROLE_UAC) {
- return dlg_on_state_proceeding_caller( dlg, tsx, event );
- } else {
- return dlg_on_state_proceeding_callee( dlg, tsx, event );
- }
-}
-
-static int dlg_on_state_connecting( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- if (tsx == dlg->invite_tsx) {
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- (tsx->state == PJSIP_TSX_STATE_TERMINATED ||
- tsx->state == PJSIP_TSX_STATE_COMPLETED ||
- tsx->state == PJSIP_TSX_STATE_CONFIRMED))
- {
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event);
- } else {
- /* Probably because we never get the ACK, or transport error
- * when sending ACK.
- */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
- }
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
- } else {
- /* Handle case when transaction is started when dialog is connecting
- * (e.g. BYE requests cross wire.
- */
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAS)
- {
- pjsip_tx_data *response;
-
- if (tsx->status_code >= 200)
- return 0;
-
- if (tsx->method.id == PJSIP_BYE_METHOD) {
- /* Set state to DISCONNECTED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- response = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 200);
- } else {
- response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- }
-
- if (response)
- pjsip_tsx_on_tx_msg(tsx, response);
-
- return 0;
- }
- }
- return 0;
-}
-
-static int dlg_on_state_established( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx)
-
- if (tsx && tsx->method.id == PJSIP_BYE_METHOD) {
- /* Set state to DISCONNECTED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- /* Answer with 200/BYE. */
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- pjsip_tx_data *tdata;
- tdata = pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata,
- 200 );
- if (tdata)
- pjsip_tsx_on_tx_msg( tsx, tdata );
- }
- } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) {
- pjsip_method_e method = event->src.rdata->cseq->method.id;
-
- PJ_TODO(PROPERLY_HANDLE_REINVITATION)
-
- /* Reinvitation. The message may be INVITE or an ACK. */
- if (method == PJSIP_INVITE_METHOD) {
- if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) {
- /* Section 14.2: A UAS that receives a second INVITE before it
- * sends the final response to a first INVITE with a lower
- * CSeq sequence number on the same dialog MUST return a 500
- * (Server Internal Error) response to the second INVITE and
- * MUST include a Retry-After header field with a randomly
- * chosen value of between 0 and 10 seconds.
- */
- pjsip_retry_after_hdr *hdr;
- pjsip_tx_data *tdata =
- pjsip_endpt_create_response(dlg->ua->endpt,
- event->src.rdata, 500);
-
- if (!tdata)
- return 0;
-
- /* Add Retry-After. */
- hdr = pjsip_retry_after_hdr_create(tdata->pool);
- hdr->ivalue = 9;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
-
- /* Send. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- return 0;
- }
-
- /* Keep this as our current INVITE transaction. */
- dlg->invite_tsx = tsx;
-
- /* Create response buffer. */
- tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 100);
- pjsip_tx_data_add_ref(tsx->last_tx);
-
- }
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event);
-
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-static int dlg_on_state_disconnected( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(tsx)
-
- /* Handle case when transaction is started when dialog is disconnected
- * (e.g. BYE requests cross wire.
- */
- if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAS)
- {
- pjsip_tx_data *response = NULL;
-
- if (tsx->status_code >= 200)
- return 0;
-
- if (tsx->method.id == PJSIP_BYE_METHOD) {
- response = pjsip_endpt_create_response( dlg->ua->endpt,
- event->src.rdata, 200);
- } else {
- response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- }
- if (response)
- pjsip_tsx_on_tx_msg(tsx, response);
-
- return 0;
- }
- /* Handle case when outgoing BYE was rejected with 401/407 */
- else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
- event->src_type == PJSIP_EVENT_RX_MSG &&
- tsx->role == PJSIP_ROLE_UAC)
- {
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool,
- &dlg->auth_sess,
- dlg->cred_count, dlg->cred_info,
- tsx->last_tx, event->src.rdata);
- if (tdata) {
- pjsip_dlg_send_msg(dlg, tdata);
- }
- }
- }
-
-
- if (dlg->pending_tsx_count == 0) {
- /* Set state to TERMINATED. */
- dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
-
- /* Notify application. */
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
-
- return -1;
- } else {
- dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
- }
-
- return 0;
-}
-
-static int dlg_on_state_terminated( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event)
-{
- PJ_UNUSED_ARG(dlg)
- PJ_UNUSED_ARG(tsx)
- PJ_UNUSED_ARG(event)
-
- return -1;
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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
+ */
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_parser.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/guid.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+
+/* TLS to keep dialog lock record (initialized by UA) */
+int pjsip_dlg_lock_tls_id;
+
+struct dialog_lock_data
+{
+ struct dialog_lock_data *prev;
+ pjsip_dlg *dlg;
+ int is_alive;
+};
+
+/*
+ * Static function prototypes.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+ pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq );
+static int dlg_on_all_state_pre( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_all_state_post( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_null( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_incoming( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_calling( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_connecting( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_established( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_disconnected( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_terminated( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+
+/*
+ * Dialog state handlers.
+ */
+static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *,
+ pjsip_event *) =
+{
+ &dlg_on_state_null,
+ &dlg_on_state_incoming,
+ &dlg_on_state_calling,
+ &dlg_on_state_proceeding,
+ &dlg_on_state_connecting,
+ &dlg_on_state_established,
+ &dlg_on_state_disconnected,
+ &dlg_on_state_terminated,
+};
+
+/*
+ * Dialog state names.
+ */
+static const char* dlg_state_names[] =
+{
+ "STATE_NULL",
+ "STATE_INCOMING",
+ "STATE_CALLING",
+ "STATE_PROCEEDING",
+ "STATE_CONNECTING",
+ "STATE_ESTABLISHED",
+ "STATE_DISCONNECTED",
+ "STATE_TERMINATED",
+};
+
+
+/*
+ * Get the dialog string state, normally for logging purpose.
+ */
+const char *pjsip_dlg_state_str(pjsip_dlg_state_e state)
+{
+ return dlg_state_names[state];
+}
+
+/* Lock dialog mutex. */
+static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+ struct dialog_lock_data *prev;
+
+ pj_mutex_lock(dlg->mutex);
+ prev = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+ lck->prev = prev;
+ lck->dlg = dlg;
+ lck->is_alive = 1;
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, lck);
+}
+
+/* Carefully unlock dialog mutex, protect against situation when the dialog
+ * has already been destroyed.
+ */
+static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+ pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck);
+ pj_assert(dlg == lck->dlg);
+
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev);
+ if (lck->is_alive)
+ pj_mutex_unlock(dlg->mutex);
+
+ return lck->is_alive ? 0 : -1;
+}
+
+/*
+ * This is called by dialog's FSM to change dialog's state.
+ */
+static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event);
+
+ PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)",
+ pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state),
+ event ? pjsip_event_str(event->type) : "",
+ event ? pjsip_event_str(event->src_type) : "",
+ event ? event->src.data : NULL));
+
+ dlg->state = state;
+ dlg->handle_tsx_event = dlg_state_handlers[state];
+}
+
+/*
+ * Invoke dialog's callback.
+ * This function is called by dialog's FSM, and interpret the event and call
+ * the corresponding callback registered by application.
+ */
+static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
+ pjsip_event *event )
+{
+ pjsip_dlg_callback *cb = dlg->ua->dlg_cb;
+ if (!cb) {
+ PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered)."));
+ return;
+ }
+
+ /* Low level event: call the all-events callback. */
+ if (cb->on_all_events) {
+ (*cb->on_all_events)(dlg, dlg_event, event);
+ }
+
+ /* Low level event: call the tx/rx callback if this is a tx/rx event. */
+ if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx)
+ {
+ (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data);
+ }
+ else if (event->type == PJSIP_EVENT_TX_MSG &&
+ event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg)
+ {
+ (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata);
+ }
+ else if (event->type == PJSIP_EVENT_RX_MSG &&
+ event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) {
+ (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata);
+ }
+
+ /* Now trigger high level events.
+ * High level event should only occurs when dialog's state has changed,
+ * except for on_provisional, which may be called multiple times whenever
+ * response message is sent
+ */
+ if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING &&
+ (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) &&
+ event->obj.tsx == dlg->invite_tsx)
+ {
+ /* Sent/received provisional responses. */
+ if (cb->on_provisional)
+ (*cb->on_provisional)(dlg, event->obj.tsx, event);
+ }
+
+ if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) {
+ if (cb->on_mid_call_events)
+ (*cb->on_mid_call_events)(dlg, event);
+ return;
+ }
+
+ if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED)
+ return;
+
+ if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) {
+
+ /* New incoming dialog. */
+ pj_assert (event->src_type == PJSIP_EVENT_RX_MSG);
+ (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) {
+
+ /* Dialog has just sent the first INVITE. */
+ if (cb->on_calling) {
+ (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata);
+ }
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) {
+
+ if (cb->on_disconnected)
+ (*cb->on_disconnected)(dlg, event);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) {
+
+ if (cb->on_terminated)
+ (*cb->on_terminated)(dlg);
+
+ pjsip_ua_destroy_dialog(dlg);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) {
+
+ if (cb->on_connecting)
+ (*cb->on_connecting)(dlg, event);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
+
+ if (cb->on_established)
+ (*cb->on_established)(dlg, event);
+ }
+}
+
+/*
+ * This callback receives event from the transaction layer (via User Agent),
+ * or from dialog timer (for 200/INVITE or ACK retransmission).
+ */
+void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int status = 0;
+ struct dialog_lock_data lck;
+
+ PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)",
+ pjsip_dlg_state_str(dlg->state),
+ pjsip_event_str(event->type),
+ pjsip_event_str(event->src_type)));
+
+
+ lock_dialog(dlg, &lck);
+
+ status = dlg_on_all_state_pre( dlg, tsx, event);
+
+ if (status==0) {
+ status = (*dlg->handle_tsx_event)(dlg, tsx, event);
+ }
+ if (status==0) {
+ status = dlg_on_all_state_post( dlg, tsx, event);
+ }
+
+ unlock_dialog(dlg, &lck);
+}
+
+/*
+ * This function contains common processing in all states, and it is called
+ * before the FSM is invoked.
+ */
+static int dlg_on_all_state_pre( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event)
+
+ if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
+ return 0;
+
+ if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING ||
+ tsx->state==PJSIP_TSX_STATE_TRYING))
+ {
+ ++dlg->pending_tsx_count;
+
+ } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED)
+ {
+ --dlg->pending_tsx_count;
+ if (tsx == dlg->invite_tsx)
+ dlg->invite_tsx = NULL;
+ }
+
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ tsx->handle_ack = 1;
+ }
+ return 0;
+}
+
+
+/*
+ * This function contains common processing in all states, and it is called
+ * after the FSM is invoked.
+ */
+static int dlg_on_all_state_post( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event)
+
+ if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) {
+ if (dlg->pending_tsx_count == 0 &&
+ dlg->state != PJSIP_DIALOG_STATE_CONNECTING &&
+ dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED &&
+ dlg->state != PJSIP_DIALOG_STATE_TERMINATED)
+ {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Internal function to initialize dialog, contains common initialization
+ * for both UAS and UAC dialog.
+ */
+static pj_status_t dlg_init( pjsip_dlg *dlg )
+{
+ /* Init mutex. */
+ dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+ if (!dlg->mutex) {
+ PJ_PERROR((dlg->obj_name, "pj_mutex_create()"));
+ return -1;
+ }
+
+ /* Init route-set (Initially empty) */
+ pj_list_init(&dlg->route_set);
+
+ /* Init auth credential list. */
+ pj_list_init(&dlg->auth_sess);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * This one is called just before dialog is destroyed.
+ * It is called while mutex is held.
+ */
+PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg )
+{
+ struct dialog_lock_data *lck;
+
+ //PJ_TODO(CHECK_THIS);
+ pj_assert(dlg->pending_tsx_count == 0);
+
+ /* Mark dialog as dead. */
+ lck = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+ while (lck) {
+ if (lck->dlg == dlg)
+ lck->is_alive = 0;
+ lck = lck->prev;
+ }
+}
+
+/*
+ * Initialize dialog from the received request.
+ * This is an internal function which is called by the User Agent (sip_ua.c),
+ * and it will initialize most of dialog's properties with values from the
+ * received message.
+ */
+pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata )
+{
+ pjsip_msg *msg = rdata->msg;
+ pjsip_to_hdr *to;
+ pjsip_contact_hdr *contact;
+ pjsip_name_addr *name_addr;
+ pjsip_url *url;
+ unsigned flag;
+ pjsip_event event;
+
+ pj_assert(dlg && rdata);
+
+ PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata));
+
+ /* Must be an INVITE request. */
+ pj_assert(msg->type == PJSIP_REQUEST_MSG &&
+ msg->line.req.method.id == PJSIP_INVITE_METHOD);
+
+ /* Init general dialog data. */
+ if (dlg_init(dlg) != PJ_SUCCESS) {
+ return -1;
+ }
+
+ /* Get the To header. */
+ to = rdata->to;
+
+ /* Copy URI in the To header as our local URI. */
+ dlg->local.info = pjsip_hdr_clone( dlg->pool, to);
+
+ /* Set tag in the local info. */
+ dlg->local.info->tag = dlg->local.tag;
+
+ /* Create local Contact to be advertised in the response.
+ * At the moment, just copy URI from the local URI as our contact.
+ */
+ dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->local.contact->star = 0;
+ name_addr = (pjsip_name_addr *)dlg->local.info->uri;
+ dlg->local.contact->uri = (pjsip_uri*) name_addr;
+ url = (pjsip_url*) name_addr->uri;
+ //url->port = rdata->via->sent_by.port;
+ //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) );
+
+ /* Save remote URI. */
+ dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from );
+ pjsip_fromto_set_to( dlg->remote.info );
+ pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag );
+
+ /* Save remote Contact. */
+ contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+ } else {
+ PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s",
+ pj_sockaddr_get_str_addr(&rdata->addr)));
+ dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+ }
+
+ /* Save Call-ID. */
+ dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+ pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id );
+
+ /* Initialize local CSeq and save remote CSeq.*/
+ dlg->local.cseq = rdata->timestamp.sec & 0xFFFF;
+ dlg->remote.cseq = rdata->cseq->cseq;
+
+ /* Secure? */
+ flag = pjsip_transport_get_flag(rdata->transport);
+ dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
+
+ /* Initial state is NULL. */
+ event.type = event.src_type = PJSIP_EVENT_RX_MSG;
+ event.src.rdata = rdata;
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+ PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set the contact details.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
+ const pj_str_t *contact )
+{
+ pjsip_uri *local_uri;
+ pj_str_t tmp;
+
+ pj_strdup_with_null(dlg->pool, &tmp, contact);
+ local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (local_uri == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI"));
+ return -1;
+ }
+
+ dlg->local.contact->star = 0;
+ dlg->local.contact->uri = local_uri;
+ return 0;
+}
+
+/*
+ * Set route set.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
+ const pjsip_route_hdr *route_set )
+{
+ pjsip_route_hdr *hdr;
+
+ pj_list_init(&dlg->route_set);
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr);
+ pj_list_insert_before( &dlg->route_set, cloned);
+ hdr = hdr->next;
+ }
+ return 0;
+}
+
+/*
+ * Set route set without cloning the header.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
+ pjsip_route_hdr *route_set)
+{
+ pjsip_route_hdr *hdr;
+
+ pj_list_init(&dlg->route_set);
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pj_list_insert_before( &dlg->route_set, hdr);
+ hdr = hdr->next;
+ }
+ return 0;
+}
+
+/*
+ * Application calls this function when it wants to initiate an outgoing
+ * dialog (incoming dialogs are created automatically by UA when it receives
+ * INVITE, by calling pjsip_dlg_init_from_rdata()).
+ * This function should initialize most of the dialog's properties.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
+ const pj_str_t *c_local_info,
+ const pj_str_t *c_remote_info,
+ const pj_str_t *c_target)
+{
+ pj_time_val tv;
+ pjsip_event event;
+ pj_str_t buf;
+
+ if (!dlg || !c_local_info || !c_remote_info) {
+ pj_assert(dlg && c_local_info && c_remote_info);
+ return -1;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "initializing"));
+
+ /* Init general dialog */
+ if (dlg_init(dlg) != PJ_SUCCESS) {
+ return -1;
+ }
+
+ /* Duplicate local info. */
+ pj_strdup_with_null( dlg->pool, &buf, c_local_info);
+
+ /* Build local URI. */
+ dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (dlg->local.target == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid local URI %s", buf.ptr));
+ return -1;
+ }
+
+ /* Set local URI. */
+ dlg->local.info = pjsip_from_hdr_create(dlg->pool);
+ dlg->local.info->uri = dlg->local.target;
+ dlg->local.info->tag = dlg->local.tag;
+
+ /* Create local Contact to be advertised in the response. */
+ dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->local.contact->star = 0;
+ dlg->local.contact->uri = dlg->local.target;
+
+ /* Set remote URI. */
+ dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
+
+ /* Duplicate to buffer. */
+ pj_strdup_with_null( dlg->pool, &buf, c_remote_info);
+
+ /* Build remote info. */
+ dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (dlg->remote.info->uri == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid remote URI %s", buf.ptr));
+ return -1;
+ }
+
+ /* Set remote Contact initially equal to the remote URI. */
+ dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+ dlg->remote.contact->star = 0;
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+
+ /* Set initial remote target. */
+ if (c_target != NULL) {
+ pj_strdup_with_null( dlg->pool, &buf, c_target);
+ dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0);
+ if (dlg->remote.target == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid remote target %s", buf.ptr));
+ return -1;
+ }
+ } else {
+ dlg->remote.target = dlg->remote.info->uri;
+ }
+
+ /* Create globally unique Call-ID */
+ dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+ pj_create_unique_string( dlg->pool, &dlg->call_id->id );
+
+ /* Local and remote CSeq */
+ pj_gettimeofday(&tv);
+ dlg->local.cseq = tv.sec & 0xFFFF;
+ dlg->remote.cseq = 0;
+
+ /* Initial state is NULL. */
+ event.type = event.src_type = PJSIP_EVENT_TX_MSG;
+ event.src.data = NULL;
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+ /* Done. */
+ PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s",
+ pjsip_role_name(dlg->role),
+ c_local_info->slen, c_local_info->ptr,
+ c_remote_info->slen, c_remote_info->ptr));
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set credentials.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
+ int count,
+ const pjsip_cred_info *cred)
+{
+ if (count > 0) {
+ dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info));
+ pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info));
+ }
+ dlg->cred_count = count;
+ return 0;
+}
+
+/*
+ * Create a new request within dialog (i.e. after the dialog session has been
+ * established). The construction of such requests follows the rule in
+ * RFC3261 section 12.2.1.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+ pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq )
+{
+ pjsip_tx_data *tdata;
+ pjsip_contact_hdr *contact;
+ pjsip_route_hdr *route, *end_list;
+
+ /* Contact Header field.
+ * Contact can only be present in requests that establish dialog (in the
+ * core SIP spec, only INVITE).
+ */
+ if (method->id == PJSIP_INVITE_METHOD)
+ contact = dlg->local.contact;
+ else
+ contact = NULL;
+
+ tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt,
+ method,
+ dlg->remote.target,
+ dlg->local.info,
+ dlg->remote.info,
+ contact,
+ dlg->call_id,
+ cseq,
+ NULL);
+ if (!tdata) {
+ PJ_THROW(1);
+ return;
+ }
+
+ /* Just copy dialog route-set to Route header.
+ * The transaction will do the processing as specified in Section 12.2.1
+ * of RFC 3261 in function tsx_process_route() in sip_transaction.c.
+ */
+ route = dlg->route_set.next;
+ end_list = &dlg->route_set;
+ for (; route != end_list; route = route->next ) {
+ pjsip_route_hdr *r;
+ r = pjsip_hdr_shallow_clone( tdata->pool, route );
+ pjsip_routing_hdr_set_route(r);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r);
+ }
+
+ /* Copy authorization headers. */
+ pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info);
+
+ *p_tdata = tdata;
+}
+
+
+/*
+ * This function is called by application to create new outgoing request
+ * message for this dialog. After the request is created, application can
+ * modify the message (such adding headers), and eventually send the request.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq)
+{
+ PJ_USE_EXCEPTION;
+ struct dialog_lock_data lck;
+ pjsip_tx_data *tdata = NULL;
+
+ pj_assert(dlg != NULL && method != NULL);
+ if (!dlg || !method) {
+ return NULL;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "Creating request"));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Use outgoing CSeq and increment it by one. */
+ if (cseq < 0)
+ cseq = dlg->local.cseq + 1;
+
+ PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d",
+ method->name.slen, method->name.ptr, cseq));
+
+ /* Create the request. */
+ PJ_TRY {
+ dlg_create_request_throw(&tdata, dlg, method, cseq);
+ PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name));
+ }
+ PJ_DEFAULT {
+ /* Failed! Delete transmit data. */
+ if (tdata) {
+ pjsip_tx_data_dec_ref( tdata );
+ tdata = NULL;
+ }
+ }
+ PJ_END;
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * Sends request.
+ * Select the transport for the request message
+ */
+static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+ pjsip_transaction *tsx;
+ pj_status_t status = PJ_SUCCESS;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL && tdata != NULL);
+ if (!dlg || !tdata) {
+ return -1;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Create a new transaction. */
+ tsx = pjsip_endpt_create_tsx( dlg->ua->endpt );
+ if (!tsx) {
+ unlock_dialog(dlg, &lck);
+ return -1;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name));
+
+ /* Initialize transaction */
+ tsx->module_data[dlg->ua->mod_id] = dlg;
+ status = pjsip_tsx_init_uac( tsx, tdata );
+ if (status != PJ_SUCCESS) {
+ unlock_dialog(dlg, &lck);
+ pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx );
+ return -1;
+ }
+ pjsip_endpt_register_tsx( dlg->ua->endpt, tsx );
+
+ /* Start the transaction. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return status;
+}
+
+/*
+ * This function can be called by application to send ANY outgoing message
+ * to remote party.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+ pj_status_t status;
+ int tsx_status;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL && tdata != NULL);
+ if (!dlg || !tdata) {
+ return -1;
+ }
+
+ lock_dialog(dlg, &lck);
+
+ if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+ int request_cseq;
+ pjsip_msg *msg = tdata->msg;
+ pjsip_cseq_hdr *cseq_hdr;
+
+ switch (msg->line.req.method.id) {
+ case PJSIP_CANCEL_METHOD:
+
+ /* Check the INVITE transaction state. */
+ tsx_status = dlg->invite_tsx->status_code;
+ if (tsx_status >= 200) {
+ /* Already terminated. Can't cancel. */
+ status = -1;
+ goto on_return;
+ }
+
+ /* If we've got provisional response, then send CANCEL and wait for
+ * the response to INVITE to arrive. Otherwise just send CANCEL and
+ * terminate the INVITE.
+ */
+ if (tsx_status < 100) {
+ pjsip_tsx_terminate( dlg->invite_tsx,
+ PJSIP_SC_REQUEST_TERMINATED);
+ status = 0;
+ goto on_return;
+ }
+
+ status = 0;
+ request_cseq = dlg->invite_tsx->cseq;
+ break;
+
+ case PJSIP_ACK_METHOD:
+ /* Sending ACK outside of transaction is not supported at present! */
+ pj_assert(0);
+ status = 0;
+ request_cseq = dlg->local.cseq;
+ break;
+
+ case PJSIP_INVITE_METHOD:
+ /* For an initial INVITE, reset dialog state to NULL so we get
+ * 'normal' UAC notifications such as on_provisional(), etc.
+ * Initial INVITE is the request that is sent when the dialog has
+ * not been established yet. It's not necessarily the first INVITE
+ * sent, as when the Authorization fails, subsequent INVITE are also
+ * considered as an initial INVITE.
+ */
+ if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+ /* Set state to NULL. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL);
+
+ } else {
+ /* This is a re-INVITE */
+ }
+ status = 0;
+ request_cseq = dlg->local.cseq + 1;
+ break;
+
+ default:
+ status = 0;
+ request_cseq = dlg->local.cseq + 1;
+ break;
+ }
+
+ if (status != 0)
+ goto on_return;
+
+ /* Update dialog's local CSeq, if necessary. */
+ if (request_cseq != dlg->local.cseq)
+ dlg->local.cseq = request_cseq;
+
+ /* Update CSeq header in the request. */
+ cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_CSEQ, NULL);
+ pj_assert(cseq_hdr != NULL);
+
+ /* Update the CSeq */
+ cseq_hdr->cseq = request_cseq;
+
+ /* Force the whole message to be re-printed. */
+ pjsip_tx_data_invalidate_msg( tdata );
+
+ /* Now send the request. */
+ status = dlg_send_request(dlg, tdata);
+
+ } else {
+ /*
+ * This is only valid for sending response to INVITE!
+ */
+ pjsip_cseq_hdr *cseq_hdr;
+
+ if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) {
+ status = -1;
+ goto on_return;
+ }
+
+ cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_CSEQ, NULL);
+ pj_assert(cseq_hdr);
+
+ if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) {
+ status = -1;
+ goto on_return;
+ }
+
+ pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq);
+
+ pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata);
+ status = 0;
+ }
+
+on_return:
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ /* Whatever happen delete the message. */
+ pjsip_tx_data_dec_ref( tdata );
+
+ return status;
+}
+
+/*
+ * Sends outgoing invitation.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg )
+{
+ pjsip_method method;
+ struct dialog_lock_data lck;
+ const pjsip_allow_hdr *allow_hdr;
+ pjsip_tx_data *tdata;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to send invitation"));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Create request. */
+ pjsip_method_set( &method, PJSIP_INVITE_METHOD);
+ tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+ if (tdata == NULL) {
+ unlock_dialog(dlg, &lck);
+ return NULL;
+ }
+
+ /* Invite SHOULD contain "Allow" header. */
+ allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+ if (allow_hdr) {
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+ }
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * Cancel pending outgoing dialog invitation.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg )
+{
+ pjsip_tx_data *tdata = NULL;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to cancel invitation"));
+
+ lock_dialog(dlg, &lck);
+
+ /* Check the INVITE transaction. */
+ if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+ "no INVITE transaction found"));
+ goto on_return;
+ }
+
+ /* Construct the CANCEL request. */
+ tdata = pjsip_endpt_create_cancel( dlg->ua->endpt,
+ dlg->invite_tsx->last_tx );
+ if (tdata == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+ "unable to construct request"));
+ goto on_return;
+ }
+
+ /* Add reference counter to tdata. */
+ pjsip_tx_data_add_ref(tdata);
+
+on_return:
+ unlock_dialog(dlg, &lck);
+ return tdata;
+}
+
+
+/*
+ * Answer incoming dialog invitation, with either provisional responses
+ * or a final response.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code )
+{
+ pjsip_tx_data *tdata = NULL;
+ pjsip_msg *msg;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Must have pending INVITE. */
+ if (dlg->invite_tsx == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found"));
+ goto on_return;
+ }
+ /* Must be UAS. */
+ if (dlg->role != PJSIP_ROLE_UAS) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS"));
+ goto on_return;
+ }
+ /* Must have not answered with final response before. */
+ if (dlg->invite_tsx->status_code >= 200) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated "
+ "with status %d", dlg->invite_tsx->status_code));
+ goto on_return;
+ }
+
+ /* Get transmit data and the message.
+ * We will rewrite the message with a new status code.
+ */
+ only if tdata is not pending!!!
+ tdata = dlg->invite_tsx->last_tx;
+ msg = tdata->msg;
+
+ /* Set status code and reason phrase. */
+ if (code < 100 || code >= 700) code = 500;
+ msg->line.status.code = code;
+ msg->line.status.reason = *pjsip_get_status_text(code);
+
+ /* For 2xx response, Contact and Record-Route must be added. */
+ if (PJSIP_IS_STATUS_IN_CLASS(code,200)) {
+ const pjsip_allow_hdr *allow_hdr;
+
+ if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) {
+ pjsip_contact_hdr *contact;
+ contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact);
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact );
+ }
+
+ /* 2xx response MUST contain "Allow" header. */
+ allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+ if (allow_hdr) {
+ pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+ }
+ }
+
+ /* for all but 100 responses, To-tag must be set. */
+ if (code != 100) {
+ pjsip_to_hdr *to;
+ to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
+ to->tag = dlg->local.tag;
+ }
+
+ /* Reset packet buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Add reference counter */
+ pjsip_tx_data_add_ref(tdata);
+
+on_return:
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+
+/*
+ * Send BYE request to terminate the dialog's session.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg )
+{
+ pjsip_method method;
+ struct dialog_lock_data lck;
+ pjsip_tx_data *tdata;
+
+ if (!dlg) {
+ pj_assert(dlg != NULL);
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to terminate session"));
+
+ lock_dialog(dlg, &lck);
+
+ pjsip_method_set( &method, PJSIP_BYE_METHOD);
+ tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * High level function to disconnect dialog's session. Depending on dialog's
+ * state, this function will either send CANCEL, final response, or BYE to
+ * trigger the disconnection. A status code must be supplied, which will be
+ * sent if dialog will be transmitting a final response to INVITE.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg,
+ int status_code )
+{
+ pjsip_tx_data *tdata = NULL;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ switch (dlg->state) {
+ case PJSIP_DIALOG_STATE_INCOMING:
+ tdata = pjsip_dlg_answer(dlg, status_code);
+ break;
+
+ case PJSIP_DIALOG_STATE_CALLING:
+ tdata = pjsip_dlg_cancel(dlg);
+ break;
+
+ case PJSIP_DIALOG_STATE_PROCEEDING:
+ if (dlg->role == PJSIP_ROLE_UAC) {
+ tdata = pjsip_dlg_cancel(dlg);
+ } else {
+ tdata = pjsip_dlg_answer(dlg, status_code);
+ }
+ break;
+
+ case PJSIP_DIALOG_STATE_ESTABLISHED:
+ tdata = pjsip_dlg_bye(dlg);
+ break;
+
+ default:
+ PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()",
+ dlg_state_names[dlg->state]));
+ break;
+ }
+
+ return tdata;
+}
+
+/*
+ * Handling of the receipt of 2xx/INVITE response.
+ */
+static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg,
+ pjsip_event *event )
+{
+ pjsip_msg *msg;
+ pjsip_contact_hdr *contact;
+ pjsip_hdr *hdr, *end_hdr;
+ pjsip_method method;
+ pjsip_tx_data *ack_tdata;
+
+ /* Get the message */
+ msg = event->src.rdata->msg;
+
+ /* Update remote's tag information. */
+ pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag);
+
+ /* Copy Contact information in the 2xx/INVITE response to dialog's.
+ * remote contact
+ */
+ contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+ } else {
+ /* duplicate contact from "From" header (?) */
+ PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!"));
+ dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+ }
+
+ /* Copy Record-Route header (in reverse order) as dialog's route-set,
+ * overwriting previous route-set, if any, even if the received route-set
+ * is empty.
+ */
+ pj_list_init(&dlg->route_set);
+ end_hdr = &msg->hdr;
+ for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) {
+ if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+ pjsip_route_hdr *r;
+ r = pjsip_hdr_clone(dlg->pool, hdr);
+ pjsip_routing_hdr_set_route(r);
+ pj_list_insert_before(&dlg->route_set, r);
+ }
+ }
+
+ /* On receipt of 200/INVITE response, send ACK.
+ * This ack must be saved and retransmitted whenever we receive
+ * 200/INVITE retransmission, until 64*T1 seconds elapsed.
+ */
+ pjsip_method_set( &method, PJSIP_ACK_METHOD);
+ ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq);
+ if (ack_tdata == NULL) {
+ //PJ_TODO(HANDLE_CREATE_ACK_FAILURE)
+ PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request"));
+ return;
+ }
+
+ /* Send with the transaction. */
+ pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata);
+
+ /* Decrement reference counter because pjsip_dlg_create_request
+ * automatically increments the request.
+ */
+ pjsip_tx_data_dec_ref( ack_tdata );
+}
+
+/*
+ * State NULL, before any events have been received.
+ */
+static int dlg_on_state_null( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG)
+ {
+ pjsip_hdr *hdr, *hdr_list;
+
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Save the INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Change state to INCOMING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event);
+
+ /* Create response buffer. */
+ tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100);
+ pjsip_tx_data_add_ref(tsx->last_tx);
+
+ /* Copy the Record-Route headers into dialog's route_set, maintaining
+ * the order.
+ */
+ pj_list_init(&dlg->route_set);
+ hdr_list = &event->src.rdata->msg->hdr;
+ hdr = hdr_list->next;
+ while (hdr != hdr_list) {
+ if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+ pjsip_route_hdr *route;
+ route = pjsip_hdr_clone(dlg->pool, hdr);
+ pjsip_routing_hdr_set_route(route);
+ pj_list_insert_before(&dlg->route_set, route);
+ }
+ hdr = hdr->next;
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_TX_MSG)
+ {
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Save the INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Change state to CALLING. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+/*
+ * State INCOMING is after the (callee) dialog has been initialized with
+ * the incoming request, but before any responses is sent by the dialog.
+ */
+static int dlg_on_state_incoming( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ return dlg_on_state_proceeding_callee( dlg, tsx, event );
+}
+
+/*
+ * State CALLING is after the (caller) dialog has sent outgoing invitation
+ * but before any responses are received.
+ */
+static int dlg_on_state_calling( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (tsx == dlg->invite_tsx) {
+ return dlg_on_state_proceeding_caller( dlg, tsx, event );
+ }
+ return 0;
+}
+
+/*
+ * State PROCEEDING is after provisional response is received.
+ * Since the processing is similar to state CALLING, this function is also
+ * called for CALLING state.
+ */
+static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int dlg_is_terminated = 0;
+
+ /* We only care about our INVITE transaction.
+ * Ignore other transaction progression (such as CANCEL).
+ */
+ if (tsx != dlg->invite_tsx) {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ return 0;
+ }
+
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) {
+ switch (tsx->state) {
+ case PJSIP_TSX_STATE_PROCEEDING:
+ if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) {
+ /* Change state to PROCEEDING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ } else {
+ /* Also notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ break;
+
+ case PJSIP_TSX_STATE_COMPLETED:
+ /* Change dialog state. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ /* Update remote target, take it from the contact hdr. */
+ pjsip_contact_hdr *contact;
+ contact = pjsip_msg_find_hdr(event->src.rdata->msg,
+ PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri);
+ } else {
+ PJ_LOG(4,(dlg->obj_name,
+ "Warning: found no Contact hdr in 200/OK"));
+ }
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+ } else if (tsx->status_code==401 || tsx->status_code==407) {
+ /* Handle Authentication challenge. */
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
+ dlg->pool, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ /* Re-use original request, with a new transaction.
+ * Need not to worry about CSeq, dialog will take care.
+ */
+ pjsip_dlg_send_msg(dlg, tdata);
+ return 0;
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* Send ACK when dialog is connected. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ pj_assert(event->src_type == PJSIP_EVENT_RX_MSG);
+ dlg_on_recv_2xx_invite(dlg, event);
+ }
+ break;
+
+ case PJSIP_TSX_STATE_TERMINATED:
+ /*
+ * Transaction is terminated because of timeout or transport error.
+ * To let the application go to normal state progression, call the
+ * callback twice. First is to emulate disconnection, and then call
+ * again (with state TERMINATED) to destroy the dialog.
+ */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* The INVITE transaction will be destroyed, so release reference
+ * to it.
+ */
+ dlg->invite_tsx = NULL;
+
+ /* We should terminate the dialog now.
+ * But it's possible that we have other pending transactions (for
+ * example, outgoing CANCEL is in progress).
+ * So destroy the dialog only if there's no other transaction.
+ */
+ if (dlg->pending_tsx_count == 0) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ dlg_is_terminated = 1;
+ }
+ break;
+
+ default:
+ pj_assert(0);
+ break;
+ }
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ return dlg_is_terminated ? -1 : 0;
+}
+
+/*
+ * State PROCEEDING for UAS is after the callee send provisional response.
+ * This function is also called for INCOMING state.
+ */
+static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int dlg_is_terminated = 0;
+
+ pj_assert( dlg->invite_tsx != NULL );
+
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_TX_MSG &&
+ tsx == dlg->invite_tsx)
+ {
+ switch (tsx->state) {
+ case PJSIP_TSX_STATE_PROCEEDING:
+ /* Change state to PROCEEDING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ break;
+
+ case PJSIP_TSX_STATE_COMPLETED:
+ case PJSIP_TSX_STATE_TERMINATED:
+ /* Change dialog state. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* If transaction is terminated in non-2xx situation,
+ * terminate dialog as well. This happens when something unexpected
+ * occurs, such as transport error.
+ */
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
+ !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200))
+ {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ dlg_is_terminated = 1;
+ }
+ break;
+
+ default:
+ pj_assert(0);
+ break;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->method.id == PJSIP_CANCEL_METHOD)
+ {
+ pjsip_tx_data *tdata;
+
+ /* Check if sequence number matches the pending INVITE. */
+ if (dlg->invite_tsx==NULL ||
+ pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0)
+ {
+ PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE"));
+
+ /* No matching INVITE transaction found. */
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST );
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ return 0;
+ }
+
+ /* Always respond the CANCEL with 200/CANCEL no matter what. */
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 200 );
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+
+ /* Respond the INVITE transaction with 487, only if transaction has
+ * not completed.
+ */
+ if (dlg->invite_tsx->last_tx) {
+ if (dlg->invite_tsx->status_code < 200) {
+ tdata = dlg->invite_tsx->last_tx;
+ tdata->msg->line.status.code = 487;
+ tdata->msg->line.status.reason = *pjsip_get_status_text(487);
+ /* Reset packet buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+ pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+ } else {
+ PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, "
+ "Transaction already terminated "
+ "with status %d",
+ dlg->invite_tsx->status_code));
+ }
+ } else {
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 487);
+ pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+ }
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return dlg_is_terminated ? -1 : 0;
+}
+
+static int dlg_on_state_proceeding( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (dlg->role == PJSIP_ROLE_UAC) {
+ return dlg_on_state_proceeding_caller( dlg, tsx, event );
+ } else {
+ return dlg_on_state_proceeding_callee( dlg, tsx, event );
+ }
+}
+
+static int dlg_on_state_connecting( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (tsx == dlg->invite_tsx) {
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ (tsx->state == PJSIP_TSX_STATE_TERMINATED ||
+ tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+ tsx->state == PJSIP_TSX_STATE_CONFIRMED))
+ {
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event);
+ } else {
+ /* Probably because we never get the ACK, or transport error
+ * when sending ACK.
+ */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ } else {
+ /* Handle case when transaction is started when dialog is connecting
+ * (e.g. BYE requests cross wire.
+ */
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAS)
+ {
+ pjsip_tx_data *response;
+
+ if (tsx->status_code >= 200)
+ return 0;
+
+ if (tsx->method.id == PJSIP_BYE_METHOD) {
+ /* Set state to DISCONNECTED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ response = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 200);
+ } else {
+ response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ }
+
+ if (response)
+ pjsip_tsx_on_tx_msg(tsx, response);
+
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int dlg_on_state_established( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx)
+
+ if (tsx && tsx->method.id == PJSIP_BYE_METHOD) {
+ /* Set state to DISCONNECTED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* Answer with 200/BYE. */
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 200 );
+ if (tdata)
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+ }
+ } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) {
+ pjsip_method_e method = event->src.rdata->cseq->method.id;
+
+ PJ_TODO(PROPERLY_HANDLE_REINVITATION)
+
+ /* Reinvitation. The message may be INVITE or an ACK. */
+ if (method == PJSIP_INVITE_METHOD) {
+ if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) {
+ /* Section 14.2: A UAS that receives a second INVITE before it
+ * sends the final response to a first INVITE with a lower
+ * CSeq sequence number on the same dialog MUST return a 500
+ * (Server Internal Error) response to the second INVITE and
+ * MUST include a Retry-After header field with a randomly
+ * chosen value of between 0 and 10 seconds.
+ */
+ pjsip_retry_after_hdr *hdr;
+ pjsip_tx_data *tdata =
+ pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata, 500);
+
+ if (!tdata)
+ return 0;
+
+ /* Add Retry-After. */
+ hdr = pjsip_retry_after_hdr_create(tdata->pool);
+ hdr->ivalue = 9;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
+
+ /* Send. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ return 0;
+ }
+
+ /* Keep this as our current INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Create response buffer. */
+ tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 100);
+ pjsip_tx_data_add_ref(tsx->last_tx);
+
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event);
+
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+static int dlg_on_state_disconnected( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx)
+
+ /* Handle case when transaction is started when dialog is disconnected
+ * (e.g. BYE requests cross wire.
+ */
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAS)
+ {
+ pjsip_tx_data *response = NULL;
+
+ if (tsx->status_code >= 200)
+ return 0;
+
+ if (tsx->method.id == PJSIP_BYE_METHOD) {
+ response = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 200);
+ } else {
+ response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ }
+ if (response)
+ pjsip_tsx_on_tx_msg(tsx, response);
+
+ return 0;
+ }
+ /* Handle case when outgoing BYE was rejected with 401/407 */
+ else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAC)
+ {
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool,
+ &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+ }
+ }
+
+
+ if (dlg->pending_tsx_count == 0) {
+ /* Set state to TERMINATED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ return -1;
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+static int dlg_on_state_terminated( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(dlg)
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(event)
+
+ return -1;
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index e7bf9bd6..1b22170e 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -1,511 +1,511 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program 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.
- *
- * This program 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
- */
-#include <pjsip_mod_ua/sip_reg.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_auth_msg.h>
-#include <pj/pool.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/log.h>
-
-#define REFRESH_TIMER 1
-#define DELAY_BEFORE_REFRESH 5
-#define THIS_FILE "sip_regc.c"
-
-/**
- * SIP client registration structure.
- */
-struct pjsip_regc
-{
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pj_bool_t _delete_flag;
- int pending_tsx;
-
- void *token;
- pjsip_regc_cb *cb;
-
- pj_str_t str_srv_url;
- pjsip_uri *srv_url;
- pjsip_cid_hdr *cid_hdr;
- pjsip_cseq_hdr *cseq_hdr;
- pjsip_from_hdr *from_hdr;
- pjsip_to_hdr *to_hdr;
- char *contact_buf;
- pjsip_generic_string_hdr *contact_hdr;
- pjsip_expires_hdr *expires_hdr;
- pjsip_contact_hdr *unreg_contact_hdr;
- pjsip_expires_hdr *unreg_expires_hdr;
- pj_uint32_t expires;
-
- /* Credentials. */
- int cred_count;
- pjsip_cred_info *cred_info;
-
- /* Authorization sessions. */
- pjsip_auth_session auth_sess_list;
-
- /* Auto refresh registration. */
- pj_bool_t auto_reg;
- pj_timer_entry timer;
-};
-
-
-
-PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
- pjsip_regc_cb *cb)
-{
- pj_pool_t *pool;
- pjsip_regc *regc;
-
- if (cb == NULL)
- return NULL;
-
- pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
- regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
-
- regc->pool = pool;
- regc->endpt = endpt;
- regc->token = token;
- regc->cb = cb;
- regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
- regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
-
- pj_list_init(&regc->auth_sess_list);
-
- return regc;
-}
-
-
-PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
-{
- if (regc->pending_tsx) {
- regc->_delete_flag = 1;
- regc->cb = NULL;
- } else {
- pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
- }
-}
-
-
-PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
-{
- return regc->pool;
-}
-
-static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
-{
- if (expires != regc->expires) {
- regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
- regc->expires_hdr->ivalue = expires;
- } else {
- regc->expires_hdr = NULL;
- }
-}
-
-
-static pj_status_t set_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
-{
- int i;
- char *s;
- const pj_str_t contact_STR = { "Contact", 7};
-
- /* Concatenate contacts. */
- for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
- if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
- return -1;
- }
- pj_memcpy(s, contact[i].ptr, contact[i].slen);
- s += contact[i].slen;
-
- if (i != contact_cnt - 1) {
- *s++ = ',';
- *s++ = ' ';
- }
- }
-
- /* Set "Contact" header. */
- regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
- regc->contact_hdr->hvalue.ptr = regc->contact_buf;
- regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
-
- return 0;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
- const pj_str_t *srv_url,
- const pj_str_t *from_url,
- const pj_str_t *to_url,
- int contact_cnt,
- const pj_str_t contact[],
- pj_uint32_t expires)
-{
- pj_str_t tmp;
-
- /* Copy server URL. */
- pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
-
- /* Set server URL. */
- tmp = regc->str_srv_url;
- regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
- if (regc->srv_url == NULL) {
- return -1;
- }
-
- /* Set "From" header. */
- pj_strdup_with_null(regc->pool, &tmp, from_url);
- regc->from_hdr = pjsip_from_hdr_create(regc->pool);
- regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->from_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
- return -1;
- }
-
- /* Set "To" header. */
- pj_strdup_with_null(regc->pool, &tmp, to_url);
- regc->to_hdr = pjsip_to_hdr_create(regc->pool);
- regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
- PJSIP_PARSE_URI_AS_NAMEADDR);
- if (!regc->to_hdr->uri) {
- PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
- return -1;
- }
-
-
- /* Set "Contact" header. */
- if (set_contact( regc, contact_cnt, contact) != 0)
- return -1;
-
- /* Set "Expires" header, if required. */
- set_expires( regc, expires);
-
- /* Set "Call-ID" header. */
- regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
- pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
-
- /* Set "CSeq" header. */
- regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
- regc->cseq_hdr->cseq = 0;
- pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
-
- /* Create "Contact" header used in unregistration. */
- regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
- regc->unreg_contact_hdr->star = 1;
-
- /* Create "Expires" header used in unregistration. */
- regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
- regc->unreg_expires_hdr->ivalue = 0;
-
- /* Done. */
- return 0;
-}
-
-PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
- int count,
- const pjsip_cred_info cred[] )
-{
- if (count > 0) {
- regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
- pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
- }
- regc->cred_count = count;
- return 0;
-}
-
-static pjsip_tx_data *create_request(pjsip_regc *regc)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg;
-
- /* Create transmit data. */
- tdata = pjsip_endpt_create_tdata(regc->endpt);
- if (!tdata) {
- return NULL;
- }
-
- /* Create request message. */
- msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
- tdata->msg = msg;
-
- /* Initialize request line. */
- pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
- msg->line.req.uri = regc->srv_url;
-
- /* Add headers. */
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
-
- /* Add cached authorization headers. */
- pjsip_auth_init_req( regc->pool, tdata, &regc->auth_sess_list,
- regc->cred_count, regc->cred_info );
-
- /* Add reference counter to transmit data. */
- pjsip_tx_data_add_ref(tdata);
-
- return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
-{
- pjsip_msg *msg;
- pjsip_tx_data *tdata;
-
- tdata = create_request(regc);
- if (!tdata)
- return NULL;
-
- msg = tdata->msg;
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
- if (regc->expires_hdr)
- pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
-
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
- regc->timer.id = 0;
- }
-
- regc->auto_reg = autoreg;
-
- return tdata;
-}
-
-
-PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
-{
- pjsip_tx_data *tdata;
- pjsip_msg *msg;
-
- if (regc->timer.id != 0) {
- pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
- regc->timer.id = 0;
- }
-
- tdata = create_request(regc);
- if (!tdata)
- return NULL;
-
- msg = tdata->msg;
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
- pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
-
- return tdata;
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
- int contact_cnt,
- const pj_str_t contact[] )
-{
- return set_contact( regc, contact_cnt, contact );
-}
-
-
-PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
- pj_uint32_t expires )
-{
- set_expires( regc, expires );
- return 0;
-}
-
-
-static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
- pjsip_rx_data *rdata, pj_int32_t expiration,
- int contact_cnt, pjsip_contact_hdr *contact[])
-{
- struct pjsip_regc_cbparam cbparam;
-
-
- cbparam.regc = regc;
- cbparam.token = regc->token;
- cbparam.code = status;
- cbparam.reason = *reason;
- cbparam.rdata = rdata;
- cbparam.contact_cnt = contact_cnt;
- cbparam.expiration = expiration;
- if (contact_cnt) {
- pj_memcpy( cbparam.contact, contact,
- contact_cnt*sizeof(pjsip_contact_hdr*));
- }
-
- (*regc->cb)(&cbparam);
-}
-
-static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
- struct pj_timer_entry *entry)
-{
- pjsip_regc *regc = entry->user_data;
- pjsip_tx_data *tdata;
-
- PJ_UNUSED_ARG(timer_heap)
-
- entry->id = 0;
- tdata = pjsip_regc_register(regc, 1);
- if (tdata) {
- pjsip_regc_send(regc, tdata);
- } else {
- pj_str_t reason = pj_str("Unable to create txdata");
- call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
- }
-}
-
-static void tsx_callback(void *token, pjsip_event *event)
-{
- pjsip_regc *regc = token;
- pjsip_transaction *tsx = event->obj.tsx;
-
- /* If registration data has been deleted by user then remove registration
- * data from transaction's callback, and don't call callback.
- */
- if (regc->_delete_flag) {
- --regc->pending_tsx;
-
- } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
- tsx->status_code == PJSIP_SC_UNAUTHORIZED)
- {
- pjsip_rx_data *rdata = event->src.rdata;
- pjsip_tx_data *tdata;
-
- tdata = pjsip_auth_reinit_req( regc->endpt,
- regc->pool, &regc->auth_sess_list,
- regc->cred_count, regc->cred_info,
- tsx->last_tx, event->src.rdata );
-
- if (tdata) {
- --regc->pending_tsx;
- pjsip_regc_send(regc, tdata);
- return;
- } else {
- call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
- rdata, -1, 0, NULL);
- --regc->pending_tsx;
- }
- } else {
- int contact_cnt = 0;
- pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
- pjsip_rx_data *rdata;
- pj_int32_t expiration = 0xFFFF;
-
- if (tsx->status_code/100 == 2) {
- int i;
- pjsip_contact_hdr *hdr;
- pjsip_msg *msg;
- pjsip_expires_hdr *expires;
-
- rdata = event->src.rdata;
- msg = rdata->msg;
- hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
- while (hdr) {
- contact[contact_cnt++] = hdr;
- hdr = hdr->next;
- if (hdr == (void*)&msg->hdr)
- break;
- hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
- }
-
- expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
-
- if (expires)
- expiration = expires->ivalue;
-
- for (i=0; i<contact_cnt; ++i) {
- hdr = contact[i];
- if (hdr->expires >= 0 && hdr->expires < expiration)
- expiration = contact[i]->expires;
- }
-
- if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
- pj_time_val delay = { 0, 0};
-
- delay.sec = expiration - DELAY_BEFORE_REFRESH;
- if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
- delay.sec > (pj_int32_t)regc->expires)
- {
- delay.sec = regc->expires;
- }
- if (delay.sec < DELAY_BEFORE_REFRESH)
- delay.sec = DELAY_BEFORE_REFRESH;
- regc->timer.cb = &regc_refresh_timer_cb;
- regc->timer.id = REFRESH_TIMER;
- regc->timer.user_data = regc;
- pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
- }
-
- } else {
- rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
- }
-
-
- /* Call callback. */
- if (expiration == 0xFFFF) expiration = -1;
- call_callback(regc, tsx->status_code,
- (rdata ? &rdata->msg->line.status.reason
- : pjsip_get_status_text(tsx->status_code)),
- rdata, expiration,
- contact_cnt, contact);
-
- --regc->pending_tsx;
- }
-
- /* Delete the record if user destroy regc during the callback. */
- if (regc->_delete_flag && regc->pending_tsx==0) {
- pjsip_regc_destroy(regc);
- }
-}
-
-PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
-{
- int status;
-
- /* Make sure we don't have pending transaction. */
- if (regc->pending_tsx) {
- pj_str_t reason = pj_str("Transaction in progress");
- call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
- pjsip_tx_data_dec_ref( tdata );
- return;
- }
-
- /* Invalidate message buffer. */
- pjsip_tx_data_invalidate_msg(tdata);
-
- /* Increment CSeq */
- regc->cseq_hdr->cseq++;
-
- /* Send. */
- status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
- if (status==0)
- ++regc->pending_tsx;
- else {
- pj_str_t reason = pj_str("Unable to send request.");
- call_callback(regc, status, &reason, NULL, -1, 0, NULL);
- }
-}
-
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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
+ */
+#include <pjsip_mod_ua/sip_reg.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+
+#define REFRESH_TIMER 1
+#define DELAY_BEFORE_REFRESH 5
+#define THIS_FILE "sip_regc.c"
+
+/**
+ * SIP client registration structure.
+ */
+struct pjsip_regc
+{
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pj_bool_t _delete_flag;
+ int pending_tsx;
+
+ void *token;
+ pjsip_regc_cb *cb;
+
+ pj_str_t str_srv_url;
+ pjsip_uri *srv_url;
+ pjsip_cid_hdr *cid_hdr;
+ pjsip_cseq_hdr *cseq_hdr;
+ pjsip_from_hdr *from_hdr;
+ pjsip_to_hdr *to_hdr;
+ char *contact_buf;
+ pjsip_generic_string_hdr *contact_hdr;
+ pjsip_expires_hdr *expires_hdr;
+ pjsip_contact_hdr *unreg_contact_hdr;
+ pjsip_expires_hdr *unreg_expires_hdr;
+ pj_uint32_t expires;
+
+ /* Credentials. */
+ int cred_count;
+ pjsip_cred_info *cred_info;
+
+ /* Authorization sessions. */
+ pjsip_auth_session auth_sess_list;
+
+ /* Auto refresh registration. */
+ pj_bool_t auto_reg;
+ pj_timer_entry timer;
+};
+
+
+
+PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+ pjsip_regc_cb *cb)
+{
+ pj_pool_t *pool;
+ pjsip_regc *regc;
+
+ if (cb == NULL)
+ return NULL;
+
+ pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
+ regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
+
+ regc->pool = pool;
+ regc->endpt = endpt;
+ regc->token = token;
+ regc->cb = cb;
+ regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
+ regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
+
+ pj_list_init(&regc->auth_sess_list);
+
+ return regc;
+}
+
+
+PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
+{
+ if (regc->pending_tsx) {
+ regc->_delete_flag = 1;
+ regc->cb = NULL;
+ } else {
+ pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
+ }
+}
+
+
+PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
+{
+ return regc->pool;
+}
+
+static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
+{
+ if (expires != regc->expires) {
+ regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
+ regc->expires_hdr->ivalue = expires;
+ } else {
+ regc->expires_hdr = NULL;
+ }
+}
+
+
+static pj_status_t set_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ int i;
+ char *s;
+ const pj_str_t contact_STR = { "Contact", 7};
+
+ /* Concatenate contacts. */
+ for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
+ if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
+ return -1;
+ }
+ pj_memcpy(s, contact[i].ptr, contact[i].slen);
+ s += contact[i].slen;
+
+ if (i != contact_cnt - 1) {
+ *s++ = ',';
+ *s++ = ' ';
+ }
+ }
+
+ /* Set "Contact" header. */
+ regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
+ regc->contact_hdr->hvalue.ptr = regc->contact_buf;
+ regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
+
+ return 0;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
+ const pj_str_t *srv_url,
+ const pj_str_t *from_url,
+ const pj_str_t *to_url,
+ int contact_cnt,
+ const pj_str_t contact[],
+ pj_uint32_t expires)
+{
+ pj_str_t tmp;
+
+ /* Copy server URL. */
+ pj_strdup_with_null(regc->pool, &regc->str_srv_url, srv_url);
+
+ /* Set server URL. */
+ tmp = regc->str_srv_url;
+ regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
+ if (regc->srv_url == NULL) {
+ return -1;
+ }
+
+ /* Set "From" header. */
+ pj_strdup_with_null(regc->pool, &tmp, from_url);
+ regc->from_hdr = pjsip_from_hdr_create(regc->pool);
+ regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->from_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
+ return -1;
+ }
+
+ /* Set "To" header. */
+ pj_strdup_with_null(regc->pool, &tmp, to_url);
+ regc->to_hdr = pjsip_to_hdr_create(regc->pool);
+ regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->to_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
+ return -1;
+ }
+
+
+ /* Set "Contact" header. */
+ if (set_contact( regc, contact_cnt, contact) != 0)
+ return -1;
+
+ /* Set "Expires" header, if required. */
+ set_expires( regc, expires);
+
+ /* Set "Call-ID" header. */
+ regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
+ pj_create_unique_string(regc->pool, &regc->cid_hdr->id);
+
+ /* Set "CSeq" header. */
+ regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
+ regc->cseq_hdr->cseq = 0;
+ pjsip_method_set( &regc->cseq_hdr->method, PJSIP_REGISTER_METHOD);
+
+ /* Create "Contact" header used in unregistration. */
+ regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
+ regc->unreg_contact_hdr->star = 1;
+
+ /* Create "Expires" header used in unregistration. */
+ regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
+ regc->unreg_expires_hdr->ivalue = 0;
+
+ /* Done. */
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
+ int count,
+ const pjsip_cred_info cred[] )
+{
+ if (count > 0) {
+ regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
+ pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
+ }
+ regc->cred_count = count;
+ return 0;
+}
+
+static pjsip_tx_data *create_request(pjsip_regc *regc)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg;
+
+ /* Create transmit data. */
+ tdata = pjsip_endpt_create_tdata(regc->endpt);
+ if (!tdata) {
+ return NULL;
+ }
+
+ /* Create request message. */
+ msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+ tdata->msg = msg;
+
+ /* Initialize request line. */
+ pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
+ msg->line.req.uri = regc->srv_url;
+
+ /* Add headers. */
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
+
+ /* Add cached authorization headers. */
+ pjsip_auth_init_req( regc->pool, tdata, &regc->auth_sess_list,
+ regc->cred_count, regc->cred_info );
+
+ /* Add reference counter to transmit data. */
+ pjsip_tx_data_add_ref(tdata);
+
+ return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
+{
+ pjsip_msg *msg;
+ pjsip_tx_data *tdata;
+
+ tdata = create_request(regc);
+ if (!tdata)
+ return NULL;
+
+ msg = tdata->msg;
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
+ if (regc->expires_hdr)
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
+ regc->timer.id = 0;
+ }
+
+ regc->auto_reg = autoreg;
+
+ return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg;
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
+ regc->timer.id = 0;
+ }
+
+ tdata = create_request(regc);
+ if (!tdata)
+ return NULL;
+
+ msg = tdata->msg;
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
+
+ return tdata;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ return set_contact( regc, contact_cnt, contact );
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
+ pj_uint32_t expires )
+{
+ set_expires( regc, expires );
+ return 0;
+}
+
+
+static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
+ pjsip_rx_data *rdata, pj_int32_t expiration,
+ int contact_cnt, pjsip_contact_hdr *contact[])
+{
+ struct pjsip_regc_cbparam cbparam;
+
+
+ cbparam.regc = regc;
+ cbparam.token = regc->token;
+ cbparam.code = status;
+ cbparam.reason = *reason;
+ cbparam.rdata = rdata;
+ cbparam.contact_cnt = contact_cnt;
+ cbparam.expiration = expiration;
+ if (contact_cnt) {
+ pj_memcpy( cbparam.contact, contact,
+ contact_cnt*sizeof(pjsip_contact_hdr*));
+ }
+
+ (*regc->cb)(&cbparam);
+}
+
+static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_regc *regc = entry->user_data;
+ pjsip_tx_data *tdata;
+
+ PJ_UNUSED_ARG(timer_heap)
+
+ entry->id = 0;
+ tdata = pjsip_regc_register(regc, 1);
+ if (tdata) {
+ pjsip_regc_send(regc, tdata);
+ } else {
+ pj_str_t reason = pj_str("Unable to create txdata");
+ call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+ }
+}
+
+static void tsx_callback(void *token, pjsip_event *event)
+{
+ pjsip_regc *regc = token;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ /* If registration data has been deleted by user then remove registration
+ * data from transaction's callback, and don't call callback.
+ */
+ if (regc->_delete_flag) {
+ --regc->pending_tsx;
+
+ } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
+ tsx->status_code == PJSIP_SC_UNAUTHORIZED)
+ {
+ pjsip_rx_data *rdata = event->src.rdata;
+ pjsip_tx_data *tdata;
+
+ tdata = pjsip_auth_reinit_req( regc->endpt,
+ regc->pool, &regc->auth_sess_list,
+ regc->cred_count, regc->cred_info,
+ tsx->last_tx, event->src.rdata );
+
+ if (tdata) {
+ --regc->pending_tsx;
+ pjsip_regc_send(regc, tdata);
+ return;
+ } else {
+ call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
+ rdata, -1, 0, NULL);
+ --regc->pending_tsx;
+ }
+ } else {
+ int contact_cnt = 0;
+ pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
+ pjsip_rx_data *rdata;
+ pj_int32_t expiration = 0xFFFF;
+
+ if (tsx->status_code/100 == 2) {
+ int i;
+ pjsip_contact_hdr *hdr;
+ pjsip_msg *msg;
+ pjsip_expires_hdr *expires;
+
+ rdata = event->src.rdata;
+ msg = rdata->msg;
+ hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ while (hdr) {
+ contact[contact_cnt++] = hdr;
+ hdr = hdr->next;
+ if (hdr == (void*)&msg->hdr)
+ break;
+ hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
+ }
+
+ expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+
+ if (expires)
+ expiration = expires->ivalue;
+
+ for (i=0; i<contact_cnt; ++i) {
+ hdr = contact[i];
+ if (hdr->expires >= 0 && hdr->expires < expiration)
+ expiration = contact[i]->expires;
+ }
+
+ if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
+ pj_time_val delay = { 0, 0};
+
+ delay.sec = expiration - DELAY_BEFORE_REFRESH;
+ if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
+ delay.sec > (pj_int32_t)regc->expires)
+ {
+ delay.sec = regc->expires;
+ }
+ if (delay.sec < DELAY_BEFORE_REFRESH)
+ delay.sec = DELAY_BEFORE_REFRESH;
+ regc->timer.cb = &regc_refresh_timer_cb;
+ regc->timer.id = REFRESH_TIMER;
+ regc->timer.user_data = regc;
+ pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
+ }
+
+ } else {
+ rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
+ }
+
+
+ /* Call callback. */
+ if (expiration == 0xFFFF) expiration = -1;
+ call_callback(regc, tsx->status_code,
+ (rdata ? &rdata->msg->line.status.reason
+ : pjsip_get_status_text(tsx->status_code)),
+ rdata, expiration,
+ contact_cnt, contact);
+
+ --regc->pending_tsx;
+ }
+
+ /* Delete the record if user destroy regc during the callback. */
+ if (regc->_delete_flag && regc->pending_tsx==0) {
+ pjsip_regc_destroy(regc);
+ }
+}
+
+PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
+{
+ int status;
+
+ /* Make sure we don't have pending transaction. */
+ if (regc->pending_tsx) {
+ pj_str_t reason = pj_str("Transaction in progress");
+ call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+ pjsip_tx_data_dec_ref( tdata );
+ return;
+ }
+
+ /* Invalidate message buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Increment CSeq */
+ regc->cseq_hdr->cseq++;
+
+ /* Send. */
+ status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
+ if (status==0)
+ ++regc->pending_tsx;
+ else {
+ pj_str_t reason = pj_str("Unable to send request.");
+ call_callback(regc, status, &reason, NULL, -1, 0, NULL);
+ }
+}
+
+
diff --git a/pjsip/src/pjsip-ua/sip_ua.c b/pjsip/src/pjsip-ua/sip_ua.c
index 11f7670c..094be07f 100644
--- a/pjsip/src/pjsip-ua/sip_ua.c
+++ b/pjsip/src/pjsip-ua/sip_ua.c
@@ -1,506 +1,506 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program 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.
- *
- * This program 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
- */
-#include <pjsip_mod_ua/sip_ua.h>
-#include <pjsip_mod_ua/sip_dialog.h>
-#include <pjsip_mod_ua/sip_ua_private.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_transaction.h>
-#include <pj/list.h>
-#include <pj/log.h>
-#include <pj/string.h>
-#include <pj/guid.h>
-#include <pj/os.h>
-#include <pj/hash.h>
-#include <pj/pool.h>
-
-#define PJSIP_POOL_LEN_USER_AGENT 1024
-#define PJSIP_POOL_INC_USER_AGENT 0
-
-
-#define LOG_THIS "useragent.."
-
-/*
- * Static prototypes.
- */
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id );
-static pj_status_t ua_start( struct pjsip_module *mod );
-static pj_status_t ua_deinit( struct pjsip_module *mod );
-static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
-static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
- pjsip_rx_data *rdata );
-static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
- pj_str_t *key );
-PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
-
-/*
- * Default UA instance.
- */
-static pjsip_user_agent ua_instance;
-
-/*
- * Module interface.
- */
-static struct pjsip_module mod_ua =
-{
- { "User-Agent", 10 }, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* User agent instance, initialized by APP. */
- 0, /* Number of methods supported (will be initialized later). */
- { 0 }, /* Array of methods (will be initialized later) */
- &ua_init, /* init_module() */
- &ua_start, /* start_module() */
- &ua_deinit, /* deinit_module() */
- &ua_tsx_handler, /* tsx_handler() */
-};
-
-/*
- * Initialize user agent instance.
- */
-static pj_status_t ua_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- static pjsip_method m_invite, m_ack, m_cancel, m_bye;
- pjsip_user_agent *ua = mod->mod_data;
- extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
-
- pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
- pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
- pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
- pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
-
- mod->method_cnt = 4;
- mod->methods[0] = &m_invite;
- mod->methods[1] = &m_ack;
- mod->methods[2] = &m_cancel;
- mod->methods[3] = &m_bye;
-
- /* Initialize the user agent. */
- ua->endpt = endpt;
- ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
- PJSIP_POOL_INC_UA);
- if (!ua->pool) {
- return -1;
- }
- ua->mod_id = id;
- ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
- if (!ua->mutex) {
- return -1;
- }
- ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
- if (ua->dlg_table == NULL) {
- return -1;
- }
- pj_list_init(&ua->dlg_list);
-
- /* Initialize dialog lock. */
- pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
- if (pjsip_dlg_lock_tls_id == -1) {
- return -1;
- }
- pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
-
- return 0;
-}
-
-/*
- * Start user agent instance.
- */
-static pj_status_t ua_start( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-/*
- * Destroy user agent.
- */
-static pj_status_t ua_deinit( struct pjsip_module *mod )
-{
- pjsip_user_agent *ua = mod->mod_data;
-
- pj_mutex_unlock(ua->mutex);
-
- /* Release pool */
- if (ua->pool) {
- pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
- }
- return 0;
-}
-
-/*
- * Get the module interface for the UA module.
- */
-PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
-{
- mod_ua.mod_data = &ua_instance;
- return &mod_ua;
-}
-
-/*
- * Register callback to receive dialog notifications.
- */
-PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
- pjsip_dlg_callback *cb )
-{
- ua->dlg_cb = cb;
-}
-
-/*
- * Find dialog.
- * This function is called for a new transactions, which a dialog hasn't been
- * 'attached' to the transaction.
- */
-static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
-{
- pjsip_dlg *dlg;
- pj_str_t *tag;
-
- /* Non-CANCEL requests/response can be found by looking at the tag in the
- * hash table. CANCEL requests don't have tags, so instead we'll try to
- * find the UAS INVITE transaction in endpoint's hash table
- */
- if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
-
- /* Create key for the rdata, but this time, use INVITE as the
- * method.
- */
- pj_str_t key;
- pjsip_role_e role;
- pjsip_method invite_method;
- pjsip_transaction *invite_tsx;
-
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- role = PJSIP_ROLE_UAS;
- } else {
- role = PJSIP_ROLE_UAC;
- }
- pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
- pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
-
- /* Lookup the INVITE transaction */
- invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
-
- /* We should find the dialog attached to the INVITE transaction */
- return invite_tsx ?
- (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
-
- } else {
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- tag = &rdata->to_tag;
- } else {
- tag = &rdata->from_tag;
- }
- /* Find the dialog in UA hash table */
- pj_mutex_lock(ua->mutex);
- dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
- pj_mutex_unlock(ua->mutex);
- }
-
- return dlg;
-}
-
-/*
- * This function receives event notification from transactions. It is called by
- * endpoint.
- */
-static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_user_agent *ua = mod->mod_data;
- pjsip_dlg *dlg = NULL;
- pjsip_transaction *tsx = event->obj.tsx;
-
- PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)",
- (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type),
- pjsip_event_str(event->src_type), event->src.data));
-
- /* Special case to handle ACK which doesn't match any INVITE transactions. */
- if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
- /* Find the dialog based on the "tag". */
- dlg = find_dialog( ua, event->src.rdata );
-
- /* We should be able to find it. */
- if (!dlg) {
- PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
- return;
- }
-
- /* Match CSeq with pending INVITE in dialog. */
- if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
- /* A match found. */
- tsx = dlg->invite_tsx;
-
- /* Pass the event to transaction if transaction handles ACK. */
- if (tsx->handle_ack) {
- PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
- pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
- return;
- }
- } else {
- tsx = NULL;
- PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
- return;
- }
- }
-
- /* For discard event, transaction is NULL. */
- if (tsx == NULL) {
- return;
- }
-
- /* Try to pickup the dlg from the transaction. */
- dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
-
- if (dlg != NULL) {
-
- /* Nothing to do now. */
-
- } else if (event->src_type == PJSIP_EVENT_RX_MSG) {
-
- /* This must be a new UAS transaction. */
-
- /* Finds dlg that can handle this transaction. */
- dlg = find_dialog( ua, event->src.rdata);
-
- /* Create a new dlg if there's no existing dlg that can handle
- the request, ONLY if the incoming message is an INVITE request.
- */
- if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
-
- if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
- /* Create new dialog. */
- dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
-
- if (dlg == NULL ||
- pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0)
- {
- pjsip_tx_data *tdata;
-
- /* Dialog initialization has failed. Respond request with 500 */
- if (dlg) {
- pjsip_ua_destroy_dialog(dlg);
- }
- tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
- PJSIP_SC_INTERNAL_SERVER_ERROR);
- if (tdata) {
- pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
- }
- return;
- }
-
- } else {
- pjsip_tx_data *tdata;
-
- /* Check the method */
- switch (tsx->method.id) {
- case PJSIP_INVITE_METHOD:
- case PJSIP_ACK_METHOD:
- case PJSIP_BYE_METHOD:
- case PJSIP_CANCEL_METHOD:
- /* Stale non-INVITE request.
- * For now, respond all stale requests with 481 (?).
- */
- tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
- if (tdata) {
- pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
- }
- break;
- }
-
- return;
- }
- } else {
- /* Check the method */
- switch (tsx->method.id) {
- case PJSIP_INVITE_METHOD:
- case PJSIP_ACK_METHOD:
- case PJSIP_BYE_METHOD:
- case PJSIP_CANCEL_METHOD:
- /* These methods belongs to dialog.
- * If we receive these methods while no dialog is found,
- * then it must be a stale responses.
- */
- break;
- default:
- return;
- }
-
- }
-
- if (dlg == NULL) {
- PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
- event->src.rdata,
- pj_sockaddr_get_str_addr(&event->src.rdata->addr),
- pj_sockaddr_get_port(&event->src.rdata->addr)));
- }
-
- /* Set the dlg in the transaction (dlg can be NULL). */
- tsx->module_data[ua->mod_id] = dlg;
-
- } else {
- /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
- * if UAS is responding to a transaction which does not exist.
- * Just ignore.
- */
- return;
- }
-
- /* Pass the event to the dlg. */
- if (dlg) {
- pjsip_dlg_on_tsx_event(dlg, tsx, event);
- }
-}
-
-/*
- * Register dialog to UA.
- */
-static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
- pj_str_t *key )
-{
- /* Assure that no entry with similar key exists in the hash table. */
- pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
-
- /* Insert entry to hash table. */
- pj_hash_set( dlg->pool, ua->dlg_table,
- key->ptr, key->slen, dlg);
-
- /* Insert to the list. */
- pj_list_insert_before(&ua->dlg_list, dlg);
- return PJ_SUCCESS;
-}
-
-/*
- * Create a new dialog.
- */
-PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
- pjsip_role_e role )
-{
- pj_pool_t *pool;
- pjsip_dlg *dlg;
-
- PJ_UNUSED_ARG(ua)
-
- /* Create pool for the dialog. */
- pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
- PJSIP_POOL_LEN_DIALOG,
- PJSIP_POOL_INC_DIALOG);
-
- /* Create the dialog. */
- dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
- dlg->pool = pool;
- dlg->ua = ua;
- dlg->role = role;
- sprintf(dlg->obj_name, "dlg%p", dlg);
-
- /* Create mutex for the dialog. */
- dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
- if (!dlg->mutex) {
- pjsip_endpt_destroy_pool(ua->endpt, pool);
- return NULL;
- }
-
- /* Create unique tag for the dialog. */
- pj_create_unique_string( pool, &dlg->local.tag );
-
- /* Register dialog. */
- pj_mutex_lock(ua->mutex);
- if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) {
- pj_mutex_unlock(ua->mutex);
- pj_mutex_destroy(dlg->mutex);
- pjsip_endpt_destroy_pool( ua->endpt, pool );
- return NULL;
- }
- pj_mutex_unlock(ua->mutex);
-
- PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
- return dlg;
-}
-
-/*
- * Destroy dialog.
- */
-PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
-{
- PJ_LOG(5, (dlg->obj_name, "destroying.."));
-
- /* Lock dialog's mutex.
- * Check the mutex validity first since this function can be called
- * on dialog initialization failure (which might be because mutex could not
- * be allocated in the first place).
- */
- if (dlg->mutex) {
- pj_mutex_lock(dlg->mutex);
- }
-
- /* This must be called while holding dialog's mutex, if any. */
- pjsip_on_dialog_destroyed(dlg);
-
- /* Lock UA. */
- pj_mutex_lock(dlg->ua->mutex);
-
- /* Erase from hash table. */
- pj_hash_set( dlg->pool, dlg->ua->dlg_table,
- dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
-
- /* Erase from the list. */
- pj_list_erase(dlg);
-
- /* Unlock UA. */
- pj_mutex_unlock(dlg->ua->mutex);
-
- /* Unlock mutex. */
- if (dlg->mutex) {
- pj_mutex_unlock(dlg->mutex);
- }
-
- /* Destroy the pool. */
- pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
-}
-
-/*
- * Dump user agent state to log file.
- */
-PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
-{
-#if PJ_LOG_MAX_LEVEL >= 3
- PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
- PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u",
- pj_pool_get_capacity(ua->pool),
- pj_pool_get_used_size(ua->pool)));
- PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
-
- if (pj_hash_count(ua->dlg_table)) {
- pjsip_dlg *dlg;
-
- PJ_LOG(3,(LOG_THIS, " Dumping dialog list:"));
- dlg = ua->dlg_list.next;
- while (dlg != (pjsip_dlg*) &ua->dlg_list) {
- PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name,
- pjsip_dlg_state_str(dlg->state)));
- dlg = dlg->next;
- }
- }
-#endif
-}
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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
+ */
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_util.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <pj/hash.h>
+#include <pj/pool.h>
+
+#define PJSIP_POOL_LEN_USER_AGENT 1024
+#define PJSIP_POOL_INC_USER_AGENT 0
+
+
+#define LOG_THIS "useragent.."
+
+/*
+ * Static prototypes.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id );
+static pj_status_t ua_start( struct pjsip_module *mod );
+static pj_status_t ua_deinit( struct pjsip_module *mod );
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
+ pjsip_rx_data *rdata );
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+ pj_str_t *key );
+PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
+
+/*
+ * Default UA instance.
+ */
+static pjsip_user_agent ua_instance;
+
+/*
+ * Module interface.
+ */
+static struct pjsip_module mod_ua =
+{
+ { "User-Agent", 10 }, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ NULL, /* User agent instance, initialized by APP. */
+ 0, /* Number of methods supported (will be initialized later). */
+ { 0 }, /* Array of methods (will be initialized later) */
+ &ua_init, /* init_module() */
+ &ua_start, /* start_module() */
+ &ua_deinit, /* deinit_module() */
+ &ua_tsx_handler, /* tsx_handler() */
+};
+
+/*
+ * Initialize user agent instance.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ static pjsip_method m_invite, m_ack, m_cancel, m_bye;
+ pjsip_user_agent *ua = mod->mod_data;
+ extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
+
+ pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
+ pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
+ pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
+ pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
+
+ mod->method_cnt = 4;
+ mod->methods[0] = &m_invite;
+ mod->methods[1] = &m_ack;
+ mod->methods[2] = &m_cancel;
+ mod->methods[3] = &m_bye;
+
+ /* Initialize the user agent. */
+ ua->endpt = endpt;
+ ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
+ PJSIP_POOL_INC_UA);
+ if (!ua->pool) {
+ return -1;
+ }
+ ua->mod_id = id;
+ ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
+ if (!ua->mutex) {
+ return -1;
+ }
+ ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
+ if (ua->dlg_table == NULL) {
+ return -1;
+ }
+ pj_list_init(&ua->dlg_list);
+
+ /* Initialize dialog lock. */
+ pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
+ if (pjsip_dlg_lock_tls_id == -1) {
+ return -1;
+ }
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
+
+ return 0;
+}
+
+/*
+ * Start user agent instance.
+ */
+static pj_status_t ua_start( struct pjsip_module *mod )
+{
+ PJ_UNUSED_ARG(mod)
+ return 0;
+}
+
+/*
+ * Destroy user agent.
+ */
+static pj_status_t ua_deinit( struct pjsip_module *mod )
+{
+ pjsip_user_agent *ua = mod->mod_data;
+
+ pj_mutex_unlock(ua->mutex);
+
+ /* Release pool */
+ if (ua->pool) {
+ pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
+ }
+ return 0;
+}
+
+/*
+ * Get the module interface for the UA module.
+ */
+PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
+{
+ mod_ua.mod_data = &ua_instance;
+ return &mod_ua;
+}
+
+/*
+ * Register callback to receive dialog notifications.
+ */
+PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
+ pjsip_dlg_callback *cb )
+{
+ ua->dlg_cb = cb;
+}
+
+/*
+ * Find dialog.
+ * This function is called for a new transactions, which a dialog hasn't been
+ * 'attached' to the transaction.
+ */
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
+{
+ pjsip_dlg *dlg;
+ pj_str_t *tag;
+
+ /* Non-CANCEL requests/response can be found by looking at the tag in the
+ * hash table. CANCEL requests don't have tags, so instead we'll try to
+ * find the UAS INVITE transaction in endpoint's hash table
+ */
+ if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
+
+ /* Create key for the rdata, but this time, use INVITE as the
+ * method.
+ */
+ pj_str_t key;
+ pjsip_role_e role;
+ pjsip_method invite_method;
+ pjsip_transaction *invite_tsx;
+
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ role = PJSIP_ROLE_UAS;
+ } else {
+ role = PJSIP_ROLE_UAC;
+ }
+ pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
+ pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
+
+ /* Lookup the INVITE transaction */
+ invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
+
+ /* We should find the dialog attached to the INVITE transaction */
+ return invite_tsx ?
+ (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
+
+ } else {
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ tag = &rdata->to_tag;
+ } else {
+ tag = &rdata->from_tag;
+ }
+ /* Find the dialog in UA hash table */
+ pj_mutex_lock(ua->mutex);
+ dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
+ pj_mutex_unlock(ua->mutex);
+ }
+
+ return dlg;
+}
+
+/*
+ * This function receives event notification from transactions. It is called by
+ * endpoint.
+ */
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_user_agent *ua = mod->mod_data;
+ pjsip_dlg *dlg = NULL;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)",
+ (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type),
+ pjsip_event_str(event->src_type), event->src.data));
+
+ /* Special case to handle ACK which doesn't match any INVITE transactions. */
+ if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
+ /* Find the dialog based on the "tag". */
+ dlg = find_dialog( ua, event->src.rdata );
+
+ /* We should be able to find it. */
+ if (!dlg) {
+ PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
+ return;
+ }
+
+ /* Match CSeq with pending INVITE in dialog. */
+ if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
+ /* A match found. */
+ tsx = dlg->invite_tsx;
+
+ /* Pass the event to transaction if transaction handles ACK. */
+ if (tsx->handle_ack) {
+ PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
+ pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
+ return;
+ }
+ } else {
+ tsx = NULL;
+ PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
+ return;
+ }
+ }
+
+ /* For discard event, transaction is NULL. */
+ if (tsx == NULL) {
+ return;
+ }
+
+ /* Try to pickup the dlg from the transaction. */
+ dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
+
+ if (dlg != NULL) {
+
+ /* Nothing to do now. */
+
+ } else if (event->src_type == PJSIP_EVENT_RX_MSG) {
+
+ /* This must be a new UAS transaction. */
+
+ /* Finds dlg that can handle this transaction. */
+ dlg = find_dialog( ua, event->src.rdata);
+
+ /* Create a new dlg if there's no existing dlg that can handle
+ the request, ONLY if the incoming message is an INVITE request.
+ */
+ if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
+
+ if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
+ /* Create new dialog. */
+ dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
+
+ if (dlg == NULL ||
+ pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0)
+ {
+ pjsip_tx_data *tdata;
+
+ /* Dialog initialization has failed. Respond request with 500 */
+ if (dlg) {
+ pjsip_ua_destroy_dialog(dlg);
+ }
+ tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ if (tdata) {
+ pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+ }
+ return;
+ }
+
+ } else {
+ pjsip_tx_data *tdata;
+
+ /* Check the method */
+ switch (tsx->method.id) {
+ case PJSIP_INVITE_METHOD:
+ case PJSIP_ACK_METHOD:
+ case PJSIP_BYE_METHOD:
+ case PJSIP_CANCEL_METHOD:
+ /* Stale non-INVITE request.
+ * For now, respond all stale requests with 481 (?).
+ */
+ tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
+ if (tdata) {
+ pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+ }
+ break;
+ }
+
+ return;
+ }
+ } else {
+ /* Check the method */
+ switch (tsx->method.id) {
+ case PJSIP_INVITE_METHOD:
+ case PJSIP_ACK_METHOD:
+ case PJSIP_BYE_METHOD:
+ case PJSIP_CANCEL_METHOD:
+ /* These methods belongs to dialog.
+ * If we receive these methods while no dialog is found,
+ * then it must be a stale responses.
+ */
+ break;
+ default:
+ return;
+ }
+
+ }
+
+ if (dlg == NULL) {
+ PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
+ event->src.rdata,
+ pj_sockaddr_get_str_addr(&event->src.rdata->addr),
+ pj_sockaddr_get_port(&event->src.rdata->addr)));
+ }
+
+ /* Set the dlg in the transaction (dlg can be NULL). */
+ tsx->module_data[ua->mod_id] = dlg;
+
+ } else {
+ /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
+ * if UAS is responding to a transaction which does not exist.
+ * Just ignore.
+ */
+ return;
+ }
+
+ /* Pass the event to the dlg. */
+ if (dlg) {
+ pjsip_dlg_on_tsx_event(dlg, tsx, event);
+ }
+}
+
+/*
+ * Register dialog to UA.
+ */
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+ pj_str_t *key )
+{
+ /* Assure that no entry with similar key exists in the hash table. */
+ pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
+
+ /* Insert entry to hash table. */
+ pj_hash_set( dlg->pool, ua->dlg_table,
+ key->ptr, key->slen, dlg);
+
+ /* Insert to the list. */
+ pj_list_insert_before(&ua->dlg_list, dlg);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create a new dialog.
+ */
+PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
+ pjsip_role_e role )
+{
+ pj_pool_t *pool;
+ pjsip_dlg *dlg;
+
+ PJ_UNUSED_ARG(ua)
+
+ /* Create pool for the dialog. */
+ pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
+ PJSIP_POOL_LEN_DIALOG,
+ PJSIP_POOL_INC_DIALOG);
+
+ /* Create the dialog. */
+ dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
+ dlg->pool = pool;
+ dlg->ua = ua;
+ dlg->role = role;
+ sprintf(dlg->obj_name, "dlg%p", dlg);
+
+ /* Create mutex for the dialog. */
+ dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+ if (!dlg->mutex) {
+ pjsip_endpt_destroy_pool(ua->endpt, pool);
+ return NULL;
+ }
+
+ /* Create unique tag for the dialog. */
+ pj_create_unique_string( pool, &dlg->local.tag );
+
+ /* Register dialog. */
+ pj_mutex_lock(ua->mutex);
+ if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) {
+ pj_mutex_unlock(ua->mutex);
+ pj_mutex_destroy(dlg->mutex);
+ pjsip_endpt_destroy_pool( ua->endpt, pool );
+ return NULL;
+ }
+ pj_mutex_unlock(ua->mutex);
+
+ PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
+ return dlg;
+}
+
+/*
+ * Destroy dialog.
+ */
+PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
+{
+ PJ_LOG(5, (dlg->obj_name, "destroying.."));
+
+ /* Lock dialog's mutex.
+ * Check the mutex validity first since this function can be called
+ * on dialog initialization failure (which might be because mutex could not
+ * be allocated in the first place).
+ */
+ if (dlg->mutex) {
+ pj_mutex_lock(dlg->mutex);
+ }
+
+ /* This must be called while holding dialog's mutex, if any. */
+ pjsip_on_dialog_destroyed(dlg);
+
+ /* Lock UA. */
+ pj_mutex_lock(dlg->ua->mutex);
+
+ /* Erase from hash table. */
+ pj_hash_set( dlg->pool, dlg->ua->dlg_table,
+ dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
+
+ /* Erase from the list. */
+ pj_list_erase(dlg);
+
+ /* Unlock UA. */
+ pj_mutex_unlock(dlg->ua->mutex);
+
+ /* Unlock mutex. */
+ if (dlg->mutex) {
+ pj_mutex_unlock(dlg->mutex);
+ }
+
+ /* Destroy the pool. */
+ pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
+}
+
+/*
+ * Dump user agent state to log file.
+ */
+PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
+ PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u",
+ pj_pool_get_capacity(ua->pool),
+ pj_pool_get_used_size(ua->pool)));
+ PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
+
+ if (pj_hash_count(ua->dlg_table)) {
+ pjsip_dlg *dlg;
+
+ PJ_LOG(3,(LOG_THIS, " Dumping dialog list:"));
+ dlg = ua->dlg_list.next;
+ while (dlg != (pjsip_dlg*) &ua->dlg_list) {
+ PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name,
+ pjsip_dlg_state_str(dlg->state)));
+ dlg = dlg->next;
+ }
+ }
+#endif
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_ua_private.h b/pjsip/src/pjsip-ua/sip_ua_private.h
index 93657ddf..8a174afe 100644
--- a/pjsip/src/pjsip-ua/sip_ua_private.h
+++ b/pjsip/src/pjsip-ua/sip_ua_private.h
@@ -1,36 +1,36 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program 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.
- *
- * This program 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
- */
-#ifndef __PJSIP_UA_PRIVATE_H__
-#define __PJSIP_UA_PRIVATE_H__
-
-
-/*
- * Internal dialog functions.
- */
-pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg,
- pjsip_rx_data *rdata );
-
-
-void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
- pjsip_transaction *tsx,
- pjsip_event *event);
-
-
-#endif /* __PJSIP_UA_PRIVATE_H__ */
-
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program 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.
+ *
+ * This program 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
+ */
+#ifndef __PJSIP_UA_PRIVATE_H__
+#define __PJSIP_UA_PRIVATE_H__
+
+
+/*
+ * Internal dialog functions.
+ */
+pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg,
+ pjsip_rx_data *rdata );
+
+
+void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+
+
+#endif /* __PJSIP_UA_PRIVATE_H__ */
+