From 0d61adeb5f784b45f76d76dad9974f4111fb3c8c Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Mon, 30 Jan 2006 18:40:05 +0000 Subject: Finished implementation of UA layer (to be tested) git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@127 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/src/pjsip-ua/sip_dialog.c | 1802 ---------------------------- pjsip/src/pjsip-ua/sip_ua.c | 473 -------- pjsip/src/pjsip-ua/sip_ua_private.h | 36 - pjsip/src/pjsip/sip_auth_client.c | 36 + pjsip/src/pjsip/sip_dialog.c | 1138 ++++++++++++++++++ pjsip/src/pjsip/sip_endpoint.c | 99 +- pjsip/src/pjsip/sip_errno.c | 6 +- pjsip/src/pjsip/sip_msg.c | 376 ++++-- pjsip/src/pjsip/sip_parser.c | 16 +- pjsip/src/pjsip/sip_tel_uri.c | 12 +- pjsip/src/pjsip/sip_transaction.c | 38 +- pjsip/src/pjsip/sip_transport.c | 45 +- pjsip/src/pjsip/sip_ua_layer.c | 674 +++++++++++ pjsip/src/pjsip/sip_uri.c | 38 +- pjsip/src/pjsip/sip_util.c | 10 +- pjsip/src/test-pjsip/dlg_core_test.c | 22 + pjsip/src/test-pjsip/msg_logger.c | 4 +- pjsip/src/test-pjsip/msg_test.c | 45 +- pjsip/src/test-pjsip/test.c | 2 +- pjsip/src/test-pjsip/transport_loop_test.c | 2 +- pjsip/src/test-pjsip/transport_test.c | 6 +- pjsip/src/test-pjsip/transport_udp_test.c | 2 +- pjsip/src/test-pjsip/tsx_basic_test.c | 2 +- pjsip/src/test-pjsip/tsx_uac_test.c | 6 +- pjsip/src/test-pjsip/tsx_uas_test.c | 6 +- pjsip/src/test-pjsip/txdata_test.c | 2 +- pjsip/src/test-pjsip/uri_test.c | 38 +- 27 files changed, 2394 insertions(+), 2542 deletions(-) delete mode 100644 pjsip/src/pjsip-ua/sip_dialog.c delete mode 100644 pjsip/src/pjsip-ua/sip_ua.c delete mode 100644 pjsip/src/pjsip-ua/sip_ua_private.h create mode 100644 pjsip/src/pjsip/sip_dialog.c create mode 100644 pjsip/src/pjsip/sip_ua_layer.c create mode 100644 pjsip/src/test-pjsip/dlg_core_test.c (limited to 'pjsip/src') diff --git a/pjsip/src/pjsip-ua/sip_dialog.c b/pjsip/src/pjsip-ua/sip_dialog.c deleted file mode 100644 index 722c06d8..00000000 --- a/pjsip/src/pjsip-ua/sip_dialog.c +++ /dev/null @@ -1,1802 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* 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_sip_uri *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_sip_uri*) 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_CATCH_ANY { - /* 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_ua.c b/pjsip/src/pjsip-ua/sip_ua.c deleted file mode 100644 index ac0980f5..00000000 --- a/pjsip/src/pjsip-ua/sip_ua.c +++ /dev/null @@ -1,473 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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_load(pjsip_endpoint *endpt); -static pj_status_t ua_unload(void); -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 ); - -/* - * Module interface. - */ -static struct user_agent -{ - pjsip_module mod; - pj_pool_t *pool; - pjsip_endpoint *endpt; - pj_mutex_t *mutex; - pj_hash_table_t *dlg_table; - pjsip_dialog dlg_list; - -} mod_ua = -{ - { - NULL, NULL, /* prev, next. */ - { "mod-ua", 6 }, /* Name. */ - -1, /* Id */ - PJSIP_MOD_PRIORITY_UA_PROXY_LAYER, /* Priority */ - NULL, /* User data. */ - 0, /* Number of methods supported. */ - { 0 }, /* Array of methods */ - &ua_load, /* load() */ - NULL, /* start() */ - NULL, /* stop() */ - &ua_unload, /* unload() */ - NULL, /* on_rx_request() */ - NULL, /* on_rx_response() */ - NULL, /* on_tx_request. */ - NULL, /* on_tx_response() */ - NULL, /* on_tsx_state() */ - } -}; - -/* - * Initialize user agent instance. - */ -static pj_status_t ua_load( pjsip_endpoint *endpt ) -{ - extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */ - pj_status_t status; - - /* Initialize the user agent. */ - mod_ua.endpt = endpt; - status = pjsip_endpt_create_pool( endpt, "pua%p", PJSIP_POOL_LEN_UA, - PJSIP_POOL_INC_UA, &mod_ua.pool); - if (status != PJ_SUCCESS) - return status; - - status = pj_mutex_create_recursive(mod_ua.pool, " ua%p", &mod_ua.mutex); - if (status != PJ_SUCCESS) - return status; - - mod_ua.dlg_table = pj_hash_create(mod_ua.pool, PJSIP_MAX_DIALOG_COUNT); - if (ua->dlg_table == NULL) - return PJ_ENOMEM; - - pj_list_init(&mod_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 PJ_SUCCESS; -} - -/* - * Destroy user agent. - */ -static pj_status_t ua_unload() -{ - pj_mutex_unlock(mod_ua.mutex); - - /* Release pool */ - if (mod_ua.pool) { - pjsip_endpt_destroy_pool( mod_ua.endpt, mod_ua.pool ); - } - return PJ_SUCCESS; -} - -/* - * 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 deleted file mode 100644 index 8a174afe..00000000 --- a/pjsip/src/pjsip-ua/sip_ua_private.h +++ /dev/null @@ -1,36 +0,0 @@ -/* $Id$ */ -/* - * Copyright (C) 2003-2006 Benny Prijono - * - * 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__ */ - diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index 523b35a9..1caaa901 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -353,6 +353,42 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_init( pjsip_auth_clt_sess *sess, } +/* Clone session. */ +PJ_DEF(pj_status_t) pjsip_auth_clt_clone( pj_pool_t *pool, + pjsip_auth_clt_sess *sess, + const pjsip_auth_clt_sess *rhs ) +{ + unsigned i; + + PJ_ASSERT_RETURN(pool && sess && rhs, PJ_EINVAL); + + sess->pool = pool; + sess->endpt = (pjsip_endpoint*)rhs->endpt; + sess->cred_cnt = rhs->cred_cnt; + sess->cred_info = pj_pool_alloc(pool, + sess->cred_cnt*sizeof(pjsip_cred_info)); + for (i=0; icred_cnt; ++i) { + pj_strdup(pool, &sess->cred_info[i].realm, &rhs->cred_info[i].realm); + pj_strdup(pool, &sess->cred_info[i].scheme, &rhs->cred_info[i].scheme); + pj_strdup(pool, &sess->cred_info[i].username, + &rhs->cred_info[i].username); + sess->cred_info[i].data_type = rhs->cred_info[i].data_type; + pj_strdup(pool, &sess->cred_info[i].data, &rhs->cred_info[i].data); + } + + /* TODO note: + * Cloning the full authentication client is quite a big task. + * We do only the necessary bits here, i.e. cloning the credentials. + * The drawback of this basic approach is, a forked dialog will have to + * re-authenticate itself on the next request because it has lost the + * cached authentication headers. + */ + PJ_TODO(FULL_CLONE_OF_AUTH_CLIENT_SESSION); + + return PJ_SUCCESS; +} + + /* Set client credentials. */ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, int cred_cnt, diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c new file mode 100644 index 00000000..d387b8ae --- /dev/null +++ b/pjsip/src/pjsip/sip_dialog.c @@ -0,0 +1,1138 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define THIS_FILE "sip_dialog.c" + + +PJ_DEF(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m) +{ + pjsip_method subscribe = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 10}}; + pjsip_method refer = { PJSIP_OTHER_METHOD, {"REFER", 5}}; + + return m->id == PJSIP_INVITE_METHOD || + (pjsip_method_cmp(m, &subscribe)==0) || + (pjsip_method_cmp(m, &refer)==0); +} + +static pj_status_t create_dialog( pjsip_user_agent *ua, + pjsip_dialog **p_dlg) +{ + pjsip_endpoint *endpt; + pj_pool_t *pool; + pjsip_dialog *dlg; + pj_status_t status; + + endpt = pjsip_ua_get_endpt(ua); + if (!endpt) + return PJ_EINVALIDOP; + + pool = pjsip_endpt_create_pool(endpt, "dlg%p", + PJSIP_POOL_LEN_DIALOG, + PJSIP_POOL_INC_DIALOG); + if (!pool) + return PJ_ENOMEM; + + dlg = pj_pool_zalloc(pool, sizeof(pjsip_dialog)); + PJ_ASSERT_RETURN(dlg != NULL, PJ_ENOMEM); + + dlg->pool = pool; + pj_sprintf(dlg->obj_name, "dlg%p", dlg); + dlg->ua = ua; + + status = pj_mutex_create_recursive(pool, "dlg%p", &dlg->mutex); + if (status != PJ_SUCCESS) + goto on_error; + + + *p_dlg = dlg; + return PJ_SUCCESS; + +on_error: + if (dlg->mutex) + pj_mutex_destroy(dlg->mutex); + pjsip_endpt_release_pool(endpt, pool); + return status; +} + +static void destroy_dialog( pjsip_dialog *dlg ) +{ + if (dlg->mutex) + pj_mutex_destroy(dlg->mutex); + pjsip_endpt_release_pool(pjsip_ua_get_endpt(dlg->ua), dlg->pool); +} + + +/* + * Create an UAC dialog. + */ +PJ_DEF(pj_status_t) pjsip_dlg_create_uac( pjsip_user_agent *ua, + const pj_str_t *local_uri, + const pj_str_t *local_contact, + const pj_str_t *remote_uri, + const pj_str_t *target, + pjsip_dialog **p_dlg) +{ + pj_status_t status; + pj_str_t tmp; + pjsip_dialog *dlg; + + /* Check arguments. */ + PJ_ASSERT_RETURN(ua && local_uri && remote_uri && p_dlg, PJ_EINVAL); + + /* Create dialog instance. */ + status = create_dialog(ua, &dlg); + if (status != PJ_SUCCESS) + return status; + + /* Parse target. */ + pj_strdup_with_null(dlg->pool, &tmp, target ? target : remote_uri); + dlg->target = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen, 0); + if (!dlg->target) { + status = PJSIP_EINVALIDURI; + goto on_error; + } + + /* Init local info. */ + dlg->local.info = pjsip_from_hdr_create(dlg->pool); + pj_strdup_with_null(dlg->pool, &tmp, local_uri); + dlg->local.info->uri = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen, 0); + if (!dlg->local.info->uri) { + status = PJSIP_EINVALIDURI; + goto on_error; + } + + /* Generate local tag. */ + pj_create_unique_string(dlg->pool, &dlg->local.info->tag); + + /* Calculate hash value of local tag. */ + dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr, + dlg->local.info->tag.slen); + + /* Randomize local CSeq. */ + dlg->local.first_cseq = pj_rand() % 0x7FFFFFFFL; + dlg->local.cseq = dlg->local.cseq; + + /* Init local contact. */ + dlg->local.contact = pjsip_contact_hdr_create(dlg->pool); + pj_strdup_with_null(dlg->pool, &tmp, + local_contact ? local_contact : local_uri); + dlg->local.contact->uri = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (!dlg->local.contact->uri) { + status = PJSIP_EINVALIDURI; + goto on_error; + } + + /* Init remote info. */ + dlg->remote.info = pjsip_to_hdr_create(dlg->pool); + pj_strdup_with_null(dlg->pool, &tmp, remote_uri); + dlg->remote.info->uri = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen, 0); + if (!dlg->remote.info->uri) { + status = PJSIP_EINVALIDURI; + goto on_error; + } + + /* Initialize remote's CSeq to -1. */ + dlg->remote.cseq = dlg->remote.first_cseq = -1; + + /* Initial role is UAC. */ + dlg->role = PJSIP_ROLE_UAC; + + /* Secure? */ + dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target); + + /* Generate Call-ID header. */ + dlg->call_id = pjsip_cid_hdr_create(dlg->pool); + pj_create_unique_string(dlg->pool, &dlg->call_id->id); + + /* Initial route set is empty. */ + pj_list_init(&dlg->route_set); + + /* Init client authentication session. */ + status = pjsip_auth_clt_init(&dlg->auth_sess, pjsip_ua_get_endpt(ua), + dlg->pool, 0); + if (status != PJ_SUCCESS) + goto on_error; + + /* Register this dialog to user agent. */ + status = pjsip_ua_register_dlg( ua, dlg ); + if (status != PJ_SUCCESS) + goto on_error; + + + /* Done! */ + *p_dlg = dlg; + + return PJ_SUCCESS; + +on_error: + destroy_dialog(dlg); + return status; +} + + +/* + * Create UAS dialog. + */ +PJ_DEF(pj_status_t) pjsip_dlg_create_uas( pjsip_user_agent *ua, + pjsip_rx_data *rdata, + const pj_str_t *contact, + pjsip_dialog **p_dlg) +{ + pj_status_t status; + pjsip_hdr *contact_hdr; + pjsip_rr_hdr *rr; + pjsip_dialog *dlg; + + /* Check arguments. */ + PJ_ASSERT_RETURN(ua && rdata && p_dlg, PJ_EINVAL); + + /* rdata must have request message. */ + PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, + PJSIP_ENOTREQUESTMSG); + + /* Request must not have To tag. + * This should have been checked in the user agent (or application?). + */ + PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen == 0, PJ_EINVALIDOP); + + /* The request must be a dialog establishing request. */ + PJ_ASSERT_RETURN( + pjsip_method_creates_dialog(&rdata->msg_info.msg->line.req.method), + PJ_EINVALIDOP); + + /* Create dialog instance. */ + status = create_dialog(ua, &dlg); + if (status != PJ_SUCCESS) + return status; + + /* Init local info from the To header. */ + dlg->local.info = pjsip_hdr_clone(dlg->pool, rdata->msg_info.to); + pjsip_fromto_hdr_set_from(dlg->local.info); + + /* Generate local tag. */ + pj_create_unique_string(dlg->pool, &dlg->local.info->tag); + + /* Calculate hash value of local tag. */ + dlg->local.tag_hval = pj_hash_calc(0, dlg->local.info->tag.ptr, + dlg->local.info->tag.slen); + + /* Randomize local cseq */ + dlg->local.first_cseq = pj_rand() % 0x7FFFFFFFL; + dlg->local.cseq = dlg->local.first_cseq; + + /* Init local contact. */ + /* TODO: + * Section 12.1.1, paragraph about using SIPS URI in Contact. + * If the request that initiated the dialog contained a SIPS URI + * in the Request-URI or in the top Record-Route header field value, + * if there was any, or the Contact header field if there was no + * Record-Route header field, the Contact header field in the response + * MUST be a SIPS URI. + */ + if (contact) { + pj_str_t tmp; + + dlg->local.contact = pjsip_contact_hdr_create(dlg->pool); + pj_strdup_with_null(dlg->pool, &tmp, contact); + dlg->local.contact->uri = pjsip_parse_uri(dlg->pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + if (!dlg->local.contact->uri) { + status = PJSIP_EINVALIDURI; + goto on_error; + } + + } else { + dlg->local.contact = pjsip_contact_hdr_create(dlg->pool); + dlg->local.contact->uri = dlg->local.info->uri; + } + + /* Init remote info from the From header. */ + dlg->remote.info = pjsip_hdr_clone(dlg->pool, rdata->msg_info.from); + pjsip_fromto_hdr_set_to(dlg->remote.info); + + /* Init remote's contact from Contact header. */ + contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, + NULL); + if (!contact_hdr) { + status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); + goto on_error; + } + dlg->remote.contact = pjsip_hdr_clone(dlg->pool, contact_hdr); + + /* Init remote's CSeq from CSeq header */ + dlg->remote.cseq = dlg->remote.first_cseq = rdata->msg_info.cseq->cseq; + + /* Set initial target to remote's Contact. */ + dlg->target = dlg->remote.contact->uri; + + /* Initial role is UAS */ + dlg->role = PJSIP_ROLE_UAS; + + /* Secure? + * RFC 3261 Section 12.1.1: + * If the request arrived over TLS, and the Request-URI contained a + * SIPS URI, the 'secure' flag is set to TRUE. + */ + dlg->secure = PJSIP_TRANSPORT_IS_SECURE(rdata->tp_info.transport) && + PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.msg->line.req.uri); + + /* Call-ID */ + dlg->call_id = pjsip_hdr_clone(dlg->pool, rdata->msg_info.cid); + + /* Route set. + * RFC 3261 Section 12.1.1: + * The route set MUST be set to the list of URIs in the Record-Route + * header field from the request, taken in order and preserving all URI + * parameters. If no Record-Route header field is present in the request, + * the route set MUST be set to the empty set. + */ + pj_list_init(&dlg->route_set); + rr = rdata->msg_info.record_route; + while (rr != NULL) { + pjsip_route_hdr *route; + + /* Clone the Record-Route, change the type to Route header. */ + route = pjsip_hdr_clone(dlg->pool, rr); + pjsip_routing_hdr_set_route(route); + + /* Add to route set. */ + pj_list_push_back(&dlg->route_set, route); + + /* Find next Record-Route header. */ + rr = rr->next; + if (rr == (pjsip_rr_hdr*)&rdata->msg_info.msg->hdr) + break; + rr = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_RECORD_ROUTE, rr); + } + + /* Init client authentication session. */ + status = pjsip_auth_clt_init(&dlg->auth_sess, pjsip_ua_get_endpt(ua), + dlg->pool, 0); + if (status != PJ_SUCCESS) + goto on_error; + + /* Calculate hash value of remote tag. */ + dlg->remote.tag_hval = pj_hash_calc(0, dlg->remote.info->tag.ptr, + dlg->remote.info->tag.slen); + + /* Register this dialog to user agent. */ + status = pjsip_ua_register_dlg( ua, dlg ); + if (status != PJ_SUCCESS) + goto on_error; + + /* Put this dialog in rdata's mod_data */ + rdata->endpt_info.mod_data[ua->id] = dlg; + + PJ_TODO(DIALOG_APP_TIMER); + + /* Done. */ + *p_dlg = dlg; + return PJ_SUCCESS; + +on_error: + destroy_dialog(dlg); + return status; +} + + +/* + * Create forked dialog from a response. + */ +PJ_DEF(pj_status_t) pjsip_dlg_fork( const pjsip_dialog *first_dlg, + const pjsip_rx_data *rdata, + pjsip_dialog **new_dlg ) +{ + pjsip_dialog *dlg; + const pjsip_route_hdr *r; + pj_status_t status; + + /* Check arguments. */ + PJ_ASSERT_RETURN(first_dlg && rdata && new_dlg, PJ_EINVAL); + + /* rdata must be response message. */ + PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG, + PJSIP_ENOTRESPONSEMSG); + + /* To tag must present in the response. */ + PJ_ASSERT_RETURN(rdata->msg_info.to->tag.slen != 0, PJSIP_EMISSINGTAG); + + /* Create the dialog. */ + status = create_dialog((pjsip_user_agent*)first_dlg->ua, &dlg); + if (status != PJ_SUCCESS) + return status; + + /* Clone remote target. */ + dlg->target = pjsip_uri_clone(dlg->pool, first_dlg->target); + + /* Clone local info. */ + dlg->local.info = pjsip_hdr_clone(dlg->pool, first_dlg->local.info); + + /* Clone local tag. */ + pj_strdup(dlg->pool, &dlg->local.info->tag, &first_dlg->local.info->tag); + dlg->local.tag_hval = first_dlg->local.tag_hval; + + /* Clone local CSeq. */ + dlg->local.first_cseq = first_dlg->local.first_cseq; + dlg->local.cseq = first_dlg->local.cseq; + + /* Clone local Contact. */ + dlg->local.contact = pjsip_hdr_clone(dlg->pool, first_dlg->local.contact); + + /* Clone remote info. */ + dlg->remote.info = pjsip_hdr_clone(dlg->pool, first_dlg->remote.info); + + /* Set remote tag from the response. */ + pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); + + /* Initialize remote's CSeq to -1. */ + dlg->remote.cseq = dlg->remote.first_cseq = -1; + + /* Initial role is UAC. */ + dlg->role = PJSIP_ROLE_UAC; + + /* Secure? */ + dlg->secure = PJSIP_URI_SCHEME_IS_SIPS(dlg->target); + + /* Clone Call-ID header. */ + dlg->call_id = pjsip_hdr_clone(dlg->pool, first_dlg->call_id); + + /* Duplicate Route-Set. */ + pj_list_init(&dlg->route_set); + r = first_dlg->route_set.next; + while (r != &first_dlg->route_set) { + pjsip_route_hdr *h; + + h = pjsip_hdr_clone(dlg->pool, r); + pj_list_push_back(&dlg->route_set, h); + + r = r->next; + } + + /* Init client authentication session. */ + status = pjsip_auth_clt_clone(dlg->pool, &dlg->auth_sess, + &first_dlg->auth_sess); + if (status != PJ_SUCCESS) + goto on_error; + + /* Register this dialog to user agent. */ + status = pjsip_ua_register_dlg(dlg->ua, dlg ); + if (status != PJ_SUCCESS) + goto on_error; + + + /* Done! */ + *new_dlg = dlg; + + return PJ_SUCCESS; + +on_error: + destroy_dialog(dlg); + return status; +} + + +/* + * Destroy dialog. + */ +static pj_status_t unregister_and_destroy_dialog( pjsip_dialog *dlg ) +{ + pj_status_t status; + + /* Lock must have been held. */ + + /* Check dialog state. */ + /* Number of sessions must be zero. */ + PJ_ASSERT_RETURN(dlg->sess_count==0, PJ_EINVALIDOP); + + /* MUST not have pending transactions. */ + PJ_ASSERT_RETURN(dlg->tsx_count==0, PJ_EINVALIDOP); + + /* Unregister from user agent. */ + status = pjsip_ua_unregister_dlg(dlg->ua, dlg); + if (status != PJ_SUCCESS) { + pj_assert(!"Unexpected failed unregistration!"); + return status; + } + + /* Destroy this dialog. */ + pj_mutex_destroy(dlg->mutex); + pjsip_endpt_release_pool(pjsip_ua_get_endpt(dlg->ua), dlg->pool); + + return PJ_SUCCESS; +} + + +/* + * Set route_set + */ +PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dialog *dlg, + const pjsip_route_hdr *route_set ) +{ + pjsip_route_hdr *r; + + PJ_ASSERT_RETURN(dlg, PJ_EINVAL); + + pj_mutex_lock(dlg->mutex); + + /* Clear route set. */ + pj_list_init(&dlg->route_set); + + if (!route_set) { + pj_mutex_unlock(dlg->mutex); + return PJ_SUCCESS; + } + + r = route_set->next; + while (r != route_set) { + pjsip_route_hdr *new_r; + + new_r = pjsip_hdr_clone(dlg->pool, r); + pj_list_push_back(&dlg->route_set, new_r); + + r = r->next; + } + + pj_mutex_unlock(dlg->mutex); + + return PJ_SUCCESS; +} + + +/* + * Increment session counter. + */ +PJ_DEF(pj_status_t) pjsip_dlg_inc_session( pjsip_dialog *dlg ) +{ + PJ_ASSERT_RETURN(dlg, PJ_EINVAL); + + pj_mutex_lock(dlg->mutex); + ++dlg->sess_count; + pj_mutex_unlock(dlg->mutex); + + return PJ_SUCCESS; +} + +/* + * Decrement session counter. + */ +PJ_DEF(pj_status_t) pjsip_dlg_dec_session( pjsip_dialog *dlg ) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(dlg, PJ_EINVAL); + + pj_mutex_lock(dlg->mutex); + + --dlg->sess_count; + + if (dlg->sess_count==0 && dlg->tsx_count==0) + status = unregister_and_destroy_dialog(dlg); + else { + pj_mutex_unlock(dlg->mutex); + status = PJ_SUCCESS; + } + + return status; +} + + +/* + * Add usage. + */ +PJ_DEF(pj_status_t) pjsip_dlg_add_usage( pjsip_dialog *dlg, + pjsip_module *mod, + void *mod_data ) +{ + unsigned index; + + PJ_ASSERT_RETURN(dlg && mod, PJ_EINVAL); + PJ_ASSERT_RETURN(mod->id >= 0 && mod->id < PJSIP_MAX_MODULE, + PJ_EINVAL); + PJ_ASSERT_RETURN(dlg->usage_cnt < PJSIP_MAX_MODULE, PJ_EBUG); + + pj_mutex_lock(dlg->mutex); + + /* Usages are sorted on priority, lowest number first. + * Find position to put the new module, also makes sure that + * this module has not been registered before. + */ + for (index=0; indexusage_cnt; ++index) { + if (dlg->usage[index] == mod) { + pj_assert(!"This module is already registered"); + pj_mutex_unlock(dlg->mutex); + return PJSIP_ETYPEEXISTS; + } + + if (dlg->usage[index]->priority > mod->priority) + break; + } + + /* index holds position to put the module. + * Insert module at this index. + */ + pj_array_insert(dlg->usage, sizeof(dlg->usage[0]), dlg->usage_cnt, + index, &mod); + + /* Set module data. */ + dlg->mod_data[mod->id] = mod_data; + + pj_mutex_unlock(dlg->mutex); + + return PJ_SUCCESS; +} + + +/* + * 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 pj_status_t dlg_create_request_throw( pjsip_dialog *dlg, + const pjsip_method *method, + int cseq, + pjsip_tx_data **p_tdata ) +{ + pjsip_tx_data *tdata; + pjsip_contact_hdr *contact; + pjsip_route_hdr *route, *end_list; + pj_status_t status; + + /* Contact Header field. + * Contact can only be present in requests that establish dialog (in the + * core SIP spec, only INVITE). + */ + if (pjsip_method_creates_dialog(method)) + contact = dlg->local.contact; + else + contact = NULL; + + /* + * Create the request by cloning from the headers in the + * dialog. + */ + status = pjsip_endpt_create_request_from_hdr(pjsip_ua_get_endpt(dlg->ua), + method, + dlg->target, + dlg->local.info, + dlg->remote.info, + contact, + dlg->call_id, + cseq, + NULL, + &tdata); + if (status != PJ_SUCCESS) + return status; + + /* 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. */ + status = pjsip_auth_clt_init_req( &dlg->auth_sess, tdata ); + if (status != PJ_SUCCESS) + return status; + + /* Done. */ + *p_tdata = tdata; + + return PJ_SUCCESS; +} + + + +/* + * Create outgoing request. + */ +PJ_DEF(pj_status_t) pjsip_dlg_create_request( pjsip_dialog *dlg, + const pjsip_method *method, + int cseq, + pjsip_tx_data **p_tdata) +{ + pj_status_t status; + pjsip_tx_data *tdata = NULL; + PJ_USE_EXCEPTION; + + PJ_ASSERT_RETURN(dlg && method && p_tdata, PJ_EINVAL); + + /* Lock dialog. */ + pj_mutex_lock(dlg->mutex); + + /* Use outgoing CSeq and increment it by one. */ + if (cseq <= 0) + cseq = dlg->local.cseq + 1; + + /* Keep compiler happy */ + status = PJ_EBUG; + + /* Create the request. */ + PJ_TRY { + status = dlg_create_request_throw(dlg, method, cseq, &tdata); + } + PJ_CATCH_ANY { + status = PJ_ENOMEM; + } + PJ_END; + + /* Failed! Delete transmit data. */ + if (status != PJ_SUCCESS && tdata) { + pjsip_tx_data_dec_ref( tdata ); + tdata = NULL; + } + + /* Unlock dialog. */ + pj_mutex_unlock(dlg->mutex); + + *p_tdata = tdata; + + return status; +} + + +/* + * Update CSeq in outgoing request to reflect the dialog. + * Then increment local CSeq. + */ +static void update_cseq( pjsip_dialog *dlg, + pjsip_tx_data *tdata ) +{ + pjsip_msg *msg = tdata->msg; + + /* Start locking the dialog. */ + pj_mutex_lock(dlg->mutex); + + /* Update dialog's CSeq and message's CSeq if request is not + * ACK nor CANCEL. + */ + if (msg->line.req.method.id != PJSIP_CANCEL_METHOD && + msg->line.req.method.id != PJSIP_ACK_METHOD) + { + pjsip_cseq_hdr *ch; + + ch = PJSIP_MSG_CSEQ_HDR(msg); + PJ_ASSERT_ON_FAIL(ch!=NULL, return); + + ch->cseq = dlg->local.cseq++; + + /* Force the whole message to be re-printed. */ + pjsip_tx_data_invalidate_msg( tdata ); + } + +} + +/* + * Send request statefully, and update dialog'c CSeq. + */ +PJ_DEF(pj_status_t) pjsip_dlg_send_request( pjsip_dialog *dlg, + pjsip_tx_data *tdata, + pjsip_transaction **p_tsx ) +{ + pjsip_transaction *tsx; + pj_status_t status; + + /* Check arguments. */ + PJ_ASSERT_RETURN(dlg && tdata && tdata->msg, PJ_EINVAL); + PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, + PJSIP_ENOTREQUESTMSG); + + /* Update CSeq */ + update_cseq(dlg, tdata); + + /* Create a new transaction. + * The transaction user is the user agent module. + */ + status = pjsip_tsx_create_uac(dlg->ua, tdata, &tsx); + if (status != PJ_SUCCESS) + goto on_error; + + /* Attach this dialog to the transaction, so that user agent + * will dispatch events to this dialog. + */ + tsx->mod_data[dlg->ua->id] = dlg; + + /* Increment transaction counter. */ + ++dlg->tsx_count; + + /* Send the message. */ + status = pjsip_tsx_send_msg(tsx, tdata); + if (status != PJ_SUCCESS) { + pjsip_tsx_terminate(tsx, tsx->status_code); + goto on_error; + } + + /* Done. */ + *p_tsx = tsx; + + /* Unlock dialog. */ + pj_mutex_unlock(dlg->mutex); + + return PJ_SUCCESS; + +on_error: + /* Unlock dialog. */ + pj_mutex_unlock(dlg->mutex); + + /* Whatever happen delete the message. */ + pjsip_tx_data_dec_ref( tdata ); + + *p_tsx = NULL; + return status; +} + + +/* + * Create response. + */ +PJ_DEF(pj_status_t) pjsip_dlg_create_response( pjsip_dialog *dlg, + pjsip_rx_data *rdata, + int st_code, + const pj_str_t *st_text, + pjsip_tx_data **p_tdata) +{ + pj_status_t status; + pjsip_cseq_hdr *cseq; + pjsip_tx_data *tdata; + int st_class; + + /* Create generic response. */ + status = pjsip_endpt_create_response(pjsip_ua_get_endpt(dlg->ua), + rdata, st_code, st_text, &tdata); + if (status != PJ_SUCCESS) + return status; + + /* Lock the dialog. */ + pj_mutex_lock(dlg->mutex); + + /* Special treatment for 2xx response to request that establishes + * dialog. + * + * RFC 3261 Section 12.1.1 + * + * When a UAS responds to a request with a response that establishes + * a dialog (such as a 2xx to INVITE): + * - MUST copy all Record-Route header field values from the request + * into the response (including the URIs, URI parameters, and any + * Record-Route header field parameters, whether they are known or + * unknown to the UAS) and MUST maintain the order of those values. + * - The Contact header field contains an address where the UAS would + * like to be contacted for subsequent requests in the dialog. + * + * Also from Table 3, page 119. + */ + cseq = PJSIP_MSG_CSEQ_HDR(tdata->msg); + pj_assert(cseq != NULL); + + st_class = st_code / 100; + + if (cseq->cseq == dlg->remote.first_cseq && + (st_class==1 || st_class==2) && st_code != 100) + { + pjsip_hdr *rr, *hdr; + + /* Duplicate Record-Route header from the request. */ + rr = (pjsip_hdr*) rdata->msg_info.record_route; + while (rr) { + hdr = pjsip_hdr_clone(tdata->pool, rr); + pjsip_msg_add_hdr(tdata->msg, hdr); + + rr = rr->next; + if (rr == &rdata->msg_info.msg->hdr) + break; + rr = pjsip_msg_find_hdr(rdata->msg_info.msg, + PJSIP_H_RECORD_ROUTE, rr); + } + } + + /* Contact header. */ + if (pjsip_method_creates_dialog(&cseq->method)) { + /* Add Contact header for 1xx, 2xx, 3xx and 485 response. */ + if (st_class==2 || st_class==3 || (st_class==1 && st_code != 100) || + st_code==485) + { + /* Add contact header. */ + pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, dlg->local.contact); + pjsip_msg_add_hdr(tdata->msg, hdr); + } + + /* Add Allow header in 2xx and 405 response. */ + if (st_class==2 || st_code==405) { + const pjsip_hdr *c_hdr; + c_hdr = pjsip_endpt_get_capability(pjsip_ua_get_endpt(dlg->ua), + PJSIP_H_ALLOW, NULL); + if (c_hdr) { + pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr); + pjsip_msg_add_hdr(tdata->msg, hdr); + } + } + + /* Add Supported header in 2xx response. */ + if (st_class==2) { + const pjsip_hdr *c_hdr; + c_hdr = pjsip_endpt_get_capability(pjsip_ua_get_endpt(dlg->ua), + PJSIP_H_SUPPORTED, NULL); + if (c_hdr) { + pjsip_hdr *hdr = pjsip_hdr_clone(tdata->pool, c_hdr); + pjsip_msg_add_hdr(tdata->msg, hdr); + } + } + + } + + /* Add To tag in all responses except 100 */ + if (st_code != 100 && rdata->msg_info.to->tag.slen == 0) { + pjsip_to_hdr *to; + + to = PJSIP_MSG_TO_HDR(tdata->msg); + pj_assert(to != NULL); + + to->tag = dlg->local.info->tag; + } + + /* Unlock the dialog. */ + pj_mutex_unlock(dlg->mutex); + + /* Done. */ + *p_tdata = tdata; + return PJ_SUCCESS; +} + +/* + * Modify response. + */ +PJ_DEF(pj_status_t) pjsip_dlg_modify_response( pjsip_dialog *dlg, + pjsip_tx_data *tdata, + int st_code, + const pj_str_t *st_text) +{ + + PJ_ASSERT_RETURN(dlg && tdata && tdata->msg, PJ_EINVAL); + PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG, + PJSIP_ENOTRESPONSEMSG); + PJ_ASSERT_RETURN(st_code >= 100 && st_code <= 699, PJ_EINVAL); + + tdata->msg->line.status.code = st_code; + if (st_text) { + pj_strdup(tdata->pool, &tdata->msg->line.status.reason, st_text); + } else { + tdata->msg->line.status.reason = *pjsip_get_status_text(st_code); + } + + return PJ_SUCCESS; +} + +/* + * Send response statefully. + */ +PJ_DEF(pj_status_t) pjsip_dlg_send_response( pjsip_dialog *dlg, + pjsip_transaction *tsx, + pjsip_tx_data *tdata) +{ + /* Sanity check. */ + PJ_ASSERT_RETURN(dlg && tsx && tdata && tdata->msg, PJ_EINVAL); + PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG, + PJSIP_ENOTRESPONSEMSG); + + /* The transaction must belong to this dialog. */ + PJ_ASSERT_RETURN(tsx->mod_data[dlg->ua->id] == dlg, PJ_EINVALIDOP); + + /* Check that transaction method and cseq match the response. + * This operation is sloooww (search CSeq header twice), that's why + * we only do it in debug mode. + */ +#if defined(PJ_DEBUG) && PJ_DEBUG!=0 + PJ_ASSERT_RETURN( PJSIP_MSG_CSEQ_HDR(tdata->msg)->cseq == tsx->cseq && + pjsip_method_cmp(&PJSIP_MSG_CSEQ_HDR(tdata->msg)->method, + &tsx->method)==0, + PJ_EINVALIDOP); +#endif + + return pjsip_tsx_send_msg(tsx, tdata); +} + + +/* This function is called by user agent upon receiving incoming response + * message. + */ +void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) +{ + pj_status_t status; + pjsip_transaction *tsx; + unsigned i; + + /* Lock the dialog. */ + pj_mutex_lock(dlg->mutex); + + /* Create UAS transaction for this request. */ + status = pjsip_tsx_create_uas(dlg->ua, rdata, &tsx); + PJ_ASSERT_ON_FAIL(status==PJ_SUCCESS,{goto on_return;}); + + /* Put this dialog in the transaction data. */ + tsx->mod_data[dlg->ua->id] = dlg; + + /* Add transaction count. */ + ++dlg->tsx_count; + + /* Report the request to dialog usages. */ + for (i=0; iusage_cnt; ++i) { + pj_bool_t processed; + + if (!dlg->usage[i]->on_rx_request) + continue; + + processed = (*dlg->usage[i]->on_rx_request)(rdata); + + if (processed) + break; + } + + if (i==dlg->usage_cnt) { + pjsip_tx_data *tdata; + + PJ_LOG(4,(dlg->obj_name, + "%s is unhandled by dialog usages. " + "Dialog will response with 500 (Internal Server Error)", + pjsip_rx_data_get_info(rdata))); + status = pjsip_endpt_create_response(pjsip_ua_get_endpt(dlg->ua), + rdata, + PJSIP_SC_INTERNAL_SERVER_ERROR, + NULL, &tdata); + if (status == PJ_SUCCESS) + status = pjsip_tsx_send_msg(tsx, tdata); + + if (status != PJ_SUCCESS) { + char errmsg[PJSIP_ERR_MSG_SIZE]; + pj_strerror(status, errmsg, sizeof(errmsg)); + PJ_LOG(4,(dlg->obj_name,"Error sending %s: %s", + pjsip_tx_data_get_info(tdata), errmsg)); + pjsip_tsx_terminate(tsx, 500); + } + } + +on_return: + /* Unlock dialog. */ + pj_mutex_unlock(dlg->mutex); +} + +/* This function is called by user agent upon receiving incoming response + * message. + */ +void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) +{ + unsigned i; + + /* Lock the dialog. */ + pj_mutex_lock(dlg->mutex); + + /* Update the remote tag, if none is specified yet. */ + if (dlg->remote.info->tag.slen == 0 && rdata->msg_info.to->tag.slen != 0) { + + pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); + + /* No need to update remote's tag_hval since its never used. */ + } + + /* Check that rdata already has dialog in mod_data. */ + pj_assert(pjsip_rdata_get_dlg(rdata) == dlg); + + /* Pass to dialog usages. */ + for (i=0; iusage_cnt; ++i) { + pj_bool_t processed; + + if (!dlg->usage[i]->on_rx_response) + continue; + + processed = (*dlg->usage[i]->on_rx_response)(rdata); + + if (processed) + break; + } + + if (i==dlg->usage_cnt) { + PJ_LOG(4,(dlg->obj_name, "%s is unhandled by dialog usages", + pjsip_rx_data_get_info(rdata))); + } + + /* Unlock dialog. */ + pj_mutex_unlock(dlg->mutex); +} + +/* This function is called by user agent upon receiving transaction + * state notification. + */ +void pjsip_dlg_on_tsx_state( pjsip_dialog *dlg, + pjsip_transaction *tsx, + pjsip_event *e ) +{ + unsigned i; + + /* Lock the dialog. */ + pj_mutex_lock(dlg->mutex); + + if (tsx->state == PJSIP_TSX_STATE_TERMINATED) + --dlg->tsx_count; + + /* Pass to dialog usages. */ + for (i=0; iusage_cnt; ++i) { + + if (!dlg->usage[i]->on_tsx_state) + continue; + + (*dlg->usage[i]->on_tsx_state)(tsx, e); + } + + if (tsx->state == PJSIP_TSX_STATE_TERMINATED && dlg->tsx_count == 0 && + dlg->sess_count == 0) + { + /* Time to destroy dialog. */ + unregister_and_destroy_dialog(dlg); + + } else { + /* Unlock dialog. */ + pj_mutex_unlock(dlg->mutex); + } +} + + diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index 71e7cff8..3d109e4c 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -77,18 +77,12 @@ struct pjsip_endpoint /** Module list, sorted by priority. */ pjsip_module module_list; - /** Number of supported methods. */ - unsigned method_cnt; - - /** Array of supported methods. */ - const pjsip_method *methods[MAX_METHODS]; - - /** Allow header. */ - pjsip_allow_hdr *allow_hdr; - /** Route header list. */ pjsip_route_hdr route_hdr_list; + /** Capability header list. */ + pjsip_hdr cap_hdr; + /** Additional request headers. */ pjsip_hdr req_hdr; }; @@ -196,7 +190,6 @@ PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt, pj_list_insert_before(m, mod); /* Done. */ - PJ_TODO(BUILD_ALLOW_HEADER_BASED_ON_MODULES_SUPPORTED_METHODS); on_return: pj_rwmutex_unlock_write(endpt->mod_mutex); @@ -248,19 +241,86 @@ PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt, /* Done. */ status = PJ_SUCCESS; - PJ_TODO(REMOVE_METHODS_FROM_ALLOW_HEADER_WHEN_MODULE_IS_UNREGISTERED); - on_return: pj_rwmutex_unlock_write(endpt->mod_mutex); return status; } + +/* + * Get the value of the specified capability header field. + */ +PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_capability( pjsip_endpoint *endpt, + int htype, + const pj_str_t *hname) +{ + pjsip_hdr *hdr = endpt->cap_hdr.next; + + /* Check arguments. */ + PJ_ASSERT_RETURN(endpt != NULL, NULL); + PJ_ASSERT_RETURN(htype != PJSIP_H_OTHER || hname, NULL); + + if (htype != PJSIP_H_OTHER) { + while (hdr != &endpt->cap_hdr) { + if (hdr->type == htype) + return hdr; + hdr = hdr->next; + } + } + return NULL; +} + + /* - * Get "Allow" header. + * Add or register new capabilities as indicated by the tags to the + * appropriate header fields in the endpoint. */ -PJ_DEF(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ) +PJ_DEF(pj_status_t) pjsip_endpt_add_capability( pjsip_endpoint *endpt, + pjsip_module *mod, + int htype, + const pj_str_t *hname, + unsigned count, + const pj_str_t tags[]) { - return endpt->allow_hdr; + pjsip_generic_array_hdr *hdr; + unsigned i; + + /* Check arguments. */ + PJ_ASSERT_RETURN(endpt!=NULL && mod!=NULL && count>0 && tags, PJ_EINVAL); + PJ_ASSERT_RETURN(htype==PJSIP_H_ACCEPT || + htype==PJSIP_H_ALLOW || + htype==PJSIP_H_SUPPORTED, + PJ_EINVAL); + + /* Find the header. */ + hdr = (pjsip_generic_array_hdr*) pjsip_endpt_get_capability(endpt, + htype, hname); + + /* Create the header when it's not present */ + if (hdr == NULL) { + switch (htype) { + case PJSIP_H_ACCEPT: + hdr = pjsip_accept_hdr_create(endpt->pool); + break; + case PJSIP_H_ALLOW: + hdr = pjsip_allow_hdr_create(endpt->pool); + break; + case PJSIP_H_SUPPORTED: + hdr = pjsip_supported_hdr_create(endpt->pool); + break; + default: + return PJ_EINVAL; + } + } + + /* Add the tags to the header. */ + for (i=0; ipool, &hdr->values[hdr->count], &tags[i]); + ++hdr->count; + } + + /* Done. */ + return PJ_SUCCESS; } /* @@ -325,7 +385,7 @@ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, pj_status_t status; pj_pool_t *pool; pjsip_endpoint *endpt; - pjsip_max_forwards_hdr *mf_hdr; + pjsip_max_fwd_hdr *mf_hdr; pj_lock_t *lock = NULL; PJ_LOG(5, (THIS_FILE, "Creating endpoint instance...")); @@ -419,10 +479,13 @@ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, pj_list_init(&endpt->route_hdr_list); /* Add "Max-Forwards" for request header. */ - mf_hdr = pjsip_max_forwards_hdr_create(endpt->pool); - mf_hdr->ivalue = PJSIP_MAX_FORWARDS_VALUE; + mf_hdr = pjsip_max_fwd_hdr_create(endpt->pool, + PJSIP_MAX_FORWARDS_VALUE); pj_list_insert_before( &endpt->req_hdr, mf_hdr); + /* Initialize capability header list. */ + pj_list_init(&endpt->cap_hdr); + /* Done. */ *p_endpt = endpt; return status; diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c index a2faf1a2..077f6553 100644 --- a/pjsip/src/pjsip/sip_errno.c +++ b/pjsip/src/pjsip/sip_errno.c @@ -45,6 +45,7 @@ static const struct { PJSIP_EINVALIDSTATUS, "Invalid status code"}, + { PJSIP_EINVALIDURI, "Invalid URI" }, { PJSIP_EINVALIDSCHEME, "Invalid URI scheme" }, { PJSIP_EMISSINGREQURI, "Missing Request-URI" }, { PJSIP_EINVALIDREQURI, "Invalid Request URI" }, @@ -77,7 +78,10 @@ static const struct { PJSIP_EAUTHACCNOTFOUND, "Account or credential not found" }, { PJSIP_EAUTHACCDISABLED, "Account or credential is disabled" }, { PJSIP_EAUTHINVALIDREALM, "Invalid authorization realm"}, - { PJSIP_EAUTHINVALIDDIGEST, "Invalid authorization digest" } + { PJSIP_EAUTHINVALIDDIGEST, "Invalid authorization digest" }, + + /* UA/dialog layer. */ + { PJSIP_EMISSINGTAG, "Missing From/To tag parameter" } }; diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c index 6010ba05..23e97f77 100644 --- a/pjsip/src/pjsip/sip_msg.c +++ b/pjsip/src/pjsip/sip_msg.c @@ -498,31 +498,40 @@ static pjsip_hdr_vptr generic_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_generic_string_hdr_print, }; -PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create( pj_pool_t *pool, - const pj_str_t *hnames ) +PJ_DEF(pjsip_generic_string_hdr*) +pjsip_generic_string_hdr_init( pj_pool_t *pool, + void *mem, + const pj_str_t *hnames, + const pj_str_t *hvalue) { - pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr)); + pjsip_generic_string_hdr *hdr = mem; + init_hdr(hdr, PJSIP_H_OTHER, &generic_hdr_vptr); if (hnames) { pj_strdup(pool, &hdr->name, hnames); hdr->sname = hdr->name; } - hdr->hvalue.ptr = NULL; - hdr->hvalue.slen = 0; + if (hvalue) { + pj_strdup(pool, &hdr->hvalue, hvalue); + } else { + hdr->hvalue.ptr = NULL; + hdr->hvalue.slen = 0; + } + return hdr; } -PJ_DEF(pjsip_generic_string_hdr*) pjsip_generic_string_hdr_create_with_text( pj_pool_t *pool, - const pj_str_t *hname, - const pj_str_t *hvalue) +PJ_DEF(pjsip_generic_string_hdr*) +pjsip_generic_string_hdr_create( pj_pool_t *pool, + const pj_str_t *hnames, + const pj_str_t *hvalue) { - pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname); - pj_strdup(pool, &hdr->hvalue, hvalue); - return hdr; + void *mem = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr)); + return pjsip_generic_string_hdr_init(pool, mem, hnames, hvalue); } -static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, - char *buf, pj_size_t size) +static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, + char *buf, pj_size_t size) { char *p = buf; @@ -543,11 +552,12 @@ static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, const pjsip_generic_string_hdr *rhs) { - pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, &rhs->name); + pjsip_generic_string_hdr *hdr; + + hdr = pjsip_generic_string_hdr_create(pool, &rhs->name, &rhs->hvalue); hdr->type = rhs->type; hdr->sname = hdr->name; - pj_strdup( pool, &hdr->hvalue, &rhs->hvalue); return hdr; } @@ -578,26 +588,28 @@ static pjsip_hdr_vptr generic_int_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_generic_int_hdr_print, }; -PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, - const pj_str_t *hnames ) +PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_init( pj_pool_t *pool, + void *mem, + const pj_str_t *hnames, + int value) { - pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr)); + pjsip_generic_int_hdr *hdr = mem; + init_hdr(hdr, PJSIP_H_OTHER, &generic_int_hdr_vptr); if (hnames) { pj_strdup(pool, &hdr->name, hnames); hdr->sname = hdr->name; } - hdr->ivalue = 0; + hdr->ivalue = value; return hdr; } -PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create_with_value( pj_pool_t *pool, - const pj_str_t *hname, - int value) +PJ_DEF(pjsip_generic_int_hdr*) pjsip_generic_int_hdr_create( pj_pool_t *pool, + const pj_str_t *hnames, + int value) { - pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname); - hdr->ivalue = value; - return hdr; + void *mem = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr)); + return pjsip_generic_int_hdr_init(pool, mem, hnames, value); } static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, @@ -651,10 +663,13 @@ static pjsip_hdr_vptr generic_array_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_generic_array_hdr_print, }; -PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool, - const pj_str_t *hnames) + +PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_hdr_init( pj_pool_t *pool, + void *mem, + const pj_str_t *hnames) { - pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr)); + pjsip_generic_array_hdr *hdr = mem; + init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr); if (hnames) { pj_strdup(pool, &hdr->name, hnames); @@ -662,6 +677,13 @@ PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_create( pj_pool_t *pool, } hdr->count = 0; return hdr; +} + +PJ_DEF(pjsip_generic_array_hdr*) pjsip_generic_array_hdr_create( pj_pool_t *pool, + const pj_str_t *hnames) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr)); + return pjsip_generic_array_hdr_init(pool, mem, hnames); } @@ -713,38 +735,69 @@ static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t /* * Accept header. */ -PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_accept_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } +PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_accept_hdr)); + return pjsip_accept_hdr_init(pool, mem); +} + /////////////////////////////////////////////////////////////////////////////// /* * Allow header. */ -PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_allow_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } +PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_allow_hdr)); + return pjsip_allow_hdr_init(pool, mem); +} + /////////////////////////////////////////////////////////////////////////////// /* * Call-ID header. */ -PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_cid_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr); return hdr; + +} + +PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_cid_hdr)); + return pjsip_cid_hdr_init(pool, mem); } @@ -763,14 +816,24 @@ static pjsip_hdr_vptr clen_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print, }; -PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr)); + pjsip_clen_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr); hdr->len = 0; return hdr; } +PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr)); + return pjsip_clen_hdr_init(pool, mem); +} + static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size) { @@ -815,9 +878,13 @@ static pjsip_hdr_vptr cseq_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print, }; -PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr)); + pjsip_cseq_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr); hdr->cseq = 0; hdr->method.id = PJSIP_OTHER_METHOD; @@ -826,6 +893,12 @@ PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) return hdr; } +PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr)); + return pjsip_cseq_hdr_init(pool, mem); +} + static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size) { char *p = buf; @@ -884,15 +957,26 @@ static pjsip_hdr_vptr contact_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print, }; -PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + pjsip_contact_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + + pj_memset(mem, 0, sizeof(pjsip_contact_hdr)); init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr); hdr->expires = -1; pj_list_init(&hdr->other_param); return hdr; } +PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_contact_hdr)); + return pjsip_contact_hdr_init(pool, mem); +} + static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size) { @@ -1002,11 +1086,23 @@ static pjsip_hdr_vptr ctype_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print, }; -PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + pjsip_ctype_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + + pj_memset(mem, 0, sizeof(pjsip_ctype_hdr)); init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr); return hdr; + +} + +PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_ctype_hdr)); + return pjsip_ctype_hdr_init(pool, mem); } static int print_media_type(char *buf, const pjsip_media_type *media) @@ -1066,12 +1162,25 @@ static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, /* * Expires header. */ -PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_init( pj_pool_t *pool, + void *mem, + int value) { - pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_expires_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr); - hdr->ivalue = 0; + hdr->ivalue = value; return hdr; + +} + +PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool, + int value ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_expires_hdr)); + return pjsip_expires_hdr_init(pool, mem, value); } /////////////////////////////////////////////////////////////////////////////// @@ -1093,30 +1202,53 @@ static pjsip_hdr_vptr fromto_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print, }; -PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + pjsip_from_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + + pj_memset(mem, 0, sizeof(pjsip_from_hdr)); init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr); pj_list_init(&hdr->other_param); return hdr; } -PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ) { - pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + void *mem = pj_pool_alloc(pool, sizeof(pjsip_from_hdr)); + return pjsip_from_hdr_init(pool, mem); +} + +PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_init( pj_pool_t *pool, + void *mem ) +{ + pjsip_to_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + + pj_memset(mem, 0, sizeof(pjsip_to_hdr)); init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr); pj_list_init(&hdr->other_param); return hdr; + +} + +PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_to_hdr)); + return pjsip_to_hdr_init(pool, mem); } -PJ_DEF(pjsip_from_hdr*) pjsip_fromto_set_from( pjsip_fromto_hdr *hdr ) +PJ_DEF(pjsip_from_hdr*) pjsip_fromto_hdr_set_from( pjsip_fromto_hdr *hdr ) { hdr->type = PJSIP_H_FROM; hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_FROM]; return hdr; } -PJ_DEF(pjsip_to_hdr*) pjsip_fromto_set_to( pjsip_fromto_hdr *hdr ) +PJ_DEF(pjsip_to_hdr*) pjsip_fromto_hdr_set_to( pjsip_fromto_hdr *hdr ) { hdr->type = PJSIP_H_TO; hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO]; @@ -1183,12 +1315,25 @@ pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, /* * Max-Forwards header. */ -PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_max_fwd_hdr*) pjsip_max_fwd_hdr_init( pj_pool_t *pool, + void *mem, + int value) { - pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_max_fwd_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr); - hdr->ivalue = 0; + hdr->ivalue = value; return hdr; + +} + +PJ_DEF(pjsip_max_fwd_hdr*) pjsip_max_fwd_hdr_create(pj_pool_t *pool, + int value) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_max_fwd_hdr)); + return pjsip_max_fwd_hdr_init(pool, mem, value); } @@ -1196,14 +1341,26 @@ PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool) /* * Min-Expires header. */ -PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_init( pj_pool_t *pool, + void *mem, + int value ) { - pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_min_expires_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr); - hdr->ivalue = 0; + hdr->ivalue = value; return hdr; } +PJ_DEF(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool, + int value ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_min_expires_hdr)); + return pjsip_min_expires_hdr_init(pool, mem, value ); +} + /////////////////////////////////////////////////////////////////////////////// /* * Record-Route and Route header. @@ -1219,24 +1376,45 @@ static pjsip_hdr_vptr routing_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print, }; -PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_rr_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr); pjsip_name_addr_init(&hdr->name_addr); pj_list_init(&hdr->other_param); return hdr; + } -PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ) { - pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + void *mem = pj_pool_alloc(pool, sizeof(pjsip_rr_hdr)); + return pjsip_rr_hdr_init(pool, mem); +} + +PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_init( pj_pool_t *pool, + void *mem ) +{ + pjsip_route_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr); pjsip_name_addr_init(&hdr->name_addr); pj_list_init(&hdr->other_param); return hdr; } +PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_route_hdr)); + return pjsip_route_hdr_init(pool, mem); +} + PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr ) { hdr->type = PJSIP_H_RECORD_ROUTE; @@ -1304,52 +1482,93 @@ static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, /* * Require header. */ -PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_require_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } +PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_require_hdr)); + return pjsip_require_hdr_init(pool, mem); +} + /////////////////////////////////////////////////////////////////////////////// /* * Retry-After header. */ -PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_init( pj_pool_t *pool, + void *mem, + int value ) { - pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_retry_after_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr); - hdr->ivalue = 0; + hdr->ivalue = value; return hdr; } +PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool, + int value ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_retry_after_hdr)); + return pjsip_retry_after_hdr_init(pool, mem, value ); +} + /////////////////////////////////////////////////////////////////////////////// /* * Supported header. */ -PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_supported_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } +PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_supported_hdr)); + return pjsip_supported_hdr_init(pool, mem); +} + /////////////////////////////////////////////////////////////////////////////// /* * Unsupported header. */ -PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool) +PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); + pjsip_unsupported_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr); hdr->count = 0; return hdr; } +PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_unsupported_hdr)); + return pjsip_unsupported_hdr_init(pool, mem); +} + /////////////////////////////////////////////////////////////////////////////// /* * Via header. @@ -1365,15 +1584,26 @@ static pjsip_hdr_vptr via_hdr_vptr = (pjsip_hdr_print_fptr) &pjsip_via_hdr_print, }; -PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ) +PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_init( pj_pool_t *pool, + void *mem ) { - pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); + pjsip_via_hdr *hdr = mem; + + PJ_UNUSED_ARG(pool); + + pj_memset(mem, 0, sizeof(pjsip_via_hdr)); init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr); - //hdr->sent_by.port = 5060; hdr->ttl_param = -1; hdr->rport_param = -1; pj_list_init(&hdr->other_param); return hdr; + +} + +PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ) +{ + void *mem = pj_pool_alloc(pool, sizeof(pjsip_via_hdr)); + return pjsip_via_hdr_init(pool, mem); } static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index f281824b..352ea2ed 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1167,10 +1167,10 @@ static pjsip_sip_uri *int_parse_sip_url( pj_scanner *scanner, } if (parser_stricmp(scheme, pjsip_SIP_STR)==0) { - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) { - url = pjsip_url_create(pool, 1); + url = pjsip_sip_uri_create(pool, 1); } else { PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); @@ -1541,7 +1541,7 @@ static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ) /* Parse Expires header. */ static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx) { - pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool); + pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool, 0); parse_generic_int_hdr(hdr, ctx->scanner); return (pjsip_hdr*)hdr; } @@ -1601,7 +1601,7 @@ static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx ) static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx) { pjsip_retry_after_hdr *hdr; - hdr = pjsip_retry_after_hdr_create(ctx->pool); + hdr = pjsip_retry_after_hdr_create(ctx->pool, 0); parse_generic_int_hdr(hdr, ctx->scanner); return (pjsip_hdr*)hdr; } @@ -1673,8 +1673,8 @@ static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, /* Parse Max-Forwards header. */ static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx ) { - pjsip_max_forwards_hdr *hdr; - hdr = pjsip_max_forwards_hdr_create(ctx->pool); + pjsip_max_fwd_hdr *hdr; + hdr = pjsip_max_fwd_hdr_create(ctx->pool, 0); parse_generic_int_hdr(hdr, ctx->scanner); if (ctx->rdata) @@ -1687,7 +1687,7 @@ static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx ) static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx) { pjsip_min_expires_hdr *hdr; - hdr = pjsip_min_expires_hdr_create(ctx->pool); + hdr = pjsip_min_expires_hdr_create(ctx->pool, 0); parse_generic_int_hdr(hdr, ctx->scanner); return (pjsip_hdr*)hdr; } @@ -1820,7 +1820,7 @@ static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx ) { pjsip_generic_string_hdr *hdr; - hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL); + hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL, NULL); parse_generic_string_hdr(hdr, ctx->scanner); return (pjsip_hdr*)hdr; diff --git a/pjsip/src/pjsip/sip_tel_uri.c b/pjsip/src/pjsip/sip_tel_uri.c index 4563f6dc..73e50dfa 100644 --- a/pjsip/src/pjsip/sip_tel_uri.c +++ b/pjsip/src/pjsip/sip_tel_uri.c @@ -64,9 +64,9 @@ static pj_str_t pjsip_PH_CTX_STR = { "phone-context", 13 }; static const pj_str_t *tel_uri_get_scheme( const pjsip_tel_uri* ); static void *tel_uri_get_uri( pjsip_tel_uri* ); -static int tel_uri_print( pjsip_uri_context_e context, - const pjsip_tel_uri *url, - char *buf, pj_size_t size); +static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, + const pjsip_tel_uri *url, + char *buf, pj_size_t size); static int tel_uri_cmp( pjsip_uri_context_e context, const pjsip_tel_uri *url1, const pjsip_tel_uri *url2); static pjsip_tel_uri* tel_uri_clone(pj_pool_t *pool, const pjsip_tel_uri *rhs); @@ -164,9 +164,9 @@ pj_status_t pjsip_tel_uri_subsys_init(void) } /* Print tel: URI */ -static int tel_uri_print( pjsip_uri_context_e context, - const pjsip_tel_uri *uri, - char *buf, pj_size_t size) +static pj_ssize_t tel_uri_print( pjsip_uri_context_e context, + const pjsip_tel_uri *uri, + char *buf, pj_size_t size) { int printed; char *startbuf = buf; diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index 61632ed7..39bc8d20 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -61,8 +61,6 @@ static struct mod_tsx_layer -1, /* Module ID */ PJSIP_MOD_PRIORITY_TSX_LAYER, /* Priority. */ NULL, /* User_data. */ - 0, /* Methods count. */ - { NULL }, /* Array of methods. */ mod_tsx_layer_load, /* load(). */ mod_tsx_layer_start, /* start() */ mod_tsx_layer_stop, /* stop() */ @@ -507,16 +505,19 @@ static pj_status_t mod_tsx_layer_register_tsx( pjsip_transaction *tsx) pj_mutex_lock(mod_tsx_layer.mutex); /* Check if no transaction with the same key exists. */ - if (pj_hash_get( mod_tsx_layer.htable, &tsx->transaction_key.ptr, - tsx->transaction_key.slen) != NULL) - { - pj_mutex_unlock(mod_tsx_layer.mutex); - return PJ_EEXISTS; - } + PJ_ASSERT_ON_FAIL(pj_hash_get( mod_tsx_layer.htable, + &tsx->transaction_key.ptr, + tsx->transaction_key.slen, + &tsx->hashed_key) == NULL, + { + pj_mutex_unlock(mod_tsx_layer.mutex); + return PJ_EEXISTS; + } + ); /* Register the transaction to the hash table. */ pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr, - tsx->transaction_key.slen, tsx); + tsx->transaction_key.slen, tsx->hashed_key, tsx); /* Unlock mutex. */ pj_mutex_unlock(mod_tsx_layer.mutex); @@ -538,7 +539,7 @@ static void mod_tsx_layer_unregister_tsx( pjsip_transaction *tsx) /* Register the transaction to the hash table. */ pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, - tsx->transaction_key.slen, NULL); + tsx->transaction_key.slen, tsx->hashed_key, NULL); /* Unlock mutex. */ pj_mutex_unlock(mod_tsx_layer.mutex); @@ -554,7 +555,7 @@ PJ_DEF(pjsip_transaction*) pjsip_tsx_layer_find_tsx( const pj_str_t *key, pjsip_transaction *tsx; pj_mutex_lock(mod_tsx_layer.mutex); - tsx = pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen ); + tsx = pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen, NULL ); pj_mutex_unlock(mod_tsx_layer.mutex); @@ -648,7 +649,7 @@ static pj_bool_t mod_tsx_layer_on_rx_request(pjsip_rx_data *rdata) /* Find transaction. */ pj_mutex_lock( mod_tsx_layer.mutex ); - tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen ); + tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, NULL ); if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { /* Transaction not found. @@ -689,7 +690,7 @@ static pj_bool_t mod_tsx_layer_on_rx_response(pjsip_rx_data *rdata) /* Find transaction. */ pj_mutex_lock( mod_tsx_layer.mutex ); - tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen ); + tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, NULL ); if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { /* Transaction not found. @@ -1035,6 +1036,10 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uac( pjsip_module *tsx_user, PJSIP_ROLE_UAC, &tsx->method, &via->branch_param); + /* Calculate hashed key value. */ + tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, + tsx->transaction_key.slen); + PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, tsx->transaction_key.ptr)); @@ -1140,6 +1145,10 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, return status; } + /* Calculate hashed key value. */ + tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, + tsx->transaction_key.slen); + /* Duplicate branch parameter for transaction. */ branch = &rdata->msg_info.via->branch_param; pj_strdup(tsx->pool, &tsx->branch, branch); @@ -1179,6 +1188,9 @@ PJ_DEF(pj_status_t) pjsip_tsx_create_uas( pjsip_module *tsx_user, return status; } + /* Put this transaction in rdata's mod_data. */ + rdata->endpt_info.mod_data[mod_tsx_layer.mod.id] = tsx; + /* Unlock transaction and return. */ unlock_tsx(tsx, &lck); diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 46d98d99..88531876 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -43,22 +43,20 @@ static pj_status_t mod_on_tx_msg(pjsip_tx_data *tdata); */ static pjsip_module mod_msg_print = { - NULL, NULL, /* prev and next */ - { "mod-msg-print", 13}, /* Name. */ - -1, /* Id */ - PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ - NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ - NULL, /* load() */ - NULL, /* start() */ - NULL, /* stop() */ - NULL, /* unload() */ - NULL, /* on_rx_request() */ - NULL, /* on_rx_response() */ - &mod_on_tx_msg, /* on_tx_request() */ - &mod_on_tx_msg, /* on_tx_response() */ - NULL, /* on_tsx_state() */ + NULL, NULL, /* prev and next */ + { "mod-msg-print", 13}, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_TRANSPORT_LAYER, /* Priority */ + NULL, /* User data. */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + NULL, /* on_rx_request() */ + NULL, /* on_rx_response() */ + &mod_on_tx_msg, /* on_tx_request() */ + &mod_on_tx_msg, /* on_tx_response() */ + NULL, /* on_tsx_state() */ }; /* @@ -348,7 +346,7 @@ PJ_DEF(char*) pjsip_tx_data_get_info( pjsip_tx_data *tdata ) { if (tdata==NULL || tdata->msg==NULL) - return "INVALID MSG"; + return "NULL"; if (tdata->info) return tdata->info; @@ -589,7 +587,7 @@ PJ_DEF(pj_status_t) pjsip_transport_register( pjsip_tpmgr *mgr, */ key_len = sizeof(tp->key.type) + tp->addr_len; pj_lock_acquire(mgr->lock); - pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, tp); + pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, 0, tp); pj_lock_release(mgr->lock); return PJ_SUCCESS; @@ -617,7 +615,7 @@ static pj_status_t destroy_transport( pjsip_tpmgr *mgr, * Unregister from hash table. */ key_len = sizeof(tp->key.type) + tp->addr_len; - pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, NULL); + pj_hash_set(tp->pool, mgr->table, &tp->key, key_len, 0, NULL); pj_lock_release(mgr->lock); @@ -878,7 +876,8 @@ PJ_DEF(pj_ssize_t) pjsip_tpmgr_receive_packet( pjsip_tpmgr *mgr, } /* Perform basic header checking. */ - if (rdata->msg_info.cid->id.ptr == NULL || + if (rdata->msg_info.cid == NULL || + rdata->msg_info.cid->id.slen == 0 || rdata->msg_info.from == NULL || rdata->msg_info.to == NULL || rdata->msg_info.via == NULL || @@ -961,7 +960,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, key.type = type; pj_memcpy(&key.addr, remote, addr_len); - transport = pj_hash_get(mgr->table, &key, key_len); + transport = pj_hash_get(mgr->table, &key, key_len, NULL); if (transport == NULL) { unsigned flag = pjsip_transport_get_flag_from_type(type); const pj_sockaddr *remote_addr = (const pj_sockaddr*)remote; @@ -974,7 +973,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, pj_memset(addr, 0, sizeof(pj_sockaddr_in)); key_len = sizeof(key.type) + sizeof(pj_sockaddr_in); - transport = pj_hash_get(mgr->table, &key, key_len); + transport = pj_hash_get(mgr->table, &key, key_len, NULL); } /* For datagram INET transports, try lookup with zero address. */ @@ -987,7 +986,7 @@ PJ_DEF(pj_status_t) pjsip_tpmgr_acquire_transport(pjsip_tpmgr *mgr, addr->sin_family = PJ_AF_INET; key_len = sizeof(key.type) + sizeof(pj_sockaddr_in); - transport = pj_hash_get(mgr->table, &key, key_len); + transport = pj_hash_get(mgr->table, &key, key_len, NULL); } } diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c new file mode 100644 index 00000000..90885847 --- /dev/null +++ b/pjsip/src/pjsip/sip_ua_layer.c @@ -0,0 +1,674 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define THIS_FILE "sip_ua_layer.c" + +/* + * Static prototypes. + */ +static pj_status_t mod_ua_load(pjsip_endpoint *endpt); +static pj_status_t mod_ua_unload(void); +static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata); +static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata); +static void mod_ua_on_tsx_state(pjsip_transaction*, pjsip_event*); + + +extern long pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */ + +/* This struct is used to represent list of dialog inside a dialog set. + * We don't want to use pjsip_dialog for this purpose, to save some + * memory (about 100 bytes per dialog set). + */ +struct dlg_set_head +{ + PJ_DECL_LIST_MEMBER(pjsip_dialog); +}; + +/* This struct represents a dialog set. + * This is the value that will be put in the UA's hash table. + */ +struct dlg_set +{ + /* To put this node in free dlg_set nodes in UA. */ + PJ_DECL_LIST_MEMBER(struct dlg_set); + + /* This is the buffer to store this entry in the hash table. */ + char ht_entry[PJ_HASH_ENTRY_SIZE]; + + /* List of dialog in this dialog set. */ + struct dlg_set_head dlg_list; +}; + + +/* + * Module interface. + */ +static struct user_agent +{ + pjsip_module mod; + pj_pool_t *pool; + pjsip_endpoint *endpt; + pj_mutex_t *mutex; + pj_hash_table_t *dlg_table; + pjsip_ua_init_param param; + struct dlg_set free_dlgset_nodes; + +} mod_ua = +{ + { + NULL, NULL, /* prev, next. */ + { "mod-ua", 6 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_UA_PROXY_LAYER, /* Priority */ + NULL, /* User data. */ + &mod_ua_load, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + &mod_ua_unload, /* unload() */ + &mod_ua_on_rx_request, /* on_rx_request() */ + &mod_ua_on_rx_response, /* on_rx_response() */ + NULL, /* on_tx_request. */ + NULL, /* on_tx_response() */ + &mod_ua_on_tsx_state, /* on_tsx_state() */ + } +}; + +/* + * mod_ua_load() + * + * Called when module is being loaded by endpoint. + */ +static pj_status_t mod_ua_load(pjsip_endpoint *endpt) +{ + pj_status_t status; + + /* Initialize the user agent. */ + mod_ua.endpt = endpt; + mod_ua.pool = pjsip_endpt_create_pool( endpt, "ua%p", PJSIP_POOL_LEN_UA, + PJSIP_POOL_INC_UA); + if (mod_ua.pool == NULL) + return PJ_ENOMEM; + + status = pj_mutex_create_recursive(mod_ua.pool, " ua%p", &mod_ua.mutex); + if (status != PJ_SUCCESS) + return status; + + mod_ua.dlg_table = pj_hash_create(mod_ua.pool, PJSIP_MAX_DIALOG_COUNT); + if (mod_ua.dlg_table == NULL) + return PJ_ENOMEM; + + pj_list_init(&mod_ua.free_dlgset_nodes); + + /* Initialize dialog lock. */ + status = pj_thread_local_alloc(&pjsip_dlg_lock_tls_id); + if (status != PJ_SUCCESS) + return status; + + pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL); + + return PJ_SUCCESS; + +} + +/* + * mod_ua_unload() + * + * Called when module is being unloaded. + */ +static pj_status_t mod_ua_unload(void) +{ + pj_thread_local_free(pjsip_dlg_lock_tls_id); + pj_mutex_destroy(mod_ua.mutex); + + /* Release pool */ + if (mod_ua.pool) { + pjsip_endpt_release_pool( mod_ua.endpt, mod_ua.pool ); + } + return PJ_SUCCESS; +} + +/* + * mod_ua_on_tsx_stats() + * + * Called on changed on transaction state. + */ +static void mod_ua_on_tsx_state( pjsip_transaction *tsx, pjsip_event *e) +{ + pjsip_dialog *dlg; + + /* Get the dialog where this transaction belongs. */ + dlg = tsx->mod_data[mod_ua.mod.id]; + + /* Must have the dialog instance! */ + PJ_ASSERT_ON_FAIL(dlg != NULL, return); + + /* Hand over the event to the dialog. */ + pjsip_dlg_on_tsx_state(dlg, tsx, e); +} + + +/* + * Init user agent module and register it to the endpoint. + */ +PJ_DEF(pj_status_t) pjsip_ua_init( pjsip_endpoint *endpt, + const pjsip_ua_init_param *prm) +{ + pj_status_t status; + + /* Check if module already registered. */ + PJ_ASSERT_RETURN(mod_ua.mod.id == -1, PJ_EINVALIDOP); + + /* Copy param, if exists. */ + if (prm) + pj_memcpy(&mod_ua.param, prm, sizeof(pjsip_ua_init_param)); + + /* Register the module. */ + status = pjsip_endpt_register_module(endpt, &mod_ua.mod); + + return status; +} + +/* + * Get the instance of the user agent. + * + */ +PJ_DEF(pjsip_user_agent*) pjsip_ua_instance(void) +{ + return &mod_ua.mod; +} + + +/* + * Destroy the user agent layer. + */ +PJ_DEF(pj_status_t) pjsip_ua_destroy(void) +{ + /* Check if module already destroyed. */ + PJ_ASSERT_RETURN(mod_ua.mod.id != -1, PJ_EINVALIDOP); + + return pjsip_endpt_unregister_module(mod_ua.endpt, &mod_ua.mod); +} + + + +/* + * Create key to identify dialog set. + */ +PJ_DEF(void) pjsip_ua_create_dlg_set_key( pj_pool_t *pool, + pj_str_t *set_key, + const pj_str_t *call_id, + const pj_str_t *local_tag) +{ + PJ_ASSERT_ON_FAIL(pool && set_key && call_id && local_tag, return;); + + set_key->slen = call_id->slen + local_tag->slen + 1; + set_key->ptr = pj_pool_alloc(pool, set_key->slen); + pj_assert(set_key->ptr != NULL); + + pj_memcpy(set_key->ptr, call_id->ptr, call_id->slen); + set_key->ptr[call_id->slen] = '$'; + pj_memcpy(set_key->ptr + call_id->slen + 1, + local_tag->ptr, local_tag->slen); +} + +/* + * Acquire one dlg_set node to be put in the hash table. + * This will first look in the free nodes list, then allocate + * a new one from UA's pool when one is not available. + */ +static struct dlg_set *alloc_dlgset_node(void) +{ + struct dlg_set *set; + + if (!pj_list_empty(&mod_ua.free_dlgset_nodes)) { + set = mod_ua.free_dlgset_nodes.next; + pj_list_erase(set); + return set; + } else { + set = pj_pool_alloc(mod_ua.pool, sizeof(struct dlg_set)); + return set; + } +} + +/* + * Register new dialog. Called by pjsip_dlg_create_uac() and + * pjsip_dlg_create_uas(); + */ +PJ_DEF(pj_status_t) pjsip_ua_register_dlg( pjsip_user_agent *ua, + pjsip_dialog *dlg ) +{ + /* Sanity check. */ + PJ_ASSERT_RETURN(ua && dlg, PJ_EINVAL); + + /* For all dialogs, local tag (inc hash) must has been initialized. */ + PJ_ASSERT_RETURN(dlg->local.info && dlg->local.info->tag.slen && + dlg->local.tag_hval != 0, PJ_EBUG); + + /* For UAS dialog, remote tag (inc hash) must have been initialized. */ + PJ_ASSERT_RETURN(dlg->role==PJSIP_ROLE_UAC || + (dlg->role==PJSIP_ROLE_UAS && dlg->remote.info->tag.slen + && dlg->remote.tag_hval != 0), PJ_EBUG); + + /* Lock the user agent. */ + pj_mutex_lock(mod_ua.mutex); + + /* For UAC, check if there is existing dialog in the same set. */ + if (dlg->role == PJSIP_ROLE_UAC) { + struct dlg_set *dlg_set; + + dlg_set = pj_hash_get( mod_ua.dlg_table, dlg->local.info->tag.ptr, + dlg->local.info->tag.slen, + &dlg->local.tag_hval); + + if (dlg_set) { + /* This is NOT the first dialog in the dialog set. + * Just add this dialog in the list. + */ + pj_assert(dlg_set->dlg_list.next != (void*)&dlg_set->dlg_list); + pj_list_push_back(&dlg_set->dlg_list, dlg); + + dlg->dlg_set = dlg_set; + + } else { + /* This is the first dialog in the dialog set. + * Create the dialog set and add this dialog to it. + */ + dlg_set = alloc_dlgset_node(); + pj_list_init(&dlg_set->dlg_list); + pj_list_push_back(&dlg_set->dlg_list, dlg); + + dlg->dlg_set = dlg_set; + + /* Register the dialog set in the hash table. */ + pj_hash_set_np(mod_ua.dlg_table, + dlg->local.info->tag.ptr, dlg->local.info->tag.slen, + dlg->local.tag_hval, dlg_set->ht_entry, dlg_set); + } + + } else { + /* For UAS, create the dialog set with a single dialog as member. */ + struct dlg_set *dlg_set; + + dlg_set = alloc_dlgset_node(); + pj_list_init(&dlg_set->dlg_list); + pj_list_push_back(&dlg_set->dlg_list, dlg); + + dlg->dlg_set = dlg_set; + + pj_hash_set_np(mod_ua.dlg_table, + dlg->local.info->tag.ptr, dlg->local.info->tag.slen, + dlg->local.tag_hval, dlg_set->ht_entry, dlg_set); + } + + /* Unlock user agent. */ + pj_mutex_unlock(mod_ua.mutex); + + /* Done. */ + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjsip_ua_unregister_dlg( pjsip_user_agent *ua, + pjsip_dialog *dlg ) +{ + struct dlg_set *dlg_set; + pjsip_dialog *d; + + /* Sanity-check arguments. */ + PJ_ASSERT_RETURN(ua && dlg, PJ_EINVAL); + + /* Check that dialog has been registered. */ + PJ_ASSERT_RETURN(dlg->dlg_set, PJ_EINVALIDOP); + + /* Lock user agent. */ + pj_mutex_lock(mod_ua.mutex); + + /* Find this dialog from the dialog set. */ + dlg_set = dlg->dlg_set; + d = dlg_set->dlg_list.next; + while (d != (pjsip_dialog*)&dlg_set->dlg_list && d != dlg) { + d = d->next; + } + + if (d != dlg) { + pj_assert(!"Dialog is not registered!"); + pj_mutex_unlock(mod_ua.mutex); + return PJ_EINVALIDOP; + } + + /* Remove this dialog from the list. */ + pj_list_erase(dlg); + + /* If dialog list is empty, remove the dialog set from the hash table. */ + if (pj_list_empty(&dlg_set->dlg_list)) { + pj_hash_set(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr, + dlg->local.info->tag.slen, dlg->local.tag_hval, NULL); + + /* Return dlg_set to free nodes. */ + pj_list_push_back(&mod_ua.free_dlgset_nodes, dlg_set); + } + + /* Unlock user agent. */ + pj_mutex_unlock(mod_ua.mutex); + + /* Done. */ + return PJ_SUCCESS; +} + + +PJ_DEF(pjsip_dialog*) pjsip_rdata_get_dlg( pjsip_rx_data *rdata ) +{ + return rdata->endpt_info.mod_data[mod_ua.mod.id]; +} + +PJ_DEF(pjsip_dialog*) pjsip_tsx_get_dlg( pjsip_transaction *tsx ) +{ + return tsx->mod_data[mod_ua.mod.id]; +} + + +/* + * Find the first dialog in dialog set in hash table for an incoming message. + */ +static struct dlg_set *find_dlg_set_for_msg( pjsip_rx_data *rdata ) +{ + /* CANCEL message doesn't have To tag, so we must lookup the dialog + * by finding the INVITE UAS transaction being cancelled. + */ + if (rdata->msg_info.cseq->method.id == PJSIP_CANCEL_METHOD) { + + pjsip_dialog *dlg; + + /* Create key for the rdata, but this time, use INVITE as the + * method. + */ + pj_str_t key; + pjsip_role_e role; + pjsip_transaction *tsx; + + if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) + role = PJSIP_ROLE_UAS; + else + role = PJSIP_ROLE_UAC; + + pjsip_tsx_create_key(rdata->tp_info.pool, &key, role, + &pjsip_invite_method, rdata); + + /* Lookup the INVITE transaction */ + tsx = pjsip_tsx_layer_find_tsx(&key, PJ_TRUE); + + /* We should find the dialog attached to the INVITE transaction */ + if (tsx) { + dlg = tsx->mod_data[mod_ua.mod.id]; + pj_mutex_unlock(tsx->mutex); + + return dlg->dlg_set; + + } else { + return NULL; + } + + + } else { + pj_str_t *tag; + struct dlg_set *dlg_set; + + if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG) + tag = &rdata->msg_info.to->tag; + else + tag = &rdata->msg_info.from->tag; + + /* Lookup the dialog set. */ + dlg_set = pj_hash_get(mod_ua.dlg_table, tag->ptr, tag->slen, NULL); + return dlg_set; + } +} + +/* On received requests. */ +static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata) +{ + struct dlg_set *dlg_set; + pj_str_t *from_tag; + pjsip_dialog *dlg; + + /* Lock user agent before looking up the dialog hash table. */ + pj_mutex_lock(mod_ua.mutex); + + /* Lookup the dialog set, based on the To tag header. */ + dlg_set = find_dlg_set_for_msg(rdata); + + /* Bail out if dialog is not found. */ + if (dlg_set == NULL) { + /* Not ours. */ + pj_mutex_unlock(mod_ua.mutex); + return PJ_FALSE; + } + + /* Dialog set has been found. + * Find the dialog in the dialog set based on the content of the From tag. + */ + from_tag = &rdata->msg_info.from->tag; + dlg = dlg_set->dlg_list.next; + while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { + + if (pj_strcmp(&dlg->remote.info->tag, from_tag) == 0) + break; + + dlg = dlg->next; + } + + /* Dialog MUST be found! */ + if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { + /* Not found. Mulfunction UAC? */ + pj_mutex_unlock(mod_ua.mutex); + pjsip_endpt_respond_stateless(mod_ua.endpt, rdata, + PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, + NULL, NULL, NULL); + return PJ_TRUE; + } + + /* Mark the dialog id of the request. */ + rdata->endpt_info.mod_data[mod_ua.mod.id] = dlg; + + /* Done processing in the UA */ + pj_mutex_unlock(mod_ua.mutex); + + /* Pass to dialog. */ + pjsip_dlg_on_rx_request(dlg, rdata); + + /* Report as handled. */ + return PJ_TRUE; +} + + +/* On rx response notification. + */ +static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) +{ + pjsip_transaction *tsx; + struct dlg_set *dlg_set; + pjsip_dialog *dlg = NULL; + + /* + * Find the dialog instance for the response. + * All outgoing dialog requests are sent statefully, which means + * there will be an UAC transaction associated with this response, + * and the dialog instance will be recorded in that transaction. + * + * But even when transaction is found, there is possibility that + * the response is a forked response. + */ + + /* Check if transaction is present. */ + tsx = pjsip_rdata_get_tsx(rdata); + if (!tsx) { + /* Check if dialog is present in the transaction. */ + dlg = pjsip_tsx_get_dlg(tsx); + if (!dlg) + return PJ_FALSE; + + /* Get the dialog set. */ + dlg_set = dlg->dlg_set; + + /* Even if transaction is found and (candidate) dialog has been + * identified, it's possible that the request has forked. + */ + + } else { + /* Transaction is not present. + * Check if this is a 2xx/OK response to INVITE, which in this + * case the response will be handled directly by the + * dialog. + */ + pjsip_cseq_hdr *cseq_hdr = rdata->msg_info.cseq; + + if (cseq_hdr->method.id != PJSIP_INVITE_METHOD || + rdata->msg_info.msg->line.status.code / 100 != 2) + { + /* Not a 2xx response to INVITE. + * This must be some stateless response sent by other modules, + * or a very late response. + */ + return PJ_FALSE; + } + + /* Lock user agent before accessing the hash table. */ + pj_mutex_lock(mod_ua.mutex); + + /* Get the dialog set. */ + dlg_set = pj_hash_get(mod_ua.dlg_table, + rdata->msg_info.from->tag.ptr, + rdata->msg_info.from->tag.slen, + NULL); + + /* Done with accessing the hash table. */ + pj_mutex_unlock(mod_ua.mutex); + + if (!dlg_set) { + /* Strayed 2xx response!! */ + PJ_LOG(4,(THIS_FILE, + "Received strayed 2xx response (no dialog is found)" + " from %s:%d: %s", + rdata->pkt_info.src_name, rdata->pkt_info.src_port, + pjsip_rx_data_get_info(rdata))); + + return PJ_TRUE; + } + } + + /* At this point, we must have the dialog set, and the dialog set + * must have a dialog in the list. + */ + pj_assert(dlg_set && !pj_list_empty(&dlg_set->dlg_list)); + + /* Check for forked response. + * Request will fork only for the initial INVITE request. + */ + if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD && + rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq) + { + pj_str_t *to_tag = &rdata->msg_info.to->tag; + + /* Must hold UA mutex before accessing dialog set. */ + pj_mutex_lock(mod_ua.mutex); + + dlg = dlg_set->dlg_list.next; + while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { + + /* If there is dialog with no remote tag (i.e. dialog has not + * been established yet), then send this response to that + * dialog. + */ + if (dlg->remote.info->tag.slen == 0) + break; + + /* Otherwise find the one with matching To tag. */ + if (pj_strcmp(to_tag, &dlg->remote.info->tag) == 0) + break; + + dlg = dlg->next; + } + + /* If no dialog with matching remote tag is found, this must be + * a forked response. + */ + if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { + /* Report to application about forked condition. + * Application can either create a dialog or ignore the response. + */ + if (mod_ua.param.on_dlg_forked) + dlg = (*mod_ua.param.on_dlg_forked)(dlg_set->dlg_list.next, + rdata); + else + dlg = NULL; + + /* Ignore this response if application doesn't want to + * create a dialog. + */ + if (dlg == NULL) { + pj_mutex_unlock(mod_ua.mutex); + + PJ_LOG(4,(THIS_FILE, + "Unhandled forked %s from %s:%d", + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name, rdata->pkt_info.src_port)); + + return PJ_TRUE; + } + } + + /* Done with the dialog set. */ + pj_mutex_unlock(mod_ua.mutex); + + } else { + /* Either this is a non-INVITE response, or subsequent INVITE + * within dialog. The dialog should have been identified when + * the transaction was found. + */ + pj_assert(tsx != NULL); + pj_assert(dlg != NULL); + } + + /* The dialog must have been found. */ + pj_assert(dlg != NULL); + + /* Put the dialog instance in the rdata. */ + rdata->endpt_info.mod_data[mod_ua.mod.id] = dlg; + + /* Pass the response to the dialog. */ + pjsip_dlg_on_rx_response(dlg, rdata); + + /* Done. */ + return PJ_TRUE; +} + + diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c index 17994051..c408d0d9 100644 --- a/pjsip/src/pjsip/sip_uri.c +++ b/pjsip/src/pjsip/sip_uri.c @@ -140,15 +140,15 @@ static pj_str_t sips_str = { "sips", 4 }; static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, const pjsip_name_addr *rhs); -static int pjsip_name_addr_print( pjsip_uri_context_e context, - const pjsip_name_addr *name, - char *buf, pj_size_t size); +static pj_ssize_t pjsip_name_addr_print(pjsip_uri_context_e context, + const pjsip_name_addr *name, + char *buf, pj_size_t size); static int pjsip_name_addr_compare( pjsip_uri_context_e context, const pjsip_name_addr *naddr1, const pjsip_name_addr *naddr2); -static int pjsip_url_print( pjsip_uri_context_e context, - const pjsip_sip_uri *url, - char *buf, pj_size_t size); +static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, + const pjsip_sip_uri *url, + char *buf, pj_size_t size); static int pjsip_url_compare( pjsip_uri_context_e context, const pjsip_sip_uri *url1, const pjsip_sip_uri *url2); @@ -204,7 +204,7 @@ static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ) return name->uri; } -PJ_DEF(void) pjsip_url_init(pjsip_sip_uri *url, int secure) +PJ_DEF(void) pjsip_sip_uri_init(pjsip_sip_uri *url, int secure) { pj_memset(url, 0, sizeof(*url)); url->ttl_param = -1; @@ -213,16 +213,16 @@ PJ_DEF(void) pjsip_url_init(pjsip_sip_uri *url, int secure) pj_list_init(&url->header_param); } -PJ_DEF(pjsip_sip_uri*) pjsip_url_create( pj_pool_t *pool, int secure ) +PJ_DEF(pjsip_sip_uri*) pjsip_sip_uri_create( pj_pool_t *pool, int secure ) { pjsip_sip_uri *url = pj_pool_alloc(pool, sizeof(pjsip_sip_uri)); - pjsip_url_init(url, secure); + pjsip_sip_uri_init(url, secure); return url; } -static int pjsip_url_print( pjsip_uri_context_e context, - const pjsip_sip_uri *url, - char *buf, pj_size_t size) +static pj_ssize_t pjsip_url_print( pjsip_uri_context_e context, + const pjsip_sip_uri *url, + char *buf, pj_size_t size) { int printed; char *startbuf = buf; @@ -465,8 +465,8 @@ static pj_status_t pjsip_url_compare( pjsip_uri_context_e context, } -PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_sip_uri *url, - const pjsip_sip_uri *rhs) +PJ_DEF(void) pjsip_sip_uri_assign(pj_pool_t *pool, pjsip_sip_uri *url, + const pjsip_sip_uri *rhs) { pj_strdup( pool, &url->user, &rhs->user); pj_strdup( pool, &url->passwd, &rhs->passwd); @@ -488,8 +488,8 @@ static pjsip_sip_uri* pjsip_url_clone(pj_pool_t *pool, const pjsip_sip_uri *rhs) if (!url) return NULL; - pjsip_url_init(url, IS_SIPS(rhs)); - pjsip_url_assign(pool, url, rhs); + pjsip_sip_uri_init(url, IS_SIPS(rhs)); + pjsip_sip_uri_assign(pool, url, rhs); return url; } @@ -513,9 +513,9 @@ PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool) return name_addr; } -static int pjsip_name_addr_print( pjsip_uri_context_e context, - const pjsip_name_addr *name, - char *buf, pj_size_t size) +static pj_ssize_t pjsip_name_addr_print(pjsip_uri_context_e context, + const pjsip_name_addr *name, + char *buf, pj_size_t size) { int printed; char *startbuf = buf; diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index f69ad9b3..af02e994 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -128,9 +128,9 @@ static void init_request_throw( pjsip_endpoint *endpt, while (hparam != &uri->header_param) { pjsip_generic_string_hdr *hdr; - hdr = pjsip_generic_string_hdr_create_with_text(tdata->pool, - &hparam->name, - &hparam->value); + hdr = pjsip_generic_string_hdr_create(tdata->pool, + &hparam->name, + &hparam->value); pjsip_msg_add_hdr(msg, (pjsip_hdr*)hdr); hparam = hparam->next; } @@ -302,9 +302,9 @@ pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, /* Duplicate target URI and headers. */ target = pjsip_uri_clone(tdata->pool, param_target); from = pjsip_hdr_clone(tdata->pool, param_from); - pjsip_fromto_set_from(from); + pjsip_fromto_hdr_set_from(from); to = pjsip_hdr_clone(tdata->pool, param_to); - pjsip_fromto_set_to(to); + pjsip_fromto_hdr_set_to(to); if (param_contact) contact = pjsip_hdr_clone(tdata->pool, param_contact); else diff --git a/pjsip/src/test-pjsip/dlg_core_test.c b/pjsip/src/test-pjsip/dlg_core_test.c new file mode 100644 index 00000000..348e3c3a --- /dev/null +++ b/pjsip/src/test-pjsip/dlg_core_test.c @@ -0,0 +1,22 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2006 Benny Prijono + * + * 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 "test.h" +#include + diff --git a/pjsip/src/test-pjsip/msg_logger.c b/pjsip/src/test-pjsip/msg_logger.c index faa36b53..45a4cbaf 100644 --- a/pjsip/src/test-pjsip/msg_logger.c +++ b/pjsip/src/test-pjsip/msg_logger.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" -#include +#include #include #define THIS_FILE "msg_logger.c" @@ -64,8 +64,6 @@ static pjsip_module mod_msg_logger = -1, /* Id */ PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ diff --git a/pjsip/src/test-pjsip/msg_test.c b/pjsip/src/test-pjsip/msg_test.c index 8fe879a3..f5debe84 100644 --- a/pjsip/src/test-pjsip/msg_test.c +++ b/pjsip/src/test-pjsip/msg_test.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" -#include +#include #include #define POOL_SIZE 8000 @@ -348,7 +348,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* "INVITE sip:user@foo SIP/2.0\n" */ pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD); - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); msg->line.req.uri = (pjsip_uri*)url; pj_strdup2(pool, &url->user, "user"); pj_strdup2(pool, &url->host, "foo"); @@ -360,7 +360,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) name_addr = pjsip_name_addr_create(pool); fromto->uri = (pjsip_uri*)name_addr; pj_strdup2(pool, &name_addr->display, "Hi I'm Joe"); - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*)url; pj_strdup2(pool, &url->user, "joe.user"); pj_strdup2(pool, &url->host, "bar.otherdomain.com"); @@ -371,7 +371,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) name_addr = pjsip_name_addr_create(pool); fromto->uri = (pjsip_uri*)name_addr; pj_strdup2(pool, &name_addr->display, "Fellow User"); - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*)url; pj_strdup2(pool, &url->user, "user"); pj_strdup2(pool, &url->host, "foo.bar.domain.com"); @@ -399,7 +399,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) contact->expires = 3600; name_addr = pjsip_name_addr_create(pool); contact->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*)url; pj_strdup2(pool, &url->user, "joe"); pj_strdup2(pool, &url->host, "bar"); @@ -410,7 +410,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) contact->q1000 = 500; name_addr = pjsip_name_addr_create(pool); contact->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*)url; pj_strdup2(pool, &url->user, "user"); pj_strdup2(pool, &url->host, "host"); @@ -420,7 +420,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); name_addr = pjsip_name_addr_create(pool); contact->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*)url; pj_strdup2(pool, &url->user, "user2"); pj_strdup2(pool, &url->host, "host2"); @@ -435,7 +435,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* "Route: ,\r\n" */ routing = pjsip_route_hdr_create(pool); pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); routing->name_addr.uri = (pjsip_uri*)url; pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); url->lr_param = 1; @@ -443,7 +443,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* " \r" */ routing = pjsip_route_hdr_create(pool); pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); routing->name_addr.uri = (pjsip_uri*)url; pj_strdup2(pool, &url->host, "server10.biloxi.com"); url->lr_param = 1; @@ -451,7 +451,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* "Record-Route: ,\r\n" */ routing = pjsip_rr_hdr_create(pool); pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); routing->name_addr.uri = (pjsip_uri*)url; pj_strdup2(pool, &url->host, "server10.biloxi.com"); url->lr_param = 0; @@ -459,7 +459,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* " \n" */ routing = pjsip_rr_hdr_create(pool); pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); routing->name_addr.uri = (pjsip_uri*)url; pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); url->lr_param = 1; @@ -497,7 +497,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* "Organization: \r" */ str.ptr = "Organization"; str.slen = 12; - generic = pjsip_generic_string_hdr_create(pool, &str); + generic = pjsip_generic_string_hdr_create(pool, &str, NULL); pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); generic->hvalue.ptr = NULL; generic->hvalue.slen = 0; @@ -505,7 +505,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* "Max-Forwards: 70\n" */ str.ptr = "Max-Forwards"; str.slen = 12; - generic = pjsip_generic_string_hdr_create(pool, &str); + generic = pjsip_generic_string_hdr_create(pool, &str, NULL); pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); str.ptr = "70"; str.slen = 2; @@ -514,7 +514,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* "X-Header: \r\n" */ str.ptr = "X-Header"; str.slen = 8; - generic = pjsip_generic_string_hdr_create(pool, &str); + generic = pjsip_generic_string_hdr_create(pool, &str, NULL); pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); str.ptr = NULL; str.slen = 0; @@ -523,7 +523,7 @@ static pjsip_msg *create_msg0(pj_pool_t *pool) /* P-Associated-URI:\r\n */ str.ptr = "P-Associated-URI"; str.slen = 16; - generic = pjsip_generic_string_hdr_create(pool, &str); + generic = pjsip_generic_string_hdr_create(pool, &str, NULL); pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); str.ptr = NULL; str.slen = 0; @@ -538,7 +538,7 @@ static pjsip_msg *create_msg1(pj_pool_t *pool) pjsip_route_hdr *route; pjsip_name_addr *name_addr; pjsip_sip_uri *url; - pjsip_max_forwards_hdr *max_fwd; + pjsip_max_fwd_hdr *max_fwd; pjsip_to_hdr *to; pjsip_from_hdr *from; pjsip_contact_hdr *contact; @@ -581,22 +581,21 @@ static pjsip_msg *create_msg1(pj_pool_t *pool) //"Route: \r\n" route = pjsip_route_hdr_create(pool); pjsip_msg_add_hdr(msg, (pjsip_hdr*)route); - url = pjsip_url_create(pool, PJ_FALSE); + url = pjsip_sip_uri_create(pool, PJ_FALSE); route->name_addr.uri = (pjsip_uri*)url; url->host = pj_str("proxy.sipprovider.com"); //"Route: \r\n" route = pjsip_route_hdr_create(pool); pjsip_msg_add_hdr(msg, (pjsip_hdr*)route); - url = pjsip_url_create(pool, PJ_FALSE); + url = pjsip_sip_uri_create(pool, PJ_FALSE); route->name_addr.uri = (pjsip_uri*)url; url->host = pj_str("proxy.supersip.com"); url->port = 5060; //"Max-Forwards: 70\r\n" - max_fwd = pjsip_max_forwards_hdr_create(pool); + max_fwd = pjsip_max_fwd_hdr_create(pool, 70); pjsip_msg_add_hdr(msg, (pjsip_hdr*)max_fwd); - max_fwd->ivalue = 70; //"To: Bob ;tag=a6c85cf\r\n" to = pjsip_to_hdr_create(pool); @@ -604,7 +603,7 @@ static pjsip_msg *create_msg1(pj_pool_t *pool) name_addr = pjsip_name_addr_create(pool); name_addr->display = pj_str("Bob"); to->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, PJ_FALSE); + url = pjsip_sip_uri_create(pool, PJ_FALSE); name_addr->uri = (pjsip_uri*)url; url->user = pj_str("bob"); url->host = pj_str("biloxi.com"); @@ -616,7 +615,7 @@ static pjsip_msg *create_msg1(pj_pool_t *pool) name_addr = pjsip_name_addr_create(pool); name_addr->display = pj_str("Alice"); from->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, PJ_FALSE); + url = pjsip_sip_uri_create(pool, PJ_FALSE); name_addr->uri = (pjsip_uri*)url; url->user = pj_str("alice"); url->host = pj_str("atlanta.com"); @@ -638,7 +637,7 @@ static pjsip_msg *create_msg1(pj_pool_t *pool) pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); name_addr = pjsip_name_addr_create(pool); contact->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, PJ_TRUE); + url = pjsip_sip_uri_create(pool, PJ_TRUE); name_addr->uri = (pjsip_uri*)url; url->user = pj_str("bob"); url->host = pj_str("192.0.2.4"); diff --git a/pjsip/src/test-pjsip/test.c b/pjsip/src/test-pjsip/test.c index d634cc80..dd3453f8 100644 --- a/pjsip/src/test-pjsip/test.c +++ b/pjsip/src/test-pjsip/test.c @@ -20,7 +20,7 @@ #include "test.h" #include -#include +#include #define THIS_FILE "test.c" diff --git a/pjsip/src/test-pjsip/transport_loop_test.c b/pjsip/src/test-pjsip/transport_loop_test.c index f4e6bb3c..351fb7b6 100644 --- a/pjsip/src/test-pjsip/transport_loop_test.c +++ b/pjsip/src/test-pjsip/transport_loop_test.c @@ -18,7 +18,7 @@ */ #include "test.h" -#include +#include #include #define THIS_FILE "transport_loop_test.c" diff --git a/pjsip/src/test-pjsip/transport_test.c b/pjsip/src/test-pjsip/transport_test.c index 74f6d796..df6f55eb 100644 --- a/pjsip/src/test-pjsip/transport_test.c +++ b/pjsip/src/test-pjsip/transport_test.c @@ -18,7 +18,7 @@ */ #include "test.h" -#include +#include #include #define THIS_FILE "transport_test.c" @@ -101,8 +101,6 @@ static pjsip_module my_module = -1, /* Id */ PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */ NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -305,8 +303,6 @@ static pjsip_module rt_module = -1, /* Id */ PJSIP_MOD_PRIORITY_TSX_LAYER-1, /* Priority */ NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ diff --git a/pjsip/src/test-pjsip/transport_udp_test.c b/pjsip/src/test-pjsip/transport_udp_test.c index ebc0557d..cf5f0ca5 100644 --- a/pjsip/src/test-pjsip/transport_udp_test.c +++ b/pjsip/src/test-pjsip/transport_udp_test.c @@ -18,7 +18,7 @@ */ #include "test.h" -#include +#include #include #define THIS_FILE "transport_udp_test.c" diff --git a/pjsip/src/test-pjsip/tsx_basic_test.c b/pjsip/src/test-pjsip/tsx_basic_test.c index 1db86f8d..6be0ea2a 100644 --- a/pjsip/src/test-pjsip/tsx_basic_test.c +++ b/pjsip/src/test-pjsip/tsx_basic_test.c @@ -18,7 +18,7 @@ */ #include "test.h" -#include +#include #include #define THIS_FILE "tsx_basic_test.c" diff --git a/pjsip/src/test-pjsip/tsx_uac_test.c b/pjsip/src/test-pjsip/tsx_uac_test.c index 2ec94e5a..c699bc54 100644 --- a/pjsip/src/test-pjsip/tsx_uac_test.c +++ b/pjsip/src/test-pjsip/tsx_uac_test.c @@ -18,7 +18,7 @@ */ #include "test.h" -#include +#include #include #define THIS_FILE "tsx_uac_test.c" @@ -103,8 +103,6 @@ static pjsip_module tsx_user = -1, /* Id */ PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -124,8 +122,6 @@ static pjsip_module msg_receiver = -1, /* Id */ PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ diff --git a/pjsip/src/test-pjsip/tsx_uas_test.c b/pjsip/src/test-pjsip/tsx_uas_test.c index ecc174c4..ed8f8574 100644 --- a/pjsip/src/test-pjsip/tsx_uas_test.c +++ b/pjsip/src/test-pjsip/tsx_uas_test.c @@ -18,7 +18,7 @@ */ #include "test.h" -#include +#include #include #define THIS_FILE "tsx_uas_test.c" @@ -142,8 +142,6 @@ static pjsip_module tsx_user = -1, /* Id */ PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ @@ -163,8 +161,6 @@ static pjsip_module msg_sender = -1, /* Id */ PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ diff --git a/pjsip/src/test-pjsip/txdata_test.c b/pjsip/src/test-pjsip/txdata_test.c index 327ea084..d39fad42 100644 --- a/pjsip/src/test-pjsip/txdata_test.c +++ b/pjsip/src/test-pjsip/txdata_test.c @@ -18,7 +18,7 @@ */ #include "test.h" -#include +#include #include #define HFIND(msg,h,H) ((pjsip_##h##_hdr*) pjsip_msg_find_hdr(msg, PJSIP_H_##H, NULL)) diff --git a/pjsip/src/test-pjsip/uri_test.c b/pjsip/src/test-pjsip/uri_test.c index 28bdedd9..a221f4ca 100644 --- a/pjsip/src/test-pjsip/uri_test.c +++ b/pjsip/src/test-pjsip/uri_test.c @@ -17,7 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "test.h" -#include +#include #include #define THIS_FILE "uri_test.c" @@ -317,7 +317,7 @@ struct uri_test static pjsip_uri *create_uri0(pj_pool_t *pool) { /* "sip:localhost" */ - pjsip_sip_uri *url = pjsip_url_create(pool, 0); + pjsip_sip_uri *url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->host, "localhost"); return (pjsip_uri*)url; @@ -326,7 +326,7 @@ static pjsip_uri *create_uri0(pj_pool_t *pool) static pjsip_uri *create_uri1(pj_pool_t *pool) { /* "sip:user@localhost" */ - pjsip_sip_uri *url = pjsip_url_create(pool, 0); + pjsip_sip_uri *url = pjsip_sip_uri_create(pool, 0); pj_strdup2( pool, &url->user, "user"); pj_strdup2( pool, &url->host, "localhost"); @@ -337,7 +337,7 @@ static pjsip_uri *create_uri1(pj_pool_t *pool) static pjsip_uri *create_uri2(pj_pool_t *pool) { /* "sip:user:password@localhost:5060" */ - pjsip_sip_uri *url = pjsip_url_create(pool, 0); + pjsip_sip_uri *url = pjsip_sip_uri_create(pool, 0); pj_strdup2( pool, &url->user, "user"); pj_strdup2( pool, &url->passwd, "password"); @@ -350,7 +350,7 @@ static pjsip_uri *create_uri2(pj_pool_t *pool) static pjsip_uri *create_uri3(pj_pool_t *pool) { /* Like: "sip:localhost:5060", but without the port. */ - pjsip_sip_uri *url = pjsip_url_create(pool, 0); + pjsip_sip_uri *url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->host, "localhost"); return (pjsip_uri*)url; @@ -359,7 +359,7 @@ static pjsip_uri *create_uri3(pj_pool_t *pool) static pjsip_uri *create_uri4(pj_pool_t *pool) { /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */ - pjsip_sip_uri *url = pjsip_url_create(pool, 0); + pjsip_sip_uri *url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->host, "localhost"); pj_strdup2(pool, &url->transport_param, "tcp"); @@ -386,7 +386,7 @@ static pjsip_uri *create_uri5(pj_pool_t *pool) /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" "?Subject=Hello%20There&Server=SIP%20Server" */ - pjsip_sip_uri *url = pjsip_url_create(pool, 0); + pjsip_sip_uri *url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->host, "localhost"); pj_strdup2(pool, &url->user_param, "phone"); @@ -405,7 +405,7 @@ static pjsip_uri *create_uri5(pj_pool_t *pool) static pjsip_uri *create_uri6(pj_pool_t *pool) { /* "sips:localhost" */ - pjsip_sip_uri *url = pjsip_url_create(pool, 1); + pjsip_sip_uri *url = pjsip_sip_uri_create(pool, 1); pj_strdup2(pool, &url->host, "localhost"); return (pjsip_uri*)url; @@ -417,7 +417,7 @@ static pjsip_uri *create_uri7(pj_pool_t *pool) pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*) url; pj_strdup2(pool, &url->host, "localhost"); @@ -430,7 +430,7 @@ static pjsip_uri *create_uri8(pj_pool_t *pool) pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); pjsip_sip_uri *url; - url = pjsip_url_create(pool, 1); + url = pjsip_sip_uri_create(pool, 1); name_addr->uri = (pjsip_uri*) url; pj_strdup2(pool, &name_addr->display, "Power Administrator"); @@ -444,7 +444,7 @@ static pjsip_uri *create_uri9(pj_pool_t *pool) pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*) url; pj_strdup2(pool, &name_addr->display, "User"); @@ -460,7 +460,7 @@ static pjsip_uri *create_uri10(pj_pool_t *pool) pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*) url; pj_strdup2(pool, &name_addr->display, "Strange User\\\"\\\\\\\""); @@ -474,7 +474,7 @@ static pjsip_uri *create_uri11(pj_pool_t *pool) pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*) url; pj_strdup2(pool, &name_addr->display, "Rogue User\\"); @@ -488,7 +488,7 @@ static pjsip_uri *create_uri12(pj_pool_t *pool) pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*) url; pj_strdup2(pool, &name_addr->display, "Strange User\""); @@ -500,7 +500,7 @@ static pjsip_uri *create_uri13(pj_pool_t *pool) { /* "sip:localhost;pvalue=\"hello world\"" */ pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->host, "localhost"); //pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\""); param_add(url->other_param, "pvalue", "hello world"); @@ -513,7 +513,7 @@ static pjsip_uri *create_uri14(pj_pool_t *pool) pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); name_addr->uri = (pjsip_uri*) url; pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me"); @@ -528,7 +528,7 @@ static pjsip_uri *create_uri15(pj_pool_t *pool) { /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */ pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->host, ALPHANUM "-_.com"); return (pjsip_uri*)url; } @@ -537,7 +537,7 @@ static pjsip_uri *create_uri16(pj_pool_t *pool) { /* "sip:" USER_CHAR ":" PASS_CHAR "@host" */ pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->user, USER_CHAR); pj_strdup2(pool, &url->passwd, PASS_CHAR); pj_strdup2(pool, &url->host, "host"); @@ -548,7 +548,7 @@ static pjsip_uri *create_uri17(pj_pool_t *pool) { /* "sip:host;user=ip;" PARAM_CHAR "%21=" PARAM_CHAR "%21;lr;other=1;transport=sctp;other2" */ pjsip_sip_uri *url; - url = pjsip_url_create(pool, 0); + url = pjsip_sip_uri_create(pool, 0); pj_strdup2(pool, &url->host, "host"); pj_strdup2(pool, &url->user_param, "ip"); pj_strdup2(pool, &url->transport_param, "sctp"); -- cgit v1.2.3