diff options
author | Benny Prijono <bennylp@teluu.com> | 2005-11-13 19:40:44 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2005-11-13 19:40:44 +0000 |
commit | a08b589d09d5197f9a76d549a189e4686bd2ca8c (patch) | |
tree | 549904e7680dfab96b3ce579b1843c5d58107100 /pjsip/src | |
parent | 8df70c6d5fef443506618bf31b686d53fef3f259 (diff) |
Applying license to pjproject
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@49 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip/src')
29 files changed, 21190 insertions, 20552 deletions
diff --git a/pjsip/src/pjsip-simple/event_notify.c b/pjsip/src/pjsip-simple/event_notify.c index 24aa68a0..42d85030 100644 --- a/pjsip/src/pjsip-simple/event_notify.c +++ b/pjsip/src/pjsip-simple/event_notify.c @@ -1,1629 +1,1651 @@ -/* $Id$ - * - */ -#include <pjsip_simple/event_notify.h> -#include <pjsip/sip_msg.h> -#include <pjsip/sip_misc.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_module.h> -#include <pjsip/sip_transaction.h> -#include <pjsip/sip_event.h> -#include <pj/pool.h> -#include <pj/timer.h> -#include <pj/string.h> -#include <pj/hash.h> -#include <pj/os.h> -#include <pj/except.h> -#include <pj/log.h> -#include <pj/guid.h> - -#define THIS_FILE "event_sub" - -/* String names for state. - * The names here should be compliant with sub_state names in RFC3265. - */ -static const pj_str_t state[] = { - { "null", 4 }, - { "active", 6 }, - { "pending", 7 }, - { "terminated", 10 }, - { "unknown", 7 } -}; - -/* Timer IDs */ -#define TIMER_ID_REFRESH 1 -#define TIMER_ID_UAS_EXPIRY 2 - -/* Static configuration. */ -#define SECONDS_BEFORE_EXPIRY 10 -#define MGR_POOL_SIZE 512 -#define MGR_POOL_INC 0 -#define SUB_POOL_SIZE 2048 -#define SUB_POOL_INC 0 -#define HASH_TABLE_SIZE 32 - -/* Static vars. */ -static int mod_id; -static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}}; -static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}}; - -typedef struct package -{ - PJ_DECL_LIST_MEMBER(struct package) - pj_str_t event; - int accept_cnt; - pj_str_t *accept; - pjsip_event_sub_pkg_cb cb; -} package; - -/* Event subscription manager singleton instance. */ -static struct pjsip_event_sub_mgr -{ - pj_pool_t *pool; - pj_hash_table_t *ht; - pjsip_endpoint *endpt; - pj_mutex_t *mutex; - pjsip_allow_events_hdr *allow_events; - package pkg_list; -} mgr; - -/* Fordward declarations for static functions. */ -static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t); -static pj_status_t mod_deinit(pjsip_module*); -static void tsx_handler(pjsip_module*, pjsip_event*); -static pjsip_event_sub *find_sub(pjsip_rx_data *); -static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*); -static void on_subscribe_response(void *, pjsip_event*); -static void on_notify_request(pjsip_transaction *, pjsip_rx_data*); -static void on_notify_response(void *, pjsip_event *); -static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*); -static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*); -static pj_status_t send_sub_refresh( pjsip_event_sub *sub ); - -/* Module descriptor. */ -static pjsip_module event_sub_module = -{ - {"EventSub", 8}, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - &mgr, /* User data. */ - 2, /* Number of methods supported . */ - { &SUBSCRIBE, &NOTIFY }, /* Array of methods */ - &mod_init, /* init_module() */ - NULL, /* start_module() */ - &mod_deinit, /* deinit_module() */ - &tsx_handler, /* tsx_handler() */ -}; - -/* - * Module initialization. - * This will be called by endpoint when it initializes all modules. - */ -static pj_status_t mod_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - pj_pool_t *pool; - - pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC); - if (!pool) - return -1; - - /* Manager initialization: create hash table and mutex. */ - mgr.pool = pool; - mgr.endpt = endpt; - mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE); - if (!mgr.ht) - return -1; - - mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE); - if (!mgr.mutex) - return -1; - - /* Attach manager to module. */ - mod->mod_data = &mgr; - - /* Init package list. */ - pj_list_init(&mgr.pkg_list); - - /* Init Allow-Events header. */ - mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool); - - /* Save the module ID. */ - mod_id = id; - - pjsip_event_notify_init_parser(); - return 0; -} - -/* - * Module deinitialization. - * Called by endpoint. - */ -static pj_status_t mod_deinit( struct pjsip_module *mod ) -{ - pj_mutex_lock(mgr.mutex); - pj_mutex_destroy(mgr.mutex); - pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool); - return 0; -} - -/* - * This public function is called by application to register callback. - * In exchange, the instance of the module is returned. - */ -PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void) -{ - return &event_sub_module; -} - -/* - * Register event package. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event, - int accept_cnt, - const pj_str_t accept[], - const pjsip_event_sub_pkg_cb *cb ) -{ - package *pkg; - int i; - - pj_mutex_lock(mgr.mutex); - - /* Create and register new package. */ - pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg)); - pj_strdup(mgr.pool, &pkg->event, event); - pj_list_insert_before(&mgr.pkg_list, pkg); - - /* Save Accept specification. */ - pkg->accept_cnt = accept_cnt; - pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t)); - for (i=0; i<accept_cnt; ++i) { - pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]); - } - - /* Copy callback. */ - pj_memcpy(&pkg->cb, cb, sizeof(*cb)); - - /* Update Allow-Events header. */ - pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS); - mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event; - - pj_mutex_unlock(mgr.mutex); - return 0; -} - -/* - * Create subscription key (for hash table). - */ -static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool, - pjsip_role_e role, - const pj_str_t *call_id, const pj_str_t *from_tag) -{ - char *p; - - p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3); - *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C'); - *p++ = '$'; - pj_memcpy(p, call_id->ptr, call_id->slen); - p += call_id->slen; - *p++ = '$'; - pj_memcpy(p, from_tag->ptr, from_tag->slen); - p += from_tag->slen; - - key->slen = p - key->ptr; -} - - -/* - * Create UAC subscription. - */ -PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, - const pj_str_t *from, - const pj_str_t *to, - const pj_str_t *event, - int expires, - int accept_cnt, - const pj_str_t accept[], - void *user_data, - const pjsip_event_sub_cb *cb) -{ - pjsip_tx_data *tdata; - pj_pool_t *pool; - const pjsip_hdr *hdr; - pjsip_event_sub *sub; - PJ_USE_EXCEPTION; - - PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s", - event->slen, event->ptr, to->slen, to->ptr)); - - /* Create pool for the event subscription. */ - pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC); - if (!pool) { - return NULL; - } - - /* Init subscription. */ - sub = pj_pool_calloc(pool, 1, sizeof(*sub)); - sub->pool = pool; - sub->endpt = endpt; - sub->role = PJSIP_ROLE_UAC; - sub->state = PJSIP_EVENT_SUB_STATE_PENDING; - sub->state_str = state[sub->state]; - sub->user_data = user_data; - sub->timer.id = 0; - sub->default_interval = expires; - pj_memcpy(&sub->cb, cb, sizeof(*cb)); - pj_list_init(&sub->auth_sess); - pj_list_init(&sub->route_set); - sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); - if (!sub->mutex) { - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - - /* The easiest way to parse the parameters is to create a dummy request! */ - tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from, - NULL, -1, NULL); - if (!tdata) { - pj_mutex_destroy(sub->mutex); - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - - /* - * Duplicate headers in the request to our structure. - */ - PJ_TRY { - int i; - - /* From */ - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); - pj_assert(hdr != NULL); - sub->from = pjsip_hdr_clone(pool, hdr); - - /* To */ - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); - pj_assert(hdr != NULL); - sub->to = pjsip_hdr_clone(pool, hdr); - - /* Contact. */ - sub->contact = pjsip_contact_hdr_create(pool); - sub->contact->uri = sub->from->uri; - - /* Call-ID */ - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL); - pj_assert(hdr != NULL); - sub->call_id = pjsip_hdr_clone(pool, hdr); - - /* CSeq */ - sub->cseq = pj_rand() % 0xFFFF; - - /* Event. */ - sub->event = pjsip_event_hdr_create(sub->pool); - pj_strdup(pool, &sub->event->event_type, event); - - /* Expires. */ - sub->uac_expires = pjsip_expires_hdr_create(pool); - sub->uac_expires->ivalue = expires; - - /* Accept. */ - sub->local_accept = pjsip_accept_hdr_create(pool); - for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) { - sub->local_accept->count++; - pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]); - } - - /* Register to hash table. */ - create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC, - &sub->call_id->id, &sub->from->tag); - pj_mutex_lock( mgr.mutex ); - pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); - pj_mutex_unlock( mgr.mutex ); - - } - PJ_DEFAULT { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init", - sub, state[sub->state].ptr, PJ_GET_EXCEPTION())); - - pjsip_tx_data_dec_ref(tdata); - pj_mutex_destroy(sub->mutex); - pjsip_endpt_destroy_pool(endpt, sub->pool); - return NULL; - } - PJ_END; - - /* All set, delete temporary transmit data as we don't need it. */ - pjsip_tx_data_dec_ref(tdata); - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s", - sub, state[sub->state].ptr, - to->slen, to->ptr, event->slen, event->ptr)); - - return sub; -} - -/* - * Set credentials. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, - int count, - const pjsip_cred_info cred[]) -{ - pj_mutex_lock(sub->mutex); - if (count > 0) { - sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info)); - pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info)); - } - sub->cred_cnt = count; - pj_mutex_unlock(sub->mutex); - return 0; -} - -/* - * Set route-set. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, - const pjsip_route_hdr *route_set ) -{ - const pjsip_route_hdr *hdr; - - pj_mutex_lock(sub->mutex); - - /* Clear existing route set. */ - pj_list_init(&sub->route_set); - - /* Duplicate route headers. */ - hdr = route_set->next; - while (hdr != route_set) { - pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr); - pj_list_insert_before(&sub->route_set, new_hdr); - hdr = hdr->next; - } - - pj_mutex_unlock(sub->mutex); - - return 0; -} - -/* - * Send subscribe request. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ) -{ - pj_status_t status; - - pj_mutex_lock(sub->mutex); - status = send_sub_refresh(sub); - pj_mutex_unlock(sub->mutex); - - return status; -} - -/* - * Destroy subscription. - * If there are pending transactions, then this will just set the flag. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub) -{ - pj_assert(sub != NULL); - if (sub == NULL) - return -1; - - /* Application must terminate the subscription first. */ - pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL || - sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed", - sub, state[sub->state].ptr)); - - pj_mutex_lock(mgr.mutex); - pj_mutex_lock(sub->mutex); - - /* Set delete flag. */ - sub->delete_flag = 1; - - /* Unregister timer, if any. */ - if (sub->timer.id != 0) { - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - sub->timer.id = 0; - } - - if (sub->pending_tsx > 0) { - pj_mutex_unlock(sub->mutex); - pj_mutex_unlock(mgr.mutex); - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later", - sub, state[sub->state].ptr, - sub->pending_tsx)); - return 1; - } - - /* Unregister from hash table. */ - pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL); - - /* Destroy. */ - pj_mutex_destroy(sub->mutex); - pjsip_endpt_destroy_pool(sub->endpt, sub->pool); - - pj_mutex_unlock(mgr.mutex); - - PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub)); - return 0; -} - -/* Change state. */ -static void sub_set_state( pjsip_event_sub *sub, int new_state) -{ - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s", - sub, state[sub->state].ptr, state[new_state].ptr)); - sub->state = new_state; - sub->state_str = state[new_state]; -} - -/* - * Refresh subscription. - */ -static pj_status_t send_sub_refresh( pjsip_event_sub *sub ) -{ - pjsip_tx_data *tdata; - pj_status_t status; - const pjsip_route_hdr *route; - - pj_assert(sub->role == PJSIP_ROLE_UAC); - pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED); - if (sub->role != PJSIP_ROLE_UAC || - sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) - { - return -1; - } - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription", - sub, state[sub->state].ptr)); - - /* Create request. */ - tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, - &SUBSCRIBE, - sub->to->uri, - sub->from, sub->to, - sub->contact, sub->call_id, - sub->cseq++, - NULL); - - if (!tdata) { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!", - sub, state[sub->state].ptr)); - return -1; - } - - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, sub->event)); - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept)); - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); - - /* Authentication */ - pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, - sub->cred_cnt, sub->cred_info); - - /* Route set. */ - route = sub->route_set.next; - while (route != &sub->route_set) { - pj_list_insert_before( &tdata->msg->hdr, - pjsip_hdr_shallow_clone(tdata->pool, route)); - route = route->next; - } - - /* Send */ - status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, - &on_subscribe_response); - if (status == 0) { - sub->pending_tsx++; - } else { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!", - sub, state[sub->state].ptr)); - } - - return status; -} - -/* - * Stop subscription. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ) -{ - pjsip_tx_data *tdata; - const pjsip_route_hdr *route; - pj_status_t status; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...", - sub, state[sub->state].ptr)); - - /* Lock subscription. */ - pj_mutex_lock(sub->mutex); - - pj_assert(sub->role == PJSIP_ROLE_UAC); - - /* Kill refresh timer, if any. */ - if (sub->timer.id != 0) { - sub->timer.id = 0; - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - } - - /* Create request. */ - tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, - &SUBSCRIBE, - sub->to->uri, - sub->from, sub->to, - sub->contact, sub->call_id, - sub->cseq++, - NULL); - - if (!tdata) { - pj_mutex_unlock(sub->mutex); - return -1; - } - - /* Add headers to request. */ - pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); - sub->uac_expires->ivalue = 0; - pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); - - /* Add authentication. */ - pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, - sub->cred_cnt, sub->cred_info); - - - /* Route set. */ - route = sub->route_set.next; - while (route != &sub->route_set) { - pj_list_insert_before( &tdata->msg->hdr, - pjsip_hdr_shallow_clone(tdata->pool, route)); - route = route->next; - } - - /* Prevent timer from refreshing itself. */ - sub->default_interval = 0; - - /* Set state. */ - sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED ); - - /* Send the request. */ - status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, - &on_subscribe_response); - if (status == 0) { - sub->pending_tsx++; - } - - pj_mutex_unlock(sub->mutex); - - if (status != 0) { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!", - sub, state[sub->state].ptr)); - } - - return status; -} - -/* - * Send notify. - */ -PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub, - pjsip_event_sub_state new_state, - const pj_str_t *reason, - pjsip_msg_body *body) -{ - pjsip_tx_data *tdata; - pjsip_sub_state_hdr *ss_hdr; - const pjsip_route_hdr *route; - pj_time_val now; - pj_status_t status; - pjsip_event_sub_state old_state = sub->state; - - pj_gettimeofday(&now); - - pj_assert(sub->role == PJSIP_ROLE_UAS); - if (sub->role != PJSIP_ROLE_UAS) - return -1; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY", - sub, state[new_state].ptr)); - - /* Lock subscription. */ - pj_mutex_lock(sub->mutex); - - /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */ - if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) { - pj_assert(0); - pj_mutex_unlock(sub->mutex); - return -1; - } - - /* Update state no matter what. */ - sub_set_state(sub, new_state); - - /* Create transmit data. */ - tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, - &NOTIFY, - sub->to->uri, - sub->from, sub->to, - sub->contact, sub->call_id, - sub->cseq++, - NULL); - if (!tdata) { - pj_mutex_unlock(sub->mutex); - return -1; - } - - /* Add Event header. */ - pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); - - /* Add Subscription-State header. */ - ss_hdr = pjsip_sub_state_hdr_create(tdata->pool); - ss_hdr->sub_state = state[new_state]; - ss_hdr->expires_param = sub->expiry_time.sec - now.sec; - if (ss_hdr->expires_param < 0) - ss_hdr->expires_param = 0; - if (reason) - pj_strdup(tdata->pool, &ss_hdr->reason_param, reason); - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr); - - /* Add Allow-Events header. */ - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); - - /* Add authentication */ - pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, - sub->cred_cnt, sub->cred_info); - - /* Route set. */ - route = sub->route_set.next; - while (route != &sub->route_set) { - pj_list_insert_before( &tdata->msg->hdr, - pjsip_hdr_shallow_clone(tdata->pool, route)); - route = route->next; - } - - /* Attach body. */ - tdata->msg->body = body; - - /* That's it, send! */ - status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response); - if (status == 0) - sub->pending_tsx++; - - /* If terminated notify application. */ - if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { - if (sub->cb.on_sub_terminated) { - sub->pending_tsx++; - (*sub->cb.on_sub_terminated)(sub, reason); - sub->pending_tsx--; - } - } - - /* Unlock subscription. */ - pj_mutex_unlock(sub->mutex); - - if (status != 0) { - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY", - sub, state[sub->state].ptr)); - } - - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } - return status; -} - - -/* If this timer callback is called, it means subscriber hasn't refreshed its - * subscription on-time. Set the state to terminated. This will also send - * NOTIFY with Subscription-State set to terminated. - */ -static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) -{ - pjsip_event_sub *sub = entry->user_data; - pj_str_t reason = { "timeout", 7 }; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!", - sub, state[sub->state].ptr)); - - pj_mutex_lock(sub->mutex); - sub->timer.id = 0; - - if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) { - /* Notify application, but prevent app from destroying the sub. */ - ++sub->pending_tsx; - (*sub->cb.on_sub_terminated)(sub, &reason); - --sub->pending_tsx; - } - //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED, - // &reason, NULL); - pj_mutex_unlock(sub->mutex); - -} - -/* Schedule notifier expiration. */ -static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay) -{ - pj_time_val delay = { 0, 0 }; - pj_parsed_time pt; - - if (sub->timer.id != 0) - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - - pj_gettimeofday(&sub->expiry_time); - sub->expiry_time.sec += sec_delay; - - sub->timer.id = TIMER_ID_UAS_EXPIRY; - sub->timer.user_data = sub; - sub->timer.cb = &uas_expire_timer_cb; - delay.sec = sec_delay; - pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay); - - pj_time_decode(&sub->expiry_time, &pt); - PJ_LOG(4,(THIS_FILE, - "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)", - sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay)); -} - -/* This timer is called for UAC to refresh the subscription. */ -static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) -{ - pjsip_event_sub *sub = entry->user_data; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer", - sub, state[sub->state].ptr)); - - pj_mutex_lock(sub->mutex); - sub->timer.id = 0; - send_sub_refresh(sub); - pj_mutex_unlock(sub->mutex); -} - - -/* This will update the UAC's refresh schedule. */ -static void update_next_refresh(pjsip_event_sub *sub, int interval) -{ - pj_time_val delay = {0, 0}; - pj_parsed_time pt; - - if (interval < SECONDS_BEFORE_EXPIRY) { - PJ_LOG(4,(THIS_FILE, - "event_sub%p (%s): expiration delay too short (%d sec)! updated.", - sub, state[sub->state].ptr, interval)); - interval = SECONDS_BEFORE_EXPIRY; - } - - if (sub->timer.id != 0) - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - - sub->timer.id = TIMER_ID_REFRESH; - sub->timer.user_data = sub; - sub->timer.cb = &refresh_timer_cb; - pj_gettimeofday(&sub->expiry_time); - delay.sec = interval - SECONDS_BEFORE_EXPIRY; - sub->expiry_time.sec += delay.sec; - - pj_time_decode(&sub->expiry_time, &pt); - PJ_LOG(4,(THIS_FILE, - "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)", - sub, state[sub->state].ptr, - pt.hour, pt.min, pt.sec, - delay.sec)); - - pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay ); -} - - -/* Find subscription in the hash table. - * If found, lock the subscription before returning to caller. - */ -static pjsip_event_sub *find_sub(pjsip_rx_data *rdata) -{ - pj_str_t key; - pjsip_role_e role; - pjsip_event_sub *sub; - pjsip_method *method = &rdata->msg->line.req.method; - pj_str_t *tag; - - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - if (pjsip_method_cmp(method, &SUBSCRIBE)==0) { - role = PJSIP_ROLE_UAS; - tag = &rdata->to_tag; - } else { - pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); - role = PJSIP_ROLE_UAC; - tag = &rdata->to_tag; - } - } else { - if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) { - role = PJSIP_ROLE_UAC; - tag = &rdata->from_tag; - } else { - pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); - role = PJSIP_ROLE_UAS; - tag = &rdata->from_tag; - } - } - create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag); - - pj_mutex_lock(mgr.mutex); - sub = pj_hash_get(mgr.ht, key.ptr, key.slen); - if (sub) - pj_mutex_lock(sub->mutex); - pj_mutex_unlock(mgr.mutex); - - return sub; -} - - -/* This function is called when we receive SUBSCRIBE request message - * to refresh existing subscription. - */ -static void on_received_sub_refresh( pjsip_event_sub *sub, - pjsip_transaction *tsx, pjsip_rx_data *rdata) -{ - pjsip_event_hdr *e; - pjsip_expires_hdr *expires; - pj_str_t hname; - int status = 200; - pj_str_t reason_phrase = { NULL, 0 }; - int new_state = sub->state; - int old_state = sub->state; - int new_interval = 0; - pjsip_tx_data *tdata; - - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", - sub, state[sub->state].ptr)); - - /* Check that the event matches. */ - hname = pj_str("Event"); - e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); - if (!e) { - status = 400; - reason_phrase = pj_str("Missing Event header"); - goto send_response; - } - if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 || - pj_stricmp(&e->id_param, &sub->event->id_param) != 0) - { - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - goto send_response; - } - - /* Check server state. */ - if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) { - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - goto send_response; - } - - /* Check expires header. */ - expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL); - if (!expires) { - /* - status = 400; - reason_phrase = pj_str("Missing Expires header"); - goto send_response; - */ - new_interval = sub->default_interval; - } else { - /* Check that interval is not too short. - * Note that expires time may be zero (for unsubscription). - */ - new_interval = expires->ivalue; - if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) { - status = PJSIP_SC_INTERVAL_TOO_BRIEF; - goto send_response; - } - } - - /* Update interval. */ - sub->default_interval = new_interval; - pj_gettimeofday(&sub->expiry_time); - sub->expiry_time.sec += new_interval; - - /* Update timer only if this is not unsubscription. */ - if (new_interval > 0) { - sub->default_interval = new_interval; - sub_schedule_uas_expire( sub, new_interval ); - - /* Call callback. */ - if (sub->cb.on_received_refresh) { - sub->pending_tsx++; - (*sub->cb.on_received_refresh)(sub, rdata); - sub->pending_tsx--; - } - } - -send_response: - tdata = pjsip_endpt_create_response( sub->endpt, rdata, status); - if (tdata) { - if (reason_phrase.slen) - tdata->msg->line.status.reason = reason_phrase; - - /* Add Expires header. */ - expires = pjsip_expires_hdr_create(tdata->pool); - expires->ivalue = sub->default_interval; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires); - - if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { - pjsip_msg_add_hdr(tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); - } - /* Send down to transaction. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - } - - if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) { - /* Notify application if sub is terminated. */ - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - sub_set_state(sub, new_state); - if (new_state!=old_state && sub->cb.on_sub_terminated) { - pj_str_t reason = {"", 0}; - if (reason_phrase.slen) reason = reason_phrase; - else reason = *pjsip_get_status_text(status); - - sub->pending_tsx++; - (*sub->cb.on_sub_terminated)(sub, &reason); - sub->pending_tsx--; - } - } - - pj_mutex_unlock(sub->mutex); - - /* Prefer to call log when we're not holding the mutex. */ - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", - sub, state[sub->state].ptr, - (tdata ? tdata->obj_name : "null"), status)); - - /* Check if application has requested deletion. */ - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } - -} - - -/* This function is called when we receive SUBSCRIBE request message for - * a new subscription. - */ -static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata ) -{ - package *pkg; - pj_pool_t *pool; - pjsip_event_sub *sub = NULL; - pj_str_t hname; - int status = 200; - pj_str_t reason = { NULL, 0 }; - pjsip_tx_data *tdata; - pjsip_expires_hdr *expires; - pjsip_accept_hdr *accept; - pjsip_event_hdr *evhdr; - - /* Get the Event header. */ - hname = pj_str("Event"); - evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); - if (!evhdr) { - status = 400; - reason = pj_str("No Event header in request"); - goto send_response; - } - - /* Find corresponding package. - * We don't lock the manager's mutex since we assume the package list - * won't change once the application is running! - */ - pkg = mgr.pkg_list.next; - while (pkg != &mgr.pkg_list) { - if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0) - break; - pkg = pkg->next; - } - - if (pkg == &mgr.pkg_list) { - /* Event type is not supported by any packages! */ - status = 489; - reason = pj_str("Bad Event"); - goto send_response; - } - - /* First check that the Accept specification matches the - * package's Accept types. - */ - accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); - if (accept) { - unsigned i; - pj_str_t *content_type = NULL; - - for (i=0; i<accept->count && !content_type; ++i) { - int j; - for (j=0; j<pkg->accept_cnt; ++j) { - if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) { - content_type = &pkg->accept[j]; - break; - } - } - } - - if (!content_type) { - status = PJSIP_SC_NOT_ACCEPTABLE_HERE; - goto send_response; - } - } - - /* Check whether the package wants to accept the subscription. */ - pj_assert(pkg->cb.on_query_subscribe != NULL); - (*pkg->cb.on_query_subscribe)(rdata, &status); - if (!PJSIP_IS_STATUS_IN_CLASS(status,200)) - goto send_response; - - /* Create new subscription record. */ - pool = pjsip_endpt_create_pool(tsx->endpt, "esub", - SUB_POOL_SIZE, SUB_POOL_INC); - if (!pool) { - status = 500; - goto send_response; - } - sub = pj_pool_calloc(pool, 1, sizeof(*sub)); - sub->pool = pool; - sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); - if (!sub->mutex) { - status = 500; - goto send_response; - } - - PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub)); - - /* Start locking mutex. */ - pj_mutex_lock(sub->mutex); - - /* Init UAS subscription */ - sub->endpt = tsx->endpt; - sub->role = PJSIP_ROLE_UAS; - sub->state = PJSIP_EVENT_SUB_STATE_PENDING; - sub->state_str = state[sub->state]; - pj_list_init(&sub->auth_sess); - pj_list_init(&sub->route_set); - sub->from = pjsip_hdr_clone(pool, rdata->to); - pjsip_fromto_set_from(sub->from); - if (sub->from->tag.slen == 0) { - pj_create_unique_string(pool, &sub->from->tag); - rdata->to->tag = sub->from->tag; - } - sub->to = pjsip_hdr_clone(pool, rdata->from); - pjsip_fromto_set_to(sub->to); - sub->contact = pjsip_contact_hdr_create(pool); - sub->contact->uri = sub->from->uri; - sub->call_id = pjsip_cid_hdr_create(pool); - pj_strdup(pool, &sub->call_id->id, &rdata->call_id); - sub->cseq = pj_rand() % 0xFFFF; - - expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL); - if (expires) { - sub->default_interval = expires->ivalue; - if (sub->default_interval > 0 && - sub->default_interval < SECONDS_BEFORE_EXPIRY) - { - status = 423; /* Interval too short. */ - goto send_response; - } - } else { - sub->default_interval = 600; - } - - /* Clone Event header. */ - sub->event = pjsip_hdr_clone(pool, evhdr); - - /* Register to hash table. */ - create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id, - &sub->from->tag); - pj_mutex_lock(mgr.mutex); - pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); - pj_mutex_unlock(mgr.mutex); - - /* Set timer where subscription will expire only when expires<>0. - * Subscriber may send new subscription with expires==0. - */ - if (sub->default_interval != 0) { - sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY); - } - - /* Notify application. */ - if (pkg->cb.on_subscribe) { - pjsip_event_sub_cb *cb = NULL; - sub->pending_tsx++; - (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval); - sub->pending_tsx--; - if (cb == NULL) - pj_memset(&sub->cb, 0, sizeof(*cb)); - else - pj_memcpy(&sub->cb, cb, sizeof(*cb)); - } - - -send_response: - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", - sub, state[sub->state].ptr, status)); - - tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status); - if (tdata) { - if (reason.slen) { - /* Customize reason text. */ - tdata->msg->line.status.reason = reason; - } - if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { - /* Add Expires header. */ - pjsip_expires_hdr *hdr; - - hdr = pjsip_expires_hdr_create(tdata->pool); - hdr->ivalue = sub->default_interval; - pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr ); - } - if (status == 423) { - /* Add Min-Expires header. */ - pjsip_min_expires_hdr *hdr; - - hdr = pjsip_min_expires_hdr_create(tdata->pool); - hdr->ivalue = SECONDS_BEFORE_EXPIRY; - pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr); - } - if (status == 489 || - status==PJSIP_SC_NOT_ACCEPTABLE_HERE || - PJSIP_IS_STATUS_IN_CLASS(status,200)) - { - /* Add Allow-Events header. */ - pjsip_hdr *hdr; - hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); - pjsip_msg_add_hdr(tdata->msg, hdr); - - /* Should add Accept header?. */ - } - - pjsip_tsx_on_tx_msg(tsx, tdata); - } - - /* If received new subscription with expires=0, terminate. */ - if (sub && sub->default_interval == 0) { - pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); - if (sub->cb.on_sub_terminated) { - pj_str_t reason = { "timeout", 7 }; - (*sub->cb.on_sub_terminated)(sub, &reason); - } - } - - if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) { - if (sub && sub->mutex) { - pjsip_event_sub_destroy(sub); - } else if (sub) { - pjsip_endpt_destroy_pool(tsx->endpt, sub->pool); - } - } else { - pj_assert(status >= 200); - pj_mutex_unlock(sub->mutex); - } -} - -/* This is the main callback when SUBSCRIBE request is received. */ -static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) -{ - pjsip_event_sub *sub = find_sub(rdata); - - if (sub) - on_received_sub_refresh(sub, tsx, rdata); - else - on_new_subscription(tsx, rdata); -} - - -/* This callback is called when response to SUBSCRIBE is received. */ -static void on_subscribe_response(void *token, pjsip_event *event) -{ - pjsip_event_sub *sub = token; - pjsip_transaction *tsx = event->obj.tsx; - int new_state, old_state = sub->state; - - pj_assert(tsx->status_code >= 200); - if (tsx->status_code < 200) - return; - - pj_assert(sub->role == PJSIP_ROLE_UAC); - - /* Lock mutex. */ - pj_mutex_lock(sub->mutex); - - /* If request failed with 401/407 error, silently retry the request. */ - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req(sub->endpt, - sub->pool, &sub->auth_sess, - sub->cred_cnt, sub->cred_info, - tsx->last_tx, event->src.rdata ); - if (tdata) { - int status; - pjsip_cseq_hdr *cseq; - cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); - cseq->cseq = sub->cseq++; - status = pjsip_endpt_send_request( sub->endpt, tdata, - -1, sub, - &on_subscribe_response); - if (status == 0) { - pj_mutex_unlock(sub->mutex); - return; - } - } - } - - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { - /* Update To tag. */ - if (sub->to->tag.slen == 0) - pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag); - - new_state = sub->state; - - } else if (tsx->status_code == 481) { - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - - } else if (tsx->status_code >= 300) { - /* RFC 3265 Section 3.1.4.2: - * If a SUBSCRIBE request to refresh a subscription fails - * with a non-481 response, the original subscription is still - * considered valid for the duration of original exires. - * - * Note: - * Since we normally send SUBSCRIBE for refreshing the subscription, - * it means the subscription already expired anyway. So we terminate - * the subscription now. - */ - if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) { - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } else { - /* Use this to be compliant with Section 3.1.4.2 - new_state = sub->state; - */ - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } - } else { - pj_assert(0); - new_state = sub->state; - } - - if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) { - sub_set_state(sub, new_state); - } - - if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE || - sub->state == PJSIP_EVENT_SUB_STATE_PENDING) - { - /* - * Register timer for next subscription refresh, but only when - * we're not unsubscribing. Also update default_interval and Expires - * header. - */ - if (sub->default_interval > 0 && !sub->delete_flag) { - pjsip_expires_hdr *exp = NULL; - - /* Could be transaction timeout. */ - if (event->src_type == PJSIP_EVENT_RX_MSG) { - exp = pjsip_msg_find_hdr(event->src.rdata->msg, - PJSIP_H_EXPIRES, NULL); - } - - if (exp) { - int delay = exp->ivalue; - if (delay > 0) { - pj_time_val new_expiry; - pj_gettimeofday(&new_expiry); - new_expiry.sec += delay; - if (sub->timer.id==0 || - new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) - { - //if (delay > 0 && delay < sub->default_interval) { - sub->default_interval = delay; - sub->uac_expires->ivalue = delay; - update_next_refresh(sub, delay); - } - } - } - } - } - - /* Call callback. */ - if (!sub->delete_flag) { - if (sub->cb.on_received_sub_response) { - (*sub->cb.on_received_sub_response)(sub, event); - } - } - - /* Notify application if we're terminated. */ - if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { - if (sub->cb.on_sub_terminated) { - pj_str_t reason; - if (event->src_type == PJSIP_EVENT_RX_MSG) - reason = event->src.rdata->msg->line.status.reason; - else - reason = *pjsip_get_status_text(tsx->status_code); - - (*sub->cb.on_sub_terminated)(sub, &reason); - } - } - - /* Decrement pending tsx count. */ - --sub->pending_tsx; - pj_assert(sub->pending_tsx >= 0); - - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } else { - pj_mutex_unlock(sub->mutex); - } - - /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */ -} - -/* - * This callback called when we receive incoming NOTIFY request. - */ -static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) -{ - pjsip_event_sub *sub; - pjsip_tx_data *tdata; - int status = 200; - int old_state; - pj_str_t reason = { NULL, 0 }; - pj_str_t reason_phrase = { NULL, 0 }; - int new_state = PJSIP_EVENT_SUB_STATE_NULL; - - /* Find subscription based on Call-ID and From tag. - * This will also automatically lock the subscription, if it's found. - */ - sub = find_sub(rdata); - if (!sub) { - /* RFC 3265: Section 3.2 Description of NOTIFY Behavior: - * Answer with 481 Subscription does not exist. - */ - PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!")); - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - - } else { - pj_assert(sub->role == PJSIP_ROLE_UAC); - PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", - sub, state[sub->state].ptr)); - - } - - new_state = old_state = sub->state; - - /* RFC 3265: Section 3.2.1 - * Check that the Event header match the subscription. - */ - if (status == 200) { - pjsip_event_hdr *hdr; - pj_str_t hname = { "Event", 5 }; - - hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); - if (!hdr) { - status = PJSIP_SC_BAD_REQUEST; - reason_phrase = pj_str("No Event header found"); - } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 || - pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) - { - status = 481; - reason_phrase = pj_str("Subscription does not exist"); - } - } - - /* Update subscription state and timer. */ - if (status == 200) { - pjsip_sub_state_hdr *hdr; - const pj_str_t hname = { "Subscription-State", 18 }; - const pj_str_t state_active = { "active", 6 }, - state_pending = { "pending", 7}, - state_terminated = { "terminated", 10 }; - - hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); - if (!hdr) { - status = PJSIP_SC_BAD_REQUEST; - reason_phrase = pj_str("No Subscription-State header found"); - goto process; - } - - /* - * Update subscription state. - */ - if (pj_stricmp(&hdr->sub_state, &state_active) == 0) { - if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) - new_state = PJSIP_EVENT_SUB_STATE_ACTIVE; - } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) { - if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) - new_state = PJSIP_EVENT_SUB_STATE_PENDING; - } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) { - new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } else { - new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN; - } - - reason = hdr->reason_param; - - if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL && - sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) - { - sub_set_state(sub, new_state); - if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) { - pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state); - } else { - sub->state_str = state[new_state]; - } - } - - /* - * Update timeout timer in required, just in case notifier changed the - * expiration to shorter time. - * Section 3.2.2: the expires param can only shorten the interval. - */ - if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || - sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) - { - pj_time_val now, new_expiry; - - pj_gettimeofday(&now); - new_expiry.sec = now.sec + hdr->expires_param; - if (sub->timer.id==0 || - new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) - { - update_next_refresh(sub, hdr->expires_param); - } - } - } - -process: - /* Note: here we sub MAY BE NULL! */ - - /* Send response to NOTIFY */ - tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status ); - if (tdata) { - if (reason_phrase.slen) - tdata->msg->line.status.reason = reason_phrase; - - if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { - pjsip_hdr *hdr; - hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); - pjsip_msg_add_hdr( tdata->msg, hdr); - } - - pjsip_tsx_on_tx_msg(tsx, tdata); - } - - /* Call NOTIFY callback, if any. */ - if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) { - sub->pending_tsx++; - (*sub->cb.on_received_notify)(sub, rdata); - sub->pending_tsx--; - } - - /* Check if subscription is terminated and call callback. */ - if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { - if (sub->cb.on_sub_terminated) { - sub->pending_tsx++; - (*sub->cb.on_sub_terminated)(sub, &reason); - sub->pending_tsx--; - } - } - - /* Check if application has requested deletion. */ - if (sub && sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } else if (sub) { - pj_mutex_unlock(sub->mutex); - } -} - -/* This callback is called when we received NOTIFY response. */ -static void on_notify_response(void *token, pjsip_event *event) -{ - pjsip_event_sub *sub = token; - pjsip_event_sub_state old_state = sub->state; - pjsip_transaction *tsx = event->obj.tsx; - - /* Lock the subscription. */ - pj_mutex_lock(sub->mutex); - - pj_assert(sub->role == PJSIP_ROLE_UAS); - - /* If request failed with authorization failure, silently retry. */ - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req(sub->endpt, - sub->pool, &sub->auth_sess, - sub->cred_cnt, sub->cred_info, - tsx->last_tx, event->src.rdata ); - if (tdata) { - int status; - pjsip_cseq_hdr *cseq; - cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); - cseq->cseq = sub->cseq++; - status = pjsip_endpt_send_request( sub->endpt, tdata, - -1, sub, - &on_notify_response); - if (status == 0) { - pj_mutex_unlock(sub->mutex); - return; - } - } - } - - /* Notify application. */ - if (sub->cb.on_received_notify_response) - (*sub->cb.on_received_notify_response)(sub, event); - - /* Check for response 481. */ - if (event->obj.tsx->status_code == 481) { - /* Remote says that the subscription does not exist! - * Terminate subscription! - */ - sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED); - if (sub->timer.id) { - pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); - sub->timer.id = 0; - } - - PJ_LOG(4, (THIS_FILE, - "event_sub%p (%s): got 481 response to NOTIFY. Terminating...", - sub, state[sub->state].ptr)); - - /* Notify app. */ - if (sub->state!=old_state && sub->cb.on_sub_terminated) - (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason); - } - - /* Decrement pending transaction count. */ - --sub->pending_tsx; - pj_assert(sub->pending_tsx >= 0); - - /* Check that the subscription is marked for deletion. */ - if (sub->delete_flag && sub->pending_tsx <= 0) { - pjsip_event_sub_destroy(sub); - } else { - pj_mutex_unlock(sub->mutex); - } - - /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */ -} - - -/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY - * requests. - */ -static void tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_msg *msg; - pjsip_rx_data *rdata; - - /* Only want incoming message events. */ - if (event->src_type != PJSIP_EVENT_RX_MSG) - return; - - rdata = event->src.rdata; - msg = rdata->msg; - - /* Only want to process request messages. */ - if (msg->type != PJSIP_REQUEST_MSG) - return; - - /* Only want the first notification. */ - if (event->obj.tsx && event->obj.tsx->status_code >= 100) - return; - - if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) { - /* Process incoming SUBSCRIBE request. */ - on_subscribe_request( event->obj.tsx, rdata ); - } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) { - /* Process incoming NOTIFY request. */ - on_notify_request( event->obj.tsx, rdata ); - } -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_simple/event_notify.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pj/pool.h>
+#include <pj/timer.h>
+#include <pj/string.h>
+#include <pj/hash.h>
+#include <pj/os.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/guid.h>
+
+#define THIS_FILE "event_sub"
+
+/* String names for state.
+ * The names here should be compliant with sub_state names in RFC3265.
+ */
+static const pj_str_t state[] = {
+ { "null", 4 },
+ { "active", 6 },
+ { "pending", 7 },
+ { "terminated", 10 },
+ { "unknown", 7 }
+};
+
+/* Timer IDs */
+#define TIMER_ID_REFRESH 1
+#define TIMER_ID_UAS_EXPIRY 2
+
+/* Static configuration. */
+#define SECONDS_BEFORE_EXPIRY 10
+#define MGR_POOL_SIZE 512
+#define MGR_POOL_INC 0
+#define SUB_POOL_SIZE 2048
+#define SUB_POOL_INC 0
+#define HASH_TABLE_SIZE 32
+
+/* Static vars. */
+static int mod_id;
+static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}};
+static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}};
+
+typedef struct package
+{
+ PJ_DECL_LIST_MEMBER(struct package)
+ pj_str_t event;
+ int accept_cnt;
+ pj_str_t *accept;
+ pjsip_event_sub_pkg_cb cb;
+} package;
+
+/* Event subscription manager singleton instance. */
+static struct pjsip_event_sub_mgr
+{
+ pj_pool_t *pool;
+ pj_hash_table_t *ht;
+ pjsip_endpoint *endpt;
+ pj_mutex_t *mutex;
+ pjsip_allow_events_hdr *allow_events;
+ package pkg_list;
+} mgr;
+
+/* Fordward declarations for static functions. */
+static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t);
+static pj_status_t mod_deinit(pjsip_module*);
+static void tsx_handler(pjsip_module*, pjsip_event*);
+static pjsip_event_sub *find_sub(pjsip_rx_data *);
+static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*);
+static void on_subscribe_response(void *, pjsip_event*);
+static void on_notify_request(pjsip_transaction *, pjsip_rx_data*);
+static void on_notify_response(void *, pjsip_event *);
+static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
+static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
+static pj_status_t send_sub_refresh( pjsip_event_sub *sub );
+
+/* Module descriptor. */
+static pjsip_module event_sub_module =
+{
+ {"EventSub", 8}, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ &mgr, /* User data. */
+ 2, /* Number of methods supported . */
+ { &SUBSCRIBE, &NOTIFY }, /* Array of methods */
+ &mod_init, /* init_module() */
+ NULL, /* start_module() */
+ &mod_deinit, /* deinit_module() */
+ &tsx_handler, /* tsx_handler() */
+};
+
+/*
+ * Module initialization.
+ * This will be called by endpoint when it initializes all modules.
+ */
+static pj_status_t mod_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ pj_pool_t *pool;
+
+ pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC);
+ if (!pool)
+ return -1;
+
+ /* Manager initialization: create hash table and mutex. */
+ mgr.pool = pool;
+ mgr.endpt = endpt;
+ mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE);
+ if (!mgr.ht)
+ return -1;
+
+ mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE);
+ if (!mgr.mutex)
+ return -1;
+
+ /* Attach manager to module. */
+ mod->mod_data = &mgr;
+
+ /* Init package list. */
+ pj_list_init(&mgr.pkg_list);
+
+ /* Init Allow-Events header. */
+ mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool);
+
+ /* Save the module ID. */
+ mod_id = id;
+
+ pjsip_event_notify_init_parser();
+ return 0;
+}
+
+/*
+ * Module deinitialization.
+ * Called by endpoint.
+ */
+static pj_status_t mod_deinit( struct pjsip_module *mod )
+{
+ pj_mutex_lock(mgr.mutex);
+ pj_mutex_destroy(mgr.mutex);
+ pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool);
+ return 0;
+}
+
+/*
+ * This public function is called by application to register callback.
+ * In exchange, the instance of the module is returned.
+ */
+PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void)
+{
+ return &event_sub_module;
+}
+
+/*
+ * Register event package.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event,
+ int accept_cnt,
+ const pj_str_t accept[],
+ const pjsip_event_sub_pkg_cb *cb )
+{
+ package *pkg;
+ int i;
+
+ pj_mutex_lock(mgr.mutex);
+
+ /* Create and register new package. */
+ pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg));
+ pj_strdup(mgr.pool, &pkg->event, event);
+ pj_list_insert_before(&mgr.pkg_list, pkg);
+
+ /* Save Accept specification. */
+ pkg->accept_cnt = accept_cnt;
+ pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t));
+ for (i=0; i<accept_cnt; ++i) {
+ pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]);
+ }
+
+ /* Copy callback. */
+ pj_memcpy(&pkg->cb, cb, sizeof(*cb));
+
+ /* Update Allow-Events header. */
+ pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS);
+ mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event;
+
+ pj_mutex_unlock(mgr.mutex);
+ return 0;
+}
+
+/*
+ * Create subscription key (for hash table).
+ */
+static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool,
+ pjsip_role_e role,
+ const pj_str_t *call_id, const pj_str_t *from_tag)
+{
+ char *p;
+
+ p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3);
+ *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C');
+ *p++ = '$';
+ pj_memcpy(p, call_id->ptr, call_id->slen);
+ p += call_id->slen;
+ *p++ = '$';
+ pj_memcpy(p, from_tag->ptr, from_tag->slen);
+ p += from_tag->slen;
+
+ key->slen = p - key->ptr;
+}
+
+
+/*
+ * Create UAC subscription.
+ */
+PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
+ const pj_str_t *from,
+ const pj_str_t *to,
+ const pj_str_t *event,
+ int expires,
+ int accept_cnt,
+ const pj_str_t accept[],
+ void *user_data,
+ const pjsip_event_sub_cb *cb)
+{
+ pjsip_tx_data *tdata;
+ pj_pool_t *pool;
+ const pjsip_hdr *hdr;
+ pjsip_event_sub *sub;
+ PJ_USE_EXCEPTION;
+
+ PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s",
+ event->slen, event->ptr, to->slen, to->ptr));
+
+ /* Create pool for the event subscription. */
+ pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC);
+ if (!pool) {
+ return NULL;
+ }
+
+ /* Init subscription. */
+ sub = pj_pool_calloc(pool, 1, sizeof(*sub));
+ sub->pool = pool;
+ sub->endpt = endpt;
+ sub->role = PJSIP_ROLE_UAC;
+ sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
+ sub->state_str = state[sub->state];
+ sub->user_data = user_data;
+ sub->timer.id = 0;
+ sub->default_interval = expires;
+ pj_memcpy(&sub->cb, cb, sizeof(*cb));
+ pj_list_init(&sub->auth_sess);
+ pj_list_init(&sub->route_set);
+ sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
+ if (!sub->mutex) {
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+
+ /* The easiest way to parse the parameters is to create a dummy request! */
+ tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from,
+ NULL, -1, NULL);
+ if (!tdata) {
+ pj_mutex_destroy(sub->mutex);
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+
+ /*
+ * Duplicate headers in the request to our structure.
+ */
+ PJ_TRY {
+ int i;
+
+ /* From */
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
+ pj_assert(hdr != NULL);
+ sub->from = pjsip_hdr_clone(pool, hdr);
+
+ /* To */
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
+ pj_assert(hdr != NULL);
+ sub->to = pjsip_hdr_clone(pool, hdr);
+
+ /* Contact. */
+ sub->contact = pjsip_contact_hdr_create(pool);
+ sub->contact->uri = sub->from->uri;
+
+ /* Call-ID */
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL);
+ pj_assert(hdr != NULL);
+ sub->call_id = pjsip_hdr_clone(pool, hdr);
+
+ /* CSeq */
+ sub->cseq = pj_rand() % 0xFFFF;
+
+ /* Event. */
+ sub->event = pjsip_event_hdr_create(sub->pool);
+ pj_strdup(pool, &sub->event->event_type, event);
+
+ /* Expires. */
+ sub->uac_expires = pjsip_expires_hdr_create(pool);
+ sub->uac_expires->ivalue = expires;
+
+ /* Accept. */
+ sub->local_accept = pjsip_accept_hdr_create(pool);
+ for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) {
+ sub->local_accept->count++;
+ pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]);
+ }
+
+ /* Register to hash table. */
+ create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC,
+ &sub->call_id->id, &sub->from->tag);
+ pj_mutex_lock( mgr.mutex );
+ pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
+ pj_mutex_unlock( mgr.mutex );
+
+ }
+ PJ_DEFAULT {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init",
+ sub, state[sub->state].ptr, PJ_GET_EXCEPTION()));
+
+ pjsip_tx_data_dec_ref(tdata);
+ pj_mutex_destroy(sub->mutex);
+ pjsip_endpt_destroy_pool(endpt, sub->pool);
+ return NULL;
+ }
+ PJ_END;
+
+ /* All set, delete temporary transmit data as we don't need it. */
+ pjsip_tx_data_dec_ref(tdata);
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s",
+ sub, state[sub->state].ptr,
+ to->slen, to->ptr, event->slen, event->ptr));
+
+ return sub;
+}
+
+/*
+ * Set credentials.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
+ int count,
+ const pjsip_cred_info cred[])
+{
+ pj_mutex_lock(sub->mutex);
+ if (count > 0) {
+ sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info));
+ pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info));
+ }
+ sub->cred_cnt = count;
+ pj_mutex_unlock(sub->mutex);
+ return 0;
+}
+
+/*
+ * Set route-set.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
+ const pjsip_route_hdr *route_set )
+{
+ const pjsip_route_hdr *hdr;
+
+ pj_mutex_lock(sub->mutex);
+
+ /* Clear existing route set. */
+ pj_list_init(&sub->route_set);
+
+ /* Duplicate route headers. */
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr);
+ pj_list_insert_before(&sub->route_set, new_hdr);
+ hdr = hdr->next;
+ }
+
+ pj_mutex_unlock(sub->mutex);
+
+ return 0;
+}
+
+/*
+ * Send subscribe request.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub )
+{
+ pj_status_t status;
+
+ pj_mutex_lock(sub->mutex);
+ status = send_sub_refresh(sub);
+ pj_mutex_unlock(sub->mutex);
+
+ return status;
+}
+
+/*
+ * Destroy subscription.
+ * If there are pending transactions, then this will just set the flag.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub)
+{
+ pj_assert(sub != NULL);
+ if (sub == NULL)
+ return -1;
+
+ /* Application must terminate the subscription first. */
+ pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL ||
+ sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed",
+ sub, state[sub->state].ptr));
+
+ pj_mutex_lock(mgr.mutex);
+ pj_mutex_lock(sub->mutex);
+
+ /* Set delete flag. */
+ sub->delete_flag = 1;
+
+ /* Unregister timer, if any. */
+ if (sub->timer.id != 0) {
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+ sub->timer.id = 0;
+ }
+
+ if (sub->pending_tsx > 0) {
+ pj_mutex_unlock(sub->mutex);
+ pj_mutex_unlock(mgr.mutex);
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later",
+ sub, state[sub->state].ptr,
+ sub->pending_tsx));
+ return 1;
+ }
+
+ /* Unregister from hash table. */
+ pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL);
+
+ /* Destroy. */
+ pj_mutex_destroy(sub->mutex);
+ pjsip_endpt_destroy_pool(sub->endpt, sub->pool);
+
+ pj_mutex_unlock(mgr.mutex);
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub));
+ return 0;
+}
+
+/* Change state. */
+static void sub_set_state( pjsip_event_sub *sub, int new_state)
+{
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s",
+ sub, state[sub->state].ptr, state[new_state].ptr));
+ sub->state = new_state;
+ sub->state_str = state[new_state];
+}
+
+/*
+ * Refresh subscription.
+ */
+static pj_status_t send_sub_refresh( pjsip_event_sub *sub )
+{
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+ const pjsip_route_hdr *route;
+
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+ pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED);
+ if (sub->role != PJSIP_ROLE_UAC ||
+ sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED)
+ {
+ return -1;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription",
+ sub, state[sub->state].ptr));
+
+ /* Create request. */
+ tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
+ &SUBSCRIBE,
+ sub->to->uri,
+ sub->from, sub->to,
+ sub->contact, sub->call_id,
+ sub->cseq++,
+ NULL);
+
+ if (!tdata) {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!",
+ sub, state[sub->state].ptr));
+ return -1;
+ }
+
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept));
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+
+ /* Authentication */
+ pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info);
+
+ /* Route set. */
+ route = sub->route_set.next;
+ while (route != &sub->route_set) {
+ pj_list_insert_before( &tdata->msg->hdr,
+ pjsip_hdr_shallow_clone(tdata->pool, route));
+ route = route->next;
+ }
+
+ /* Send */
+ status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
+ &on_subscribe_response);
+ if (status == 0) {
+ sub->pending_tsx++;
+ } else {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!",
+ sub, state[sub->state].ptr));
+ }
+
+ return status;
+}
+
+/*
+ * Stop subscription.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub )
+{
+ pjsip_tx_data *tdata;
+ const pjsip_route_hdr *route;
+ pj_status_t status;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...",
+ sub, state[sub->state].ptr));
+
+ /* Lock subscription. */
+ pj_mutex_lock(sub->mutex);
+
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+
+ /* Kill refresh timer, if any. */
+ if (sub->timer.id != 0) {
+ sub->timer.id = 0;
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+ }
+
+ /* Create request. */
+ tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
+ &SUBSCRIBE,
+ sub->to->uri,
+ sub->from, sub->to,
+ sub->contact, sub->call_id,
+ sub->cseq++,
+ NULL);
+
+ if (!tdata) {
+ pj_mutex_unlock(sub->mutex);
+ return -1;
+ }
+
+ /* Add headers to request. */
+ pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+ sub->uac_expires->ivalue = 0;
+ pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
+
+ /* Add authentication. */
+ pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info);
+
+
+ /* Route set. */
+ route = sub->route_set.next;
+ while (route != &sub->route_set) {
+ pj_list_insert_before( &tdata->msg->hdr,
+ pjsip_hdr_shallow_clone(tdata->pool, route));
+ route = route->next;
+ }
+
+ /* Prevent timer from refreshing itself. */
+ sub->default_interval = 0;
+
+ /* Set state. */
+ sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED );
+
+ /* Send the request. */
+ status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
+ &on_subscribe_response);
+ if (status == 0) {
+ sub->pending_tsx++;
+ }
+
+ pj_mutex_unlock(sub->mutex);
+
+ if (status != 0) {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!",
+ sub, state[sub->state].ptr));
+ }
+
+ return status;
+}
+
+/*
+ * Send notify.
+ */
+PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub,
+ pjsip_event_sub_state new_state,
+ const pj_str_t *reason,
+ pjsip_msg_body *body)
+{
+ pjsip_tx_data *tdata;
+ pjsip_sub_state_hdr *ss_hdr;
+ const pjsip_route_hdr *route;
+ pj_time_val now;
+ pj_status_t status;
+ pjsip_event_sub_state old_state = sub->state;
+
+ pj_gettimeofday(&now);
+
+ pj_assert(sub->role == PJSIP_ROLE_UAS);
+ if (sub->role != PJSIP_ROLE_UAS)
+ return -1;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY",
+ sub, state[new_state].ptr));
+
+ /* Lock subscription. */
+ pj_mutex_lock(sub->mutex);
+
+ /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */
+ if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) {
+ pj_assert(0);
+ pj_mutex_unlock(sub->mutex);
+ return -1;
+ }
+
+ /* Update state no matter what. */
+ sub_set_state(sub, new_state);
+
+ /* Create transmit data. */
+ tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
+ &NOTIFY,
+ sub->to->uri,
+ sub->from, sub->to,
+ sub->contact, sub->call_id,
+ sub->cseq++,
+ NULL);
+ if (!tdata) {
+ pj_mutex_unlock(sub->mutex);
+ return -1;
+ }
+
+ /* Add Event header. */
+ pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+
+ /* Add Subscription-State header. */
+ ss_hdr = pjsip_sub_state_hdr_create(tdata->pool);
+ ss_hdr->sub_state = state[new_state];
+ ss_hdr->expires_param = sub->expiry_time.sec - now.sec;
+ if (ss_hdr->expires_param < 0)
+ ss_hdr->expires_param = 0;
+ if (reason)
+ pj_strdup(tdata->pool, &ss_hdr->reason_param, reason);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr);
+
+ /* Add Allow-Events header. */
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+
+ /* Add authentication */
+ pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info);
+
+ /* Route set. */
+ route = sub->route_set.next;
+ while (route != &sub->route_set) {
+ pj_list_insert_before( &tdata->msg->hdr,
+ pjsip_hdr_shallow_clone(tdata->pool, route));
+ route = route->next;
+ }
+
+ /* Attach body. */
+ tdata->msg->body = body;
+
+ /* That's it, send! */
+ status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response);
+ if (status == 0)
+ sub->pending_tsx++;
+
+ /* If terminated notify application. */
+ if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ if (sub->cb.on_sub_terminated) {
+ sub->pending_tsx++;
+ (*sub->cb.on_sub_terminated)(sub, reason);
+ sub->pending_tsx--;
+ }
+ }
+
+ /* Unlock subscription. */
+ pj_mutex_unlock(sub->mutex);
+
+ if (status != 0) {
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY",
+ sub, state[sub->state].ptr));
+ }
+
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ }
+ return status;
+}
+
+
+/* If this timer callback is called, it means subscriber hasn't refreshed its
+ * subscription on-time. Set the state to terminated. This will also send
+ * NOTIFY with Subscription-State set to terminated.
+ */
+static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
+{
+ pjsip_event_sub *sub = entry->user_data;
+ pj_str_t reason = { "timeout", 7 };
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!",
+ sub, state[sub->state].ptr));
+
+ pj_mutex_lock(sub->mutex);
+ sub->timer.id = 0;
+
+ if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ /* Notify application, but prevent app from destroying the sub. */
+ ++sub->pending_tsx;
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ --sub->pending_tsx;
+ }
+ //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED,
+ // &reason, NULL);
+ pj_mutex_unlock(sub->mutex);
+
+}
+
+/* Schedule notifier expiration. */
+static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay)
+{
+ pj_time_val delay = { 0, 0 };
+ pj_parsed_time pt;
+
+ if (sub->timer.id != 0)
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+
+ pj_gettimeofday(&sub->expiry_time);
+ sub->expiry_time.sec += sec_delay;
+
+ sub->timer.id = TIMER_ID_UAS_EXPIRY;
+ sub->timer.user_data = sub;
+ sub->timer.cb = &uas_expire_timer_cb;
+ delay.sec = sec_delay;
+ pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay);
+
+ pj_time_decode(&sub->expiry_time, &pt);
+ PJ_LOG(4,(THIS_FILE,
+ "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)",
+ sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay));
+}
+
+/* This timer is called for UAC to refresh the subscription. */
+static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
+{
+ pjsip_event_sub *sub = entry->user_data;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer",
+ sub, state[sub->state].ptr));
+
+ pj_mutex_lock(sub->mutex);
+ sub->timer.id = 0;
+ send_sub_refresh(sub);
+ pj_mutex_unlock(sub->mutex);
+}
+
+
+/* This will update the UAC's refresh schedule. */
+static void update_next_refresh(pjsip_event_sub *sub, int interval)
+{
+ pj_time_val delay = {0, 0};
+ pj_parsed_time pt;
+
+ if (interval < SECONDS_BEFORE_EXPIRY) {
+ PJ_LOG(4,(THIS_FILE,
+ "event_sub%p (%s): expiration delay too short (%d sec)! updated.",
+ sub, state[sub->state].ptr, interval));
+ interval = SECONDS_BEFORE_EXPIRY;
+ }
+
+ if (sub->timer.id != 0)
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+
+ sub->timer.id = TIMER_ID_REFRESH;
+ sub->timer.user_data = sub;
+ sub->timer.cb = &refresh_timer_cb;
+ pj_gettimeofday(&sub->expiry_time);
+ delay.sec = interval - SECONDS_BEFORE_EXPIRY;
+ sub->expiry_time.sec += delay.sec;
+
+ pj_time_decode(&sub->expiry_time, &pt);
+ PJ_LOG(4,(THIS_FILE,
+ "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)",
+ sub, state[sub->state].ptr,
+ pt.hour, pt.min, pt.sec,
+ delay.sec));
+
+ pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay );
+}
+
+
+/* Find subscription in the hash table.
+ * If found, lock the subscription before returning to caller.
+ */
+static pjsip_event_sub *find_sub(pjsip_rx_data *rdata)
+{
+ pj_str_t key;
+ pjsip_role_e role;
+ pjsip_event_sub *sub;
+ pjsip_method *method = &rdata->msg->line.req.method;
+ pj_str_t *tag;
+
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ if (pjsip_method_cmp(method, &SUBSCRIBE)==0) {
+ role = PJSIP_ROLE_UAS;
+ tag = &rdata->to_tag;
+ } else {
+ pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
+ role = PJSIP_ROLE_UAC;
+ tag = &rdata->to_tag;
+ }
+ } else {
+ if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) {
+ role = PJSIP_ROLE_UAC;
+ tag = &rdata->from_tag;
+ } else {
+ pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
+ role = PJSIP_ROLE_UAS;
+ tag = &rdata->from_tag;
+ }
+ }
+ create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag);
+
+ pj_mutex_lock(mgr.mutex);
+ sub = pj_hash_get(mgr.ht, key.ptr, key.slen);
+ if (sub)
+ pj_mutex_lock(sub->mutex);
+ pj_mutex_unlock(mgr.mutex);
+
+ return sub;
+}
+
+
+/* This function is called when we receive SUBSCRIBE request message
+ * to refresh existing subscription.
+ */
+static void on_received_sub_refresh( pjsip_event_sub *sub,
+ pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+ pjsip_event_hdr *e;
+ pjsip_expires_hdr *expires;
+ pj_str_t hname;
+ int status = 200;
+ pj_str_t reason_phrase = { NULL, 0 };
+ int new_state = sub->state;
+ int old_state = sub->state;
+ int new_interval = 0;
+ pjsip_tx_data *tdata;
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh",
+ sub, state[sub->state].ptr));
+
+ /* Check that the event matches. */
+ hname = pj_str("Event");
+ e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
+ if (!e) {
+ status = 400;
+ reason_phrase = pj_str("Missing Event header");
+ goto send_response;
+ }
+ if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
+ pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
+ {
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+ goto send_response;
+ }
+
+ /* Check server state. */
+ if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+ goto send_response;
+ }
+
+ /* Check expires header. */
+ expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
+ if (!expires) {
+ /*
+ status = 400;
+ reason_phrase = pj_str("Missing Expires header");
+ goto send_response;
+ */
+ new_interval = sub->default_interval;
+ } else {
+ /* Check that interval is not too short.
+ * Note that expires time may be zero (for unsubscription).
+ */
+ new_interval = expires->ivalue;
+ if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
+ status = PJSIP_SC_INTERVAL_TOO_BRIEF;
+ goto send_response;
+ }
+ }
+
+ /* Update interval. */
+ sub->default_interval = new_interval;
+ pj_gettimeofday(&sub->expiry_time);
+ sub->expiry_time.sec += new_interval;
+
+ /* Update timer only if this is not unsubscription. */
+ if (new_interval > 0) {
+ sub->default_interval = new_interval;
+ sub_schedule_uas_expire( sub, new_interval );
+
+ /* Call callback. */
+ if (sub->cb.on_received_refresh) {
+ sub->pending_tsx++;
+ (*sub->cb.on_received_refresh)(sub, rdata);
+ sub->pending_tsx--;
+ }
+ }
+
+send_response:
+ tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
+ if (tdata) {
+ if (reason_phrase.slen)
+ tdata->msg->line.status.reason = reason_phrase;
+
+ /* Add Expires header. */
+ expires = pjsip_expires_hdr_create(tdata->pool);
+ expires->ivalue = sub->default_interval;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);
+
+ if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ pjsip_msg_add_hdr(tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
+ }
+ /* Send down to transaction. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+
+ if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ /* Notify application if sub is terminated. */
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ sub_set_state(sub, new_state);
+ if (new_state!=old_state && sub->cb.on_sub_terminated) {
+ pj_str_t reason = {"", 0};
+ if (reason_phrase.slen) reason = reason_phrase;
+ else reason = *pjsip_get_status_text(status);
+
+ sub->pending_tsx++;
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ sub->pending_tsx--;
+ }
+ }
+
+ pj_mutex_unlock(sub->mutex);
+
+ /* Prefer to call log when we're not holding the mutex. */
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d",
+ sub, state[sub->state].ptr,
+ (tdata ? tdata->obj_name : "null"), status));
+
+ /* Check if application has requested deletion. */
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ }
+
+}
+
+
+/* This function is called when we receive SUBSCRIBE request message for
+ * a new subscription.
+ */
+static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
+{
+ package *pkg;
+ pj_pool_t *pool;
+ pjsip_event_sub *sub = NULL;
+ pj_str_t hname;
+ int status = 200;
+ pj_str_t reason = { NULL, 0 };
+ pjsip_tx_data *tdata;
+ pjsip_expires_hdr *expires;
+ pjsip_accept_hdr *accept;
+ pjsip_event_hdr *evhdr;
+
+ /* Get the Event header. */
+ hname = pj_str("Event");
+ evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
+ if (!evhdr) {
+ status = 400;
+ reason = pj_str("No Event header in request");
+ goto send_response;
+ }
+
+ /* Find corresponding package.
+ * We don't lock the manager's mutex since we assume the package list
+ * won't change once the application is running!
+ */
+ pkg = mgr.pkg_list.next;
+ while (pkg != &mgr.pkg_list) {
+ if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
+ break;
+ pkg = pkg->next;
+ }
+
+ if (pkg == &mgr.pkg_list) {
+ /* Event type is not supported by any packages! */
+ status = 489;
+ reason = pj_str("Bad Event");
+ goto send_response;
+ }
+
+ /* First check that the Accept specification matches the
+ * package's Accept types.
+ */
+ accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
+ if (accept) {
+ unsigned i;
+ pj_str_t *content_type = NULL;
+
+ for (i=0; i<accept->count && !content_type; ++i) {
+ int j;
+ for (j=0; j<pkg->accept_cnt; ++j) {
+ if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
+ content_type = &pkg->accept[j];
+ break;
+ }
+ }
+ }
+
+ if (!content_type) {
+ status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
+ goto send_response;
+ }
+ }
+
+ /* Check whether the package wants to accept the subscription. */
+ pj_assert(pkg->cb.on_query_subscribe != NULL);
+ (*pkg->cb.on_query_subscribe)(rdata, &status);
+ if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
+ goto send_response;
+
+ /* Create new subscription record. */
+ pool = pjsip_endpt_create_pool(tsx->endpt, "esub",
+ SUB_POOL_SIZE, SUB_POOL_INC);
+ if (!pool) {
+ status = 500;
+ goto send_response;
+ }
+ sub = pj_pool_calloc(pool, 1, sizeof(*sub));
+ sub->pool = pool;
+ sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
+ if (!sub->mutex) {
+ status = 500;
+ goto send_response;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));
+
+ /* Start locking mutex. */
+ pj_mutex_lock(sub->mutex);
+
+ /* Init UAS subscription */
+ sub->endpt = tsx->endpt;
+ sub->role = PJSIP_ROLE_UAS;
+ sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
+ sub->state_str = state[sub->state];
+ pj_list_init(&sub->auth_sess);
+ pj_list_init(&sub->route_set);
+ sub->from = pjsip_hdr_clone(pool, rdata->to);
+ pjsip_fromto_set_from(sub->from);
+ if (sub->from->tag.slen == 0) {
+ pj_create_unique_string(pool, &sub->from->tag);
+ rdata->to->tag = sub->from->tag;
+ }
+ sub->to = pjsip_hdr_clone(pool, rdata->from);
+ pjsip_fromto_set_to(sub->to);
+ sub->contact = pjsip_contact_hdr_create(pool);
+ sub->contact->uri = sub->from->uri;
+ sub->call_id = pjsip_cid_hdr_create(pool);
+ pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
+ sub->cseq = pj_rand() % 0xFFFF;
+
+ expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
+ if (expires) {
+ sub->default_interval = expires->ivalue;
+ if (sub->default_interval > 0 &&
+ sub->default_interval < SECONDS_BEFORE_EXPIRY)
+ {
+ status = 423; /* Interval too short. */
+ goto send_response;
+ }
+ } else {
+ sub->default_interval = 600;
+ }
+
+ /* Clone Event header. */
+ sub->event = pjsip_hdr_clone(pool, evhdr);
+
+ /* Register to hash table. */
+ create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
+ &sub->from->tag);
+ pj_mutex_lock(mgr.mutex);
+ pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
+ pj_mutex_unlock(mgr.mutex);
+
+ /* Set timer where subscription will expire only when expires<>0.
+ * Subscriber may send new subscription with expires==0.
+ */
+ if (sub->default_interval != 0) {
+ sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
+ }
+
+ /* Notify application. */
+ if (pkg->cb.on_subscribe) {
+ pjsip_event_sub_cb *cb = NULL;
+ sub->pending_tsx++;
+ (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
+ sub->pending_tsx--;
+ if (cb == NULL)
+ pj_memset(&sub->cb, 0, sizeof(*cb));
+ else
+ pj_memcpy(&sub->cb, cb, sizeof(*cb));
+ }
+
+
+send_response:
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d",
+ sub, state[sub->state].ptr, status));
+
+ tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
+ if (tdata) {
+ if (reason.slen) {
+ /* Customize reason text. */
+ tdata->msg->line.status.reason = reason;
+ }
+ if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ /* Add Expires header. */
+ pjsip_expires_hdr *hdr;
+
+ hdr = pjsip_expires_hdr_create(tdata->pool);
+ hdr->ivalue = sub->default_interval;
+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
+ }
+ if (status == 423) {
+ /* Add Min-Expires header. */
+ pjsip_min_expires_hdr *hdr;
+
+ hdr = pjsip_min_expires_hdr_create(tdata->pool);
+ hdr->ivalue = SECONDS_BEFORE_EXPIRY;
+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
+ }
+ if (status == 489 ||
+ status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
+ PJSIP_IS_STATUS_IN_CLASS(status,200))
+ {
+ /* Add Allow-Events header. */
+ pjsip_hdr *hdr;
+ hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
+ pjsip_msg_add_hdr(tdata->msg, hdr);
+
+ /* Should add Accept header?. */
+ }
+
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+
+ /* If received new subscription with expires=0, terminate. */
+ if (sub && sub->default_interval == 0) {
+ pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
+ if (sub->cb.on_sub_terminated) {
+ pj_str_t reason = { "timeout", 7 };
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ }
+ }
+
+ if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
+ if (sub && sub->mutex) {
+ pjsip_event_sub_destroy(sub);
+ } else if (sub) {
+ pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
+ }
+ } else {
+ pj_assert(status >= 200);
+ pj_mutex_unlock(sub->mutex);
+ }
+}
+
+/* This is the main callback when SUBSCRIBE request is received. */
+static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+ pjsip_event_sub *sub = find_sub(rdata);
+
+ if (sub)
+ on_received_sub_refresh(sub, tsx, rdata);
+ else
+ on_new_subscription(tsx, rdata);
+}
+
+
+/* This callback is called when response to SUBSCRIBE is received. */
+static void on_subscribe_response(void *token, pjsip_event *event)
+{
+ pjsip_event_sub *sub = token;
+ pjsip_transaction *tsx = event->obj.tsx;
+ int new_state, old_state = sub->state;
+
+ pj_assert(tsx->status_code >= 200);
+ if (tsx->status_code < 200)
+ return;
+
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+
+ /* Lock mutex. */
+ pj_mutex_lock(sub->mutex);
+
+ /* If request failed with 401/407 error, silently retry the request. */
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req(sub->endpt,
+ sub->pool, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info,
+ tsx->last_tx, event->src.rdata );
+ if (tdata) {
+ int status;
+ pjsip_cseq_hdr *cseq;
+ cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ cseq->cseq = sub->cseq++;
+ status = pjsip_endpt_send_request( sub->endpt, tdata,
+ -1, sub,
+ &on_subscribe_response);
+ if (status == 0) {
+ pj_mutex_unlock(sub->mutex);
+ return;
+ }
+ }
+ }
+
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
+ /* Update To tag. */
+ if (sub->to->tag.slen == 0)
+ pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag);
+
+ new_state = sub->state;
+
+ } else if (tsx->status_code == 481) {
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+
+ } else if (tsx->status_code >= 300) {
+ /* RFC 3265 Section 3.1.4.2:
+ * If a SUBSCRIBE request to refresh a subscription fails
+ * with a non-481 response, the original subscription is still
+ * considered valid for the duration of original exires.
+ *
+ * Note:
+ * Since we normally send SUBSCRIBE for refreshing the subscription,
+ * it means the subscription already expired anyway. So we terminate
+ * the subscription now.
+ */
+ if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) {
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ } else {
+ /* Use this to be compliant with Section 3.1.4.2
+ new_state = sub->state;
+ */
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ }
+ } else {
+ pj_assert(0);
+ new_state = sub->state;
+ }
+
+ if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ sub_set_state(sub, new_state);
+ }
+
+ if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE ||
+ sub->state == PJSIP_EVENT_SUB_STATE_PENDING)
+ {
+ /*
+ * Register timer for next subscription refresh, but only when
+ * we're not unsubscribing. Also update default_interval and Expires
+ * header.
+ */
+ if (sub->default_interval > 0 && !sub->delete_flag) {
+ pjsip_expires_hdr *exp = NULL;
+
+ /* Could be transaction timeout. */
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ exp = pjsip_msg_find_hdr(event->src.rdata->msg,
+ PJSIP_H_EXPIRES, NULL);
+ }
+
+ if (exp) {
+ int delay = exp->ivalue;
+ if (delay > 0) {
+ pj_time_val new_expiry;
+ pj_gettimeofday(&new_expiry);
+ new_expiry.sec += delay;
+ if (sub->timer.id==0 ||
+ new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
+ {
+ //if (delay > 0 && delay < sub->default_interval) {
+ sub->default_interval = delay;
+ sub->uac_expires->ivalue = delay;
+ update_next_refresh(sub, delay);
+ }
+ }
+ }
+ }
+ }
+
+ /* Call callback. */
+ if (!sub->delete_flag) {
+ if (sub->cb.on_received_sub_response) {
+ (*sub->cb.on_received_sub_response)(sub, event);
+ }
+ }
+
+ /* Notify application if we're terminated. */
+ if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ if (sub->cb.on_sub_terminated) {
+ pj_str_t reason;
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ reason = event->src.rdata->msg->line.status.reason;
+ else
+ reason = *pjsip_get_status_text(tsx->status_code);
+
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ }
+ }
+
+ /* Decrement pending tsx count. */
+ --sub->pending_tsx;
+ pj_assert(sub->pending_tsx >= 0);
+
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ } else {
+ pj_mutex_unlock(sub->mutex);
+ }
+
+ /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */
+}
+
+/*
+ * This callback called when we receive incoming NOTIFY request.
+ */
+static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
+{
+ pjsip_event_sub *sub;
+ pjsip_tx_data *tdata;
+ int status = 200;
+ int old_state;
+ pj_str_t reason = { NULL, 0 };
+ pj_str_t reason_phrase = { NULL, 0 };
+ int new_state = PJSIP_EVENT_SUB_STATE_NULL;
+
+ /* Find subscription based on Call-ID and From tag.
+ * This will also automatically lock the subscription, if it's found.
+ */
+ sub = find_sub(rdata);
+ if (!sub) {
+ /* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
+ * Answer with 481 Subscription does not exist.
+ */
+ PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+
+ } else {
+ pj_assert(sub->role == PJSIP_ROLE_UAC);
+ PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY",
+ sub, state[sub->state].ptr));
+
+ }
+
+ new_state = old_state = sub->state;
+
+ /* RFC 3265: Section 3.2.1
+ * Check that the Event header match the subscription.
+ */
+ if (status == 200) {
+ pjsip_event_hdr *hdr;
+ pj_str_t hname = { "Event", 5 };
+
+ hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
+ if (!hdr) {
+ status = PJSIP_SC_BAD_REQUEST;
+ reason_phrase = pj_str("No Event header found");
+ } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
+ pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0)
+ {
+ status = 481;
+ reason_phrase = pj_str("Subscription does not exist");
+ }
+ }
+
+ /* Update subscription state and timer. */
+ if (status == 200) {
+ pjsip_sub_state_hdr *hdr;
+ const pj_str_t hname = { "Subscription-State", 18 };
+ const pj_str_t state_active = { "active", 6 },
+ state_pending = { "pending", 7},
+ state_terminated = { "terminated", 10 };
+
+ hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
+ if (!hdr) {
+ status = PJSIP_SC_BAD_REQUEST;
+ reason_phrase = pj_str("No Subscription-State header found");
+ goto process;
+ }
+
+ /*
+ * Update subscription state.
+ */
+ if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
+ if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+ new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
+ } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
+ if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+ new_state = PJSIP_EVENT_SUB_STATE_PENDING;
+ } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
+ new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ } else {
+ new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
+ }
+
+ reason = hdr->reason_param;
+
+ if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
+ sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
+ {
+ sub_set_state(sub, new_state);
+ if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
+ pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
+ } else {
+ sub->state_str = state[new_state];
+ }
+ }
+
+ /*
+ * Update timeout timer in required, just in case notifier changed the
+ * expiration to shorter time.
+ * Section 3.2.2: the expires param can only shorten the interval.
+ */
+ if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE ||
+ sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0)
+ {
+ pj_time_val now, new_expiry;
+
+ pj_gettimeofday(&now);
+ new_expiry.sec = now.sec + hdr->expires_param;
+ if (sub->timer.id==0 ||
+ new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
+ {
+ update_next_refresh(sub, hdr->expires_param);
+ }
+ }
+ }
+
+process:
+ /* Note: here we sub MAY BE NULL! */
+
+ /* Send response to NOTIFY */
+ tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
+ if (tdata) {
+ if (reason_phrase.slen)
+ tdata->msg->line.status.reason = reason_phrase;
+
+ if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
+ pjsip_hdr *hdr;
+ hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
+ pjsip_msg_add_hdr( tdata->msg, hdr);
+ }
+
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+
+ /* Call NOTIFY callback, if any. */
+ if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
+ sub->pending_tsx++;
+ (*sub->cb.on_received_notify)(sub, rdata);
+ sub->pending_tsx--;
+ }
+
+ /* Check if subscription is terminated and call callback. */
+ if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
+ if (sub->cb.on_sub_terminated) {
+ sub->pending_tsx++;
+ (*sub->cb.on_sub_terminated)(sub, &reason);
+ sub->pending_tsx--;
+ }
+ }
+
+ /* Check if application has requested deletion. */
+ if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ } else if (sub) {
+ pj_mutex_unlock(sub->mutex);
+ }
+}
+
+/* This callback is called when we received NOTIFY response. */
+static void on_notify_response(void *token, pjsip_event *event)
+{
+ pjsip_event_sub *sub = token;
+ pjsip_event_sub_state old_state = sub->state;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ /* Lock the subscription. */
+ pj_mutex_lock(sub->mutex);
+
+ pj_assert(sub->role == PJSIP_ROLE_UAS);
+
+ /* If request failed with authorization failure, silently retry. */
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req(sub->endpt,
+ sub->pool, &sub->auth_sess,
+ sub->cred_cnt, sub->cred_info,
+ tsx->last_tx, event->src.rdata );
+ if (tdata) {
+ int status;
+ pjsip_cseq_hdr *cseq;
+ cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ cseq->cseq = sub->cseq++;
+ status = pjsip_endpt_send_request( sub->endpt, tdata,
+ -1, sub,
+ &on_notify_response);
+ if (status == 0) {
+ pj_mutex_unlock(sub->mutex);
+ return;
+ }
+ }
+ }
+
+ /* Notify application. */
+ if (sub->cb.on_received_notify_response)
+ (*sub->cb.on_received_notify_response)(sub, event);
+
+ /* Check for response 481. */
+ if (event->obj.tsx->status_code == 481) {
+ /* Remote says that the subscription does not exist!
+ * Terminate subscription!
+ */
+ sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED);
+ if (sub->timer.id) {
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+ sub->timer.id = 0;
+ }
+
+ PJ_LOG(4, (THIS_FILE,
+ "event_sub%p (%s): got 481 response to NOTIFY. Terminating...",
+ sub, state[sub->state].ptr));
+
+ /* Notify app. */
+ if (sub->state!=old_state && sub->cb.on_sub_terminated)
+ (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason);
+ }
+
+ /* Decrement pending transaction count. */
+ --sub->pending_tsx;
+ pj_assert(sub->pending_tsx >= 0);
+
+ /* Check that the subscription is marked for deletion. */
+ if (sub->delete_flag && sub->pending_tsx <= 0) {
+ pjsip_event_sub_destroy(sub);
+ } else {
+ pj_mutex_unlock(sub->mutex);
+ }
+
+ /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */
+}
+
+
+/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY
+ * requests.
+ */
+static void tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_msg *msg;
+ pjsip_rx_data *rdata;
+
+ /* Only want incoming message events. */
+ if (event->src_type != PJSIP_EVENT_RX_MSG)
+ return;
+
+ rdata = event->src.rdata;
+ msg = rdata->msg;
+
+ /* Only want to process request messages. */
+ if (msg->type != PJSIP_REQUEST_MSG)
+ return;
+
+ /* Only want the first notification. */
+ if (event->obj.tsx && event->obj.tsx->status_code >= 100)
+ return;
+
+ if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) {
+ /* Process incoming SUBSCRIBE request. */
+ on_subscribe_request( event->obj.tsx, rdata );
+ } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) {
+ /* Process incoming NOTIFY request. */
+ on_notify_request( event->obj.tsx, rdata );
+ }
+}
+
diff --git a/pjsip/src/pjsip-simple/event_notify_msg.c b/pjsip/src/pjsip-simple/event_notify_msg.c index 9d0f0cd7..2b69aef9 100644 --- a/pjsip/src/pjsip-simple/event_notify_msg.c +++ b/pjsip/src/pjsip-simple/event_notify_msg.c @@ -1,307 +1,329 @@ -/* $Id$ - * - */ -#include <pjsip_simple/event_notify_msg.h> -#include <pjsip/print.h> -#include <pjsip/sip_parser.h> -#include <pj/pool.h> -#include <pj/string.h> -#include <pj/except.h> - -static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, - const pjsip_event_hdr *hdr); -static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_event_hdr*); - -static pjsip_hdr_vptr event_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_event_hdr_print, -}; - - -PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool) -{ - pj_str_t event = { "Event", 5 }; - pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - hdr->type = PJSIP_H_OTHER; - hdr->name = hdr->sname = event; - hdr->vptr = &event_hdr_vptr; - pj_list_init(hdr); - return hdr; -} - -static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - char *endbuf = buf+size; - int printed; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - copy_advance(p, hdr->event_type); - copy_advance_pair(p, ";id=", 4, hdr->id_param); - if (hdr->other_param.slen) - copy_advance(p, hdr->other_param); - return p - buf; -} - -static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool, - const pjsip_event_hdr *rhs) -{ - pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); - pj_strdup(pool, &hdr->event_type, &rhs->event_type); - pj_strdup(pool, &hdr->id_param, &rhs->id_param); - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_event_hdr *rhs ) -{ - pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr *hdr); -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr*); - -static pjsip_hdr_vptr allow_event_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print, -}; - - -PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool) -{ - pj_str_t allow_events = { "Allow-Events", 12 }; - pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - hdr->type = PJSIP_H_OTHER; - hdr->name = hdr->sname = allow_events; - hdr->vptr = &allow_event_hdr_vptr; - pj_list_init(hdr); - return hdr; -} - -static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - char *endbuf = buf+size; - int printed; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - if (hdr->event_cnt > 0) { - int i; - copy_advance(p, hdr->events[0]); - for (i=1; i<hdr->event_cnt; ++i) { - copy_advance_pair(p, ",", 1, hdr->events[i]); - } - } - - return p - buf; -} - -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr *rhs) -{ - int i; - - pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); - hdr->event_cnt = rhs->event_cnt; - for (i=0; i<rhs->event_cnt; ++i) { - pj_strdup(pool, &hdr->events[i], &rhs->events[i]); - } - return hdr; -} - -static pjsip_allow_events_hdr* -pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_allow_events_hdr *rhs) -{ - pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr *hdr); -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr*); - -static pjsip_hdr_vptr sub_state_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print, -}; - - -PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool) -{ - pj_str_t sub_state = { "Subscription-State", 18 }; - pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - hdr->type = PJSIP_H_OTHER; - hdr->name = hdr->sname = sub_state; - hdr->vptr = &sub_state_hdr_vptr; - hdr->expires_param = -1; - hdr->retry_after = -1; - pj_list_init(hdr); - return hdr; -} - -static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - char *endbuf = buf+size; - int printed; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - copy_advance(p, hdr->sub_state); - copy_advance_pair(p, ";reason=", 8, hdr->reason_param); - if (hdr->expires_param >= 0) { - pj_memcpy(p, ";expires=", 9); - p += 9; - printed = pj_utoa(hdr->expires_param, p); - p += printed; - } - if (hdr->retry_after >= 0) { - pj_memcpy(p, ";retry-after=", 13); - p += 9; - printed = pj_utoa(hdr->retry_after, p); - p += printed; - } - if (hdr->other_param.slen) - copy_advance(p, hdr->other_param); - - return p - buf; -} - -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr *rhs) -{ - pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); - pj_strdup(pool, &hdr->sub_state, &rhs->sub_state); - pj_strdup(pool, &hdr->reason_param, &rhs->reason_param); - hdr->retry_after = rhs->retry_after; - hdr->expires_param = rhs->expires_param; - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_sub_state_hdr* -pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool, - const pjsip_sub_state_hdr *rhs) -{ - pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); - const pj_str_t id_param = { "id", 2 }; - - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type); - - while (*scanner->current == ';') { - pj_str_t pname, pvalue; - pj_scan_get_char(scanner); - pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); - if (pj_stricmp(&pname, &id_param)==0) { - hdr->id_param = pvalue; - } else { - pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); - } - } - pjsip_parse_end_hdr_imp( scanner ); - return hdr; -} - -static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); - - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]); - hdr->event_cnt = 1; - - while (*scanner->current == ',') { - pj_scan_get_char(scanner); - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]); - if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - } - - pjsip_parse_end_hdr_imp( scanner ); - return hdr; -} - -static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner, - pj_pool_t *pool) -{ - pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); - const pj_str_t reason = { "reason", 6 }, - expires = { "expires", 7 }, - retry_after = { "retry-after", 11 }; - pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state); - - while (*scanner->current == ';') { - pj_str_t pname, pvalue; - - pj_scan_get_char(scanner); - pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); - if (pj_stricmp(&pname, &reason) == 0) { - hdr->reason_param = pvalue; - } else if (pj_stricmp(&pname, &expires) == 0) { - hdr->expires_param = pj_strtoul(&pvalue); - } else if (pj_stricmp(&pname, &retry_after) == 0) { - hdr->retry_after = pj_strtoul(&pvalue); - } else { - pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); - } - } - - pjsip_parse_end_hdr_imp( scanner ); - return hdr; -} - -PJ_DEF(void) pjsip_event_notify_init_parser(void) -{ - pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event); - pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events); - pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state); -} +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_simple/event_notify_msg.h>
+#include <pjsip/print.h>
+#include <pjsip/sip_parser.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/except.h>
+
+static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
+ const pjsip_event_hdr *hdr);
+static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_event_hdr*);
+
+static pjsip_hdr_vptr event_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_event_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_event_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_event_hdr_print,
+};
+
+
+PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool)
+{
+ pj_str_t event = { "Event", 5 };
+ pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = event;
+ hdr->vptr = &event_hdr_vptr;
+ pj_list_init(hdr);
+ return hdr;
+}
+
+static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ char *endbuf = buf+size;
+ int printed;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ copy_advance(p, hdr->event_type);
+ copy_advance_pair(p, ";id=", 4, hdr->id_param);
+ if (hdr->other_param.slen)
+ copy_advance(p, hdr->other_param);
+ return p - buf;
+}
+
+static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
+ const pjsip_event_hdr *rhs)
+{
+ pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
+ pj_strdup(pool, &hdr->event_type, &rhs->event_type);
+ pj_strdup(pool, &hdr->id_param, &rhs->id_param);
+ pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_event_hdr *rhs )
+{
+ pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr *hdr);
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr*);
+
+static pjsip_hdr_vptr allow_event_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print,
+};
+
+
+PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool)
+{
+ pj_str_t allow_events = { "Allow-Events", 12 };
+ pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = allow_events;
+ hdr->vptr = &allow_event_hdr_vptr;
+ pj_list_init(hdr);
+ return hdr;
+}
+
+static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ char *endbuf = buf+size;
+ int printed;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (hdr->event_cnt > 0) {
+ int i;
+ copy_advance(p, hdr->events[0]);
+ for (i=1; i<hdr->event_cnt; ++i) {
+ copy_advance_pair(p, ",", 1, hdr->events[i]);
+ }
+ }
+
+ return p - buf;
+}
+
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr *rhs)
+{
+ int i;
+
+ pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
+ hdr->event_cnt = rhs->event_cnt;
+ for (i=0; i<rhs->event_cnt; ++i) {
+ pj_strdup(pool, &hdr->events[i], &rhs->events[i]);
+ }
+ return hdr;
+}
+
+static pjsip_allow_events_hdr*
+pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_allow_events_hdr *rhs)
+{
+ pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr *hdr);
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr*);
+
+static pjsip_hdr_vptr sub_state_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_sub_state_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_sub_state_hdr_print,
+};
+
+
+PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool)
+{
+ pj_str_t sub_state = { "Subscription-State", 18 };
+ pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = sub_state;
+ hdr->vptr = &sub_state_hdr_vptr;
+ hdr->expires_param = -1;
+ hdr->retry_after = -1;
+ pj_list_init(hdr);
+ return hdr;
+}
+
+static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ char *endbuf = buf+size;
+ int printed;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ copy_advance(p, hdr->sub_state);
+ copy_advance_pair(p, ";reason=", 8, hdr->reason_param);
+ if (hdr->expires_param >= 0) {
+ pj_memcpy(p, ";expires=", 9);
+ p += 9;
+ printed = pj_utoa(hdr->expires_param, p);
+ p += printed;
+ }
+ if (hdr->retry_after >= 0) {
+ pj_memcpy(p, ";retry-after=", 13);
+ p += 9;
+ printed = pj_utoa(hdr->retry_after, p);
+ p += printed;
+ }
+ if (hdr->other_param.slen)
+ copy_advance(p, hdr->other_param);
+
+ return p - buf;
+}
+
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr *rhs)
+{
+ pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
+ pj_strdup(pool, &hdr->sub_state, &rhs->sub_state);
+ pj_strdup(pool, &hdr->reason_param, &rhs->reason_param);
+ hdr->retry_after = rhs->retry_after;
+ hdr->expires_param = rhs->expires_param;
+ pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_sub_state_hdr*
+pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
+ const pjsip_sub_state_hdr *rhs)
+{
+ pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
+ const pj_str_t id_param = { "id", 2 };
+
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type);
+
+ while (*scanner->current == ';') {
+ pj_str_t pname, pvalue;
+ pj_scan_get_char(scanner);
+ pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+ if (pj_stricmp(&pname, &id_param)==0) {
+ hdr->id_param = pvalue;
+ } else {
+ pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+ }
+ }
+ pjsip_parse_end_hdr_imp( scanner );
+ return hdr;
+}
+
+static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
+
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]);
+ hdr->event_cnt = 1;
+
+ while (*scanner->current == ',') {
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]);
+ if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+ return hdr;
+}
+
+static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
+ const pj_str_t reason = { "reason", 6 },
+ expires = { "expires", 7 },
+ retry_after = { "retry-after", 11 };
+ pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state);
+
+ while (*scanner->current == ';') {
+ pj_str_t pname, pvalue;
+
+ pj_scan_get_char(scanner);
+ pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+ if (pj_stricmp(&pname, &reason) == 0) {
+ hdr->reason_param = pvalue;
+ } else if (pj_stricmp(&pname, &expires) == 0) {
+ hdr->expires_param = pj_strtoul(&pvalue);
+ } else if (pj_stricmp(&pname, &retry_after) == 0) {
+ hdr->retry_after = pj_strtoul(&pvalue);
+ } else {
+ pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+ }
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+ return hdr;
+}
+
+PJ_DEF(void) pjsip_event_notify_init_parser(void)
+{
+ pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event);
+ pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events);
+ pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state);
+}
diff --git a/pjsip/src/pjsip-simple/messaging.c b/pjsip/src/pjsip-simple/messaging.c index c3992b4c..3d08ba28 100644 --- a/pjsip/src/pjsip-simple/messaging.c +++ b/pjsip/src/pjsip-simple/messaging.c @@ -1,337 +1,359 @@ -/* $Id$ - * - */ -#include <pjsip_simple/messaging.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_parser.h> -#include <pjsip/sip_transaction.h> -#include <pjsip/sip_event.h> -#include <pjsip/sip_module.h> -#include <pjsip/sip_misc.h> -#include <pj/string.h> -#include <pj/pool.h> -#include <pj/guid.h> -#include <pj/string.h> -#include <pj/log.h> -#include <stdio.h> -#include <stdlib.h> - -#define THIS_FILE "messaging" - -struct messaging_data -{ - void *token; - pjsip_messaging_cb cb; -}; - -struct pjsip_messaging_session -{ - pj_pool_t *pool; - pjsip_endpoint *endpt; - pjsip_from_hdr *from; - pjsip_to_hdr *to; - pjsip_cid_hdr *call_id; - pjsip_cseq_hdr *cseq; -}; - -static int module_id; -static pjsip_on_new_msg_cb incoming_cb; -static pjsip_method message_method; - - -/* - * Set global callback to receive incoming message. - */ -PJ_DEF(pjsip_on_new_msg_cb) -pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb) -{ - pjsip_on_new_msg_cb prev_cb = incoming_cb; - incoming_cb = cb; - return prev_cb; -} - - -/* - * Create an independent message (ie. not associated with a session). - */ -PJ_DEF(pjsip_tx_data*) -pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt, - const pjsip_uri *target, - const pjsip_from_hdr *param_from, - const pjsip_to_hdr *param_to, - const pjsip_cid_hdr *param_call_id, - int param_cseq, - const pj_str_t *param_text) -{ - return pjsip_endpt_create_request_from_hdr( endpt, &message_method, - target, - param_from, param_to, - NULL, param_call_id, - param_cseq, param_text ); -} - -/* - * Create independent message from string (instead of from header). - */ -PJ_DEF(pjsip_tx_data*) -pjsip_messaging_create_msg( pjsip_endpoint *endpt, - const pj_str_t *target, - const pj_str_t *param_from, - const pj_str_t *param_to, - const pj_str_t *param_call_id, - int param_cseq, - const pj_str_t *param_text) -{ - return pjsip_endpt_create_request( endpt, &message_method, target, - param_from, param_to, NULL, param_call_id, - param_cseq, param_text); -} - -/* - * Initiate transaction to send outgoing message. - */ -PJ_DEF(pj_status_t) -pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata, - void *token, pjsip_messaging_cb cb ) -{ - pjsip_transaction *tsx; - struct messaging_data *msg_data; - - /* Create transaction. */ - tsx = pjsip_endpt_create_tsx(endpt); - if (!tsx) { - pjsip_tx_data_dec_ref(tdata); - return -1; - } - - /* Save parameters to messaging data and attach to tsx. */ - msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data)); - msg_data->cb = cb; - msg_data->token = token; - - /* Init transaction. */ - tsx->module_data[module_id] = msg_data; - if (pjsip_tsx_init_uac(tsx, tdata) != 0) { - pjsip_tx_data_dec_ref(tdata); - pjsip_endpt_destroy_tsx(endpt, tsx); - return -1; - } - - pjsip_endpt_register_tsx(endpt, tsx); - - /* - * Instruct transaction to send message. - * Further events will be received via transaction's event. - */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - /* Decrement reference counter. */ - pjsip_tx_data_dec_ref(tdata); - return 0; -} - - -/* - * Create 'IM session'. - */ -PJ_DEF(pjsip_messaging_session*) -pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from, - const pj_str_t *param_to ) -{ - pj_pool_t *pool; - pjsip_messaging_session *ses; - pj_str_t tmp, to; - - pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024); - if (!pool) - return NULL; - - ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session)); - ses->pool = pool; - ses->endpt = endpt; - - ses->call_id = pjsip_cid_hdr_create(pool); - pj_create_unique_string(pool, &ses->call_id->id); - - ses->cseq = pjsip_cseq_hdr_create(pool); - ses->cseq->cseq = pj_rand(); - ses->cseq->method = message_method; - - ses->from = pjsip_from_hdr_create(pool); - pj_strdup_with_null(pool, &tmp, param_from); - ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); - if (ses->from->uri == NULL) { - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - pj_create_unique_string(pool, &ses->from->tag); - - ses->to = pjsip_to_hdr_create(pool); - pj_strdup_with_null(pool, &to, param_from); - ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR); - if (ses->to->uri == NULL) { - pjsip_endpt_destroy_pool(endpt, pool); - return NULL; - } - - PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr)); - return ses; -} - - -/* - * Send IM message using identification from 'IM session'. - */ -PJ_DEF(pjsip_tx_data*) -pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text ) -{ - return pjsip_endpt_create_request_from_hdr( ses->endpt, - &message_method, - ses->to->uri, - ses->from, - ses->to, - NULL, - ses->call_id, - ses->cseq->cseq++, - text); -} - - -/* - * Destroy 'IM session'. - */ -PJ_DEF(pj_status_t) -pjsip_messaging_destroy_session( pjsip_messaging_session *ses ) -{ - /* - * NOTE ABOUT POSSIBLE BUG HERE... - * - * We don't check number of pending transaction before destroying IM - * session. As the result, the headers in the txdata of pending transaction - * wil be INVALID once the IM session is deleted (because we only - * shallo_clone()-ed them). - * - * This normally should be okay, because once the message is - * submitted to transaction, the transaction (or rather the transport) - * will 'print' the message to a buffer, and once it is printed, it - * won't try to access the original message again. So even when the - * original message has a dangling pointer, we should be safe. - * - * However, it will cause a problem if: - * - resolving completes asynchronously and with a substantial delay, - * and before the resolver/transport finished its job the user - * destroy the IM session. - * - if the transmit data is invalidated after the IM session is - * destroyed. - */ - - pjsip_endpt_destroy_pool(ses->endpt, ses->pool); - return 0; -} - - -static pj_status_t messaging_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - PJ_UNUSED_ARG(endpt) - PJ_UNUSED_ARG(mod) - - module_id = id; - return 0; -} - -static pj_status_t messaging_start( struct pjsip_module *mod ) -{ - PJ_UNUSED_ARG(mod) - return 0; -} - -static pj_status_t messaging_deinit( struct pjsip_module *mod ) -{ - PJ_UNUSED_ARG(mod) - return 0; -} - -static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_transaction *tsx = event->obj.tsx; - struct messaging_data *mod_data; - - PJ_UNUSED_ARG(mod) - - /* Ignore non transaction event */ - if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL) - return; - - /* If this is an incoming message, inform application. */ - if (tsx->role == PJSIP_ROLE_UAS) { - int status = 100; - pjsip_tx_data *tdata; - - /* Check if we already answered this request. */ - if (tsx->status_code >= 200) - return; - - /* Only handle MESSAGE requests!. */ - if (pjsip_method_cmp(&tsx->method, &message_method) != 0) - return; - - /* Call application callback. */ - if (incoming_cb) - status = (*incoming_cb)(event->src.rdata); - - if (status < 200 || status >= 700) - status = PJSIP_SC_INTERNAL_SERVER_ERROR; - - /* Respond request. */ - tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status ); - if (tdata) - pjsip_tsx_on_tx_msg(tsx, tdata); - - return; - } - - /* Ignore if it's not something that came from messaging module. */ - mod_data = tsx->module_data[ module_id ]; - if (mod_data == NULL) - return; - - /* Ignore non final response. */ - if (tsx->status_code < 200) - return; - - /* Don't want to call the callback more than once. */ - tsx->module_data[ module_id ] = NULL; - - /* Now call the callback. */ - if (mod_data->cb) { - (*mod_data->cb)(mod_data->token, tsx->status_code); - } -} - -static pjsip_module messaging_module = -{ - { "Messaging", 9}, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - NULL, /* User agent instance, initialized by APP. */ - 0, /* Number of methods supported (will be initialized later). */ - { 0 }, /* Array of methods (will be initialized later) */ - &messaging_init, /* init_module() */ - &messaging_start, /* start_module() */ - &messaging_deinit, /* deinit_module() */ - &messaging_tsx_handler, /* tsx_handler() */ -}; - -PJ_DEF(pjsip_module*) pjsip_messaging_get_module() -{ - static pj_str_t method_str = { "MESSAGE", 7 }; - - pjsip_method_init_np( &message_method, &method_str); - - messaging_module.method_cnt = 1; - messaging_module.methods[0] = &message_method; - - return &messaging_module; -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_simple/messaging.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_misc.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+#include <pj/guid.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define THIS_FILE "messaging"
+
+struct messaging_data
+{
+ void *token;
+ pjsip_messaging_cb cb;
+};
+
+struct pjsip_messaging_session
+{
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pjsip_from_hdr *from;
+ pjsip_to_hdr *to;
+ pjsip_cid_hdr *call_id;
+ pjsip_cseq_hdr *cseq;
+};
+
+static int module_id;
+static pjsip_on_new_msg_cb incoming_cb;
+static pjsip_method message_method;
+
+
+/*
+ * Set global callback to receive incoming message.
+ */
+PJ_DEF(pjsip_on_new_msg_cb)
+pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb)
+{
+ pjsip_on_new_msg_cb prev_cb = incoming_cb;
+ incoming_cb = cb;
+ return prev_cb;
+}
+
+
+/*
+ * Create an independent message (ie. not associated with a session).
+ */
+PJ_DEF(pjsip_tx_data*)
+pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
+ const pjsip_uri *target,
+ const pjsip_from_hdr *param_from,
+ const pjsip_to_hdr *param_to,
+ const pjsip_cid_hdr *param_call_id,
+ int param_cseq,
+ const pj_str_t *param_text)
+{
+ return pjsip_endpt_create_request_from_hdr( endpt, &message_method,
+ target,
+ param_from, param_to,
+ NULL, param_call_id,
+ param_cseq, param_text );
+}
+
+/*
+ * Create independent message from string (instead of from header).
+ */
+PJ_DEF(pjsip_tx_data*)
+pjsip_messaging_create_msg( pjsip_endpoint *endpt,
+ const pj_str_t *target,
+ const pj_str_t *param_from,
+ const pj_str_t *param_to,
+ const pj_str_t *param_call_id,
+ int param_cseq,
+ const pj_str_t *param_text)
+{
+ return pjsip_endpt_create_request( endpt, &message_method, target,
+ param_from, param_to, NULL, param_call_id,
+ param_cseq, param_text);
+}
+
+/*
+ * Initiate transaction to send outgoing message.
+ */
+PJ_DEF(pj_status_t)
+pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata,
+ void *token, pjsip_messaging_cb cb )
+{
+ pjsip_transaction *tsx;
+ struct messaging_data *msg_data;
+
+ /* Create transaction. */
+ tsx = pjsip_endpt_create_tsx(endpt);
+ if (!tsx) {
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ /* Save parameters to messaging data and attach to tsx. */
+ msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data));
+ msg_data->cb = cb;
+ msg_data->token = token;
+
+ /* Init transaction. */
+ tsx->module_data[module_id] = msg_data;
+ if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+ pjsip_tx_data_dec_ref(tdata);
+ pjsip_endpt_destroy_tsx(endpt, tsx);
+ return -1;
+ }
+
+ pjsip_endpt_register_tsx(endpt, tsx);
+
+ /*
+ * Instruct transaction to send message.
+ * Further events will be received via transaction's event.
+ */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ /* Decrement reference counter. */
+ pjsip_tx_data_dec_ref(tdata);
+ return 0;
+}
+
+
+/*
+ * Create 'IM session'.
+ */
+PJ_DEF(pjsip_messaging_session*)
+pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from,
+ const pj_str_t *param_to )
+{
+ pj_pool_t *pool;
+ pjsip_messaging_session *ses;
+ pj_str_t tmp, to;
+
+ pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024);
+ if (!pool)
+ return NULL;
+
+ ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session));
+ ses->pool = pool;
+ ses->endpt = endpt;
+
+ ses->call_id = pjsip_cid_hdr_create(pool);
+ pj_create_unique_string(pool, &ses->call_id->id);
+
+ ses->cseq = pjsip_cseq_hdr_create(pool);
+ ses->cseq->cseq = pj_rand();
+ ses->cseq->method = message_method;
+
+ ses->from = pjsip_from_hdr_create(pool);
+ pj_strdup_with_null(pool, &tmp, param_from);
+ ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (ses->from->uri == NULL) {
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+ pj_create_unique_string(pool, &ses->from->tag);
+
+ ses->to = pjsip_to_hdr_create(pool);
+ pj_strdup_with_null(pool, &to, param_from);
+ ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (ses->to->uri == NULL) {
+ pjsip_endpt_destroy_pool(endpt, pool);
+ return NULL;
+ }
+
+ PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr));
+ return ses;
+}
+
+
+/*
+ * Send IM message using identification from 'IM session'.
+ */
+PJ_DEF(pjsip_tx_data*)
+pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text )
+{
+ return pjsip_endpt_create_request_from_hdr( ses->endpt,
+ &message_method,
+ ses->to->uri,
+ ses->from,
+ ses->to,
+ NULL,
+ ses->call_id,
+ ses->cseq->cseq++,
+ text);
+}
+
+
+/*
+ * Destroy 'IM session'.
+ */
+PJ_DEF(pj_status_t)
+pjsip_messaging_destroy_session( pjsip_messaging_session *ses )
+{
+ /*
+ * NOTE ABOUT POSSIBLE BUG HERE...
+ *
+ * We don't check number of pending transaction before destroying IM
+ * session. As the result, the headers in the txdata of pending transaction
+ * wil be INVALID once the IM session is deleted (because we only
+ * shallo_clone()-ed them).
+ *
+ * This normally should be okay, because once the message is
+ * submitted to transaction, the transaction (or rather the transport)
+ * will 'print' the message to a buffer, and once it is printed, it
+ * won't try to access the original message again. So even when the
+ * original message has a dangling pointer, we should be safe.
+ *
+ * However, it will cause a problem if:
+ * - resolving completes asynchronously and with a substantial delay,
+ * and before the resolver/transport finished its job the user
+ * destroy the IM session.
+ * - if the transmit data is invalidated after the IM session is
+ * destroyed.
+ */
+
+ pjsip_endpt_destroy_pool(ses->endpt, ses->pool);
+ return 0;
+}
+
+
+static pj_status_t messaging_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ PJ_UNUSED_ARG(endpt)
+ PJ_UNUSED_ARG(mod)
+
+ module_id = id;
+ return 0;
+}
+
+static pj_status_t messaging_start( struct pjsip_module *mod )
+{
+ PJ_UNUSED_ARG(mod)
+ return 0;
+}
+
+static pj_status_t messaging_deinit( struct pjsip_module *mod )
+{
+ PJ_UNUSED_ARG(mod)
+ return 0;
+}
+
+static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_transaction *tsx = event->obj.tsx;
+ struct messaging_data *mod_data;
+
+ PJ_UNUSED_ARG(mod)
+
+ /* Ignore non transaction event */
+ if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL)
+ return;
+
+ /* If this is an incoming message, inform application. */
+ if (tsx->role == PJSIP_ROLE_UAS) {
+ int status = 100;
+ pjsip_tx_data *tdata;
+
+ /* Check if we already answered this request. */
+ if (tsx->status_code >= 200)
+ return;
+
+ /* Only handle MESSAGE requests!. */
+ if (pjsip_method_cmp(&tsx->method, &message_method) != 0)
+ return;
+
+ /* Call application callback. */
+ if (incoming_cb)
+ status = (*incoming_cb)(event->src.rdata);
+
+ if (status < 200 || status >= 700)
+ status = PJSIP_SC_INTERNAL_SERVER_ERROR;
+
+ /* Respond request. */
+ tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status );
+ if (tdata)
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ return;
+ }
+
+ /* Ignore if it's not something that came from messaging module. */
+ mod_data = tsx->module_data[ module_id ];
+ if (mod_data == NULL)
+ return;
+
+ /* Ignore non final response. */
+ if (tsx->status_code < 200)
+ return;
+
+ /* Don't want to call the callback more than once. */
+ tsx->module_data[ module_id ] = NULL;
+
+ /* Now call the callback. */
+ if (mod_data->cb) {
+ (*mod_data->cb)(mod_data->token, tsx->status_code);
+ }
+}
+
+static pjsip_module messaging_module =
+{
+ { "Messaging", 9}, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ NULL, /* User agent instance, initialized by APP. */
+ 0, /* Number of methods supported (will be initialized later). */
+ { 0 }, /* Array of methods (will be initialized later) */
+ &messaging_init, /* init_module() */
+ &messaging_start, /* start_module() */
+ &messaging_deinit, /* deinit_module() */
+ &messaging_tsx_handler, /* tsx_handler() */
+};
+
+PJ_DEF(pjsip_module*) pjsip_messaging_get_module()
+{
+ static pj_str_t method_str = { "MESSAGE", 7 };
+
+ pjsip_method_init_np( &message_method, &method_str);
+
+ messaging_module.method_cnt = 1;
+ messaging_module.methods[0] = &message_method;
+
+ return &messaging_module;
+}
+
diff --git a/pjsip/src/pjsip-simple/pidf.c b/pjsip/src/pjsip-simple/pidf.c index f1de9c9d..0c76b34e 100644 --- a/pjsip/src/pjsip-simple/pidf.c +++ b/pjsip/src/pjsip-simple/pidf.c @@ -1,335 +1,357 @@ -/* $Id$ - * - */ -#include <pjsip_simple/pidf.h> -#include <pj/string.h> -#include <pj/pool.h> - -struct pjpidf_op_desc pjpidf_op = -{ - { - &pjpidf_pres_construct, - &pjpidf_pres_add_tuple, - &pjpidf_pres_get_first_tuple, - &pjpidf_pres_get_next_tuple, - &pjpidf_pres_find_tuple, - &pjpidf_pres_remove_tuple, - &pjpidf_pres_add_note, - &pjpidf_pres_get_first_note, - &pjpidf_pres_get_next_note - }, - { - &pjpidf_tuple_construct, - &pjpidf_tuple_get_id, - &pjpidf_tuple_set_id, - &pjpidf_tuple_get_status, - &pjpidf_tuple_get_contact, - &pjpidf_tuple_set_contact, - &pjpidf_tuple_set_contact_prio, - &pjpidf_tuple_get_contact_prio, - &pjpidf_tuple_add_note, - &pjpidf_tuple_get_first_note, - &pjpidf_tuple_get_next_note, - &pjpidf_tuple_get_timestamp, - &pjpidf_tuple_set_timestamp, - &pjpidf_tuple_set_timestamp_np - }, - { - &pjpidf_status_construct, - &pjpidf_status_is_basic_open, - &pjpidf_status_set_basic_open - } -}; - -static pj_str_t PRESENCE = { "presence", 8 }; -static pj_str_t ENTITY = { "entity", 6}; -static pj_str_t TUPLE = { "tuple", 5 }; -static pj_str_t ID = { "id", 2 }; -static pj_str_t NOTE = { "note", 4 }; -static pj_str_t STATUS = { "status", 6 }; -static pj_str_t CONTACT = { "contact", 7 }; -static pj_str_t PRIORITY = { "priority", 8 }; -static pj_str_t TIMESTAMP = { "timestamp", 9 }; -static pj_str_t BASIC = { "basic", 5 }; -static pj_str_t OPEN = { "open", 4 }; -static pj_str_t CLOSED = { "closed", 6 }; -static pj_str_t EMPTY_STRING = { NULL, 0 }; - -static void xml_init_node(pj_pool_t *pool, pj_xml_node *node, - pj_str_t *name, const pj_str_t *value) -{ - pj_list_init(&node->attr_head); - pj_list_init(&node->node_head); - node->name = *name; - if (value) pj_strdup(pool, &node->content, value); - else node->content.ptr=NULL, node->content.slen=0; -} - -static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, - const pj_str_t *value) -{ - pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); - attr->name = *name; - pj_strdup(pool, &attr->value, value); - return attr; -} - -/* Presence */ -PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *entity) -{ - pj_xml_attr *attr; - - xml_init_node(pool, pres, &PRESENCE, NULL); - attr = xml_create_attr(pool, &ENTITY, entity); - pj_xml_add_attr(pres, attr); -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *id) -{ - pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t)); - pjpidf_tuple_construct(pool, t, id); - pj_xml_add_node(pres, t); - return t; -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres) -{ - return pj_xml_find_node(pres, &TUPLE); -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres, - pjpidf_tuple *tuple) -{ - return pj_xml_find_next_node(pres, tuple, &TUPLE); -} - -static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id) -{ - return pj_xml_find_attr(node, &ID, id) != NULL; -} - -PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id) -{ - return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id); -} - -PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t) -{ - PJ_UNUSED_ARG(pres) - pj_list_erase(t); -} - -PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres, - const pj_str_t *text) -{ - pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); - xml_init_node(pool, note, &NOTE, text); - pj_xml_add_node(pres, note); - return note; -} - -PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres) -{ - return pj_xml_find_node( pres, &NOTE); -} - -PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note) -{ - return pj_xml_find_next_node(t, note, &NOTE); -} - - -/* Tuple */ -PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *id) -{ - pj_xml_attr *attr; - pjpidf_status *st; - - xml_init_node(pool, t, &TUPLE, NULL); - attr = xml_create_attr(pool, &ID, id); - pj_xml_add_attr(t, attr); - st = pj_pool_alloc(pool, sizeof(*st)); - pjpidf_status_construct(pool, st); - pj_xml_add_node(t, st); -} - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t) -{ - const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL); - pj_assert(attr); - return &attr->value; -} - -PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id) -{ - pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL); - pj_assert(attr); - pj_strdup(pool, &attr->value, id); -} - - -PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t) -{ - pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS); - pj_assert(st); - return st; -} - - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); - if (!node) - return &EMPTY_STRING; - return &node->content; -} - -PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *contact) -{ - pj_xml_node *node = pj_xml_find_node(t, &CONTACT); - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &CONTACT, contact); - pj_xml_add_node(t, node); - } else { - pj_strdup(pool, &node->content, contact); - } -} - -PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *prio) -{ - pj_xml_node *node = pj_xml_find_node(t, &CONTACT); - pj_xml_attr *attr; - - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &CONTACT, NULL); - pj_xml_add_node(t, node); - } - attr = pj_xml_find_attr(node, &PRIORITY, NULL); - if (!attr) { - attr = xml_create_attr(pool, &PRIORITY, prio); - pj_xml_add_attr(node, attr); - } else { - pj_strdup(pool, &attr->value, prio); - } -} - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT); - pj_xml_attr *attr; - - if (!node) - return &EMPTY_STRING; - attr = pj_xml_find_attr(node, &PRIORITY, NULL); - if (!attr) - return &EMPTY_STRING; - return &attr->value; -} - - -PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *text) -{ - pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note)); - xml_init_node(pool, note, &NOTE, text); - pj_xml_add_node(t, note); - return note; -} - -PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t) -{ - return pj_xml_find_node(t, &NOTE); -} - -PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n) -{ - return pj_xml_find_next_node(t, n, &NOTE); -} - - -PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP); - return node ? &node->content : &EMPTY_STRING; -} - -PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t, - const pj_str_t *ts) -{ - pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &TIMESTAMP, ts); - } else { - pj_strdup(pool, &node->content, ts); - } -} - - -PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t, - pj_str_t *ts) -{ - pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP); - if (!node) { - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &TIMESTAMP, ts); - } else { - node->content = *ts; - } -} - - -/* Status */ -PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st) -{ - pj_xml_node *node; - - xml_init_node(pool, st, &STATUS, NULL); - node = pj_pool_alloc(pool, sizeof(*node)); - xml_init_node(pool, node, &BASIC, &CLOSED); - pj_xml_add_node(st, node); -} - -PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st) -{ - pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC); - pj_assert(node != NULL); - return pj_stricmp(&node->content, &OPEN)==0; -} - -PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open) -{ - pj_xml_node *node = pj_xml_find_node(st, &BASIC); - pj_assert(node != NULL); - node->content = open ? OPEN : CLOSED; -} - -PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity) -{ - pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres)); - pjpidf_pres_construct(pool, pres, entity); - return pres; -} - -PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len) -{ - pjpidf_pres *pres = pj_xml_parse(pool, text, len); - if (pres) { - if (pj_stricmp(&pres->name, &PRESENCE) != 0) - return NULL; - } - return pres; -} - -PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len) -{ - return pj_xml_print(pres, buf, len, PJ_TRUE); -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_simple/pidf.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+struct pjpidf_op_desc pjpidf_op =
+{
+ {
+ &pjpidf_pres_construct,
+ &pjpidf_pres_add_tuple,
+ &pjpidf_pres_get_first_tuple,
+ &pjpidf_pres_get_next_tuple,
+ &pjpidf_pres_find_tuple,
+ &pjpidf_pres_remove_tuple,
+ &pjpidf_pres_add_note,
+ &pjpidf_pres_get_first_note,
+ &pjpidf_pres_get_next_note
+ },
+ {
+ &pjpidf_tuple_construct,
+ &pjpidf_tuple_get_id,
+ &pjpidf_tuple_set_id,
+ &pjpidf_tuple_get_status,
+ &pjpidf_tuple_get_contact,
+ &pjpidf_tuple_set_contact,
+ &pjpidf_tuple_set_contact_prio,
+ &pjpidf_tuple_get_contact_prio,
+ &pjpidf_tuple_add_note,
+ &pjpidf_tuple_get_first_note,
+ &pjpidf_tuple_get_next_note,
+ &pjpidf_tuple_get_timestamp,
+ &pjpidf_tuple_set_timestamp,
+ &pjpidf_tuple_set_timestamp_np
+ },
+ {
+ &pjpidf_status_construct,
+ &pjpidf_status_is_basic_open,
+ &pjpidf_status_set_basic_open
+ }
+};
+
+static pj_str_t PRESENCE = { "presence", 8 };
+static pj_str_t ENTITY = { "entity", 6};
+static pj_str_t TUPLE = { "tuple", 5 };
+static pj_str_t ID = { "id", 2 };
+static pj_str_t NOTE = { "note", 4 };
+static pj_str_t STATUS = { "status", 6 };
+static pj_str_t CONTACT = { "contact", 7 };
+static pj_str_t PRIORITY = { "priority", 8 };
+static pj_str_t TIMESTAMP = { "timestamp", 9 };
+static pj_str_t BASIC = { "basic", 5 };
+static pj_str_t OPEN = { "open", 4 };
+static pj_str_t CLOSED = { "closed", 6 };
+static pj_str_t EMPTY_STRING = { NULL, 0 };
+
+static void xml_init_node(pj_pool_t *pool, pj_xml_node *node,
+ pj_str_t *name, const pj_str_t *value)
+{
+ pj_list_init(&node->attr_head);
+ pj_list_init(&node->node_head);
+ node->name = *name;
+ if (value) pj_strdup(pool, &node->content, value);
+ else node->content.ptr=NULL, node->content.slen=0;
+}
+
+static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
+ const pj_str_t *value)
+{
+ pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
+ attr->name = *name;
+ pj_strdup(pool, &attr->value, value);
+ return attr;
+}
+
+/* Presence */
+PJ_DEF(void) pjpidf_pres_construct(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *entity)
+{
+ pj_xml_attr *attr;
+
+ xml_init_node(pool, pres, &PRESENCE, NULL);
+ attr = xml_create_attr(pool, &ENTITY, entity);
+ pj_xml_add_attr(pres, attr);
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_add_tuple(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *id)
+{
+ pjpidf_tuple *t = pj_pool_alloc(pool, sizeof(*t));
+ pjpidf_tuple_construct(pool, t, id);
+ pj_xml_add_node(pres, t);
+ return t;
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_first_tuple(pjpidf_pres *pres)
+{
+ return pj_xml_find_node(pres, &TUPLE);
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_get_next_tuple(pjpidf_pres *pres,
+ pjpidf_tuple *tuple)
+{
+ return pj_xml_find_next_node(pres, tuple, &TUPLE);
+}
+
+static pj_bool_t find_tuple_by_id(pj_xml_node *node, const void *id)
+{
+ return pj_xml_find_attr(node, &ID, id) != NULL;
+}
+
+PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *id)
+{
+ return pj_xml_find(pres, &TUPLE, id, &find_tuple_by_id);
+}
+
+PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t)
+{
+ PJ_UNUSED_ARG(pres)
+ pj_list_erase(t);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_add_note(pj_pool_t *pool, pjpidf_pres *pres,
+ const pj_str_t *text)
+{
+ pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
+ xml_init_node(pool, note, &NOTE, text);
+ pj_xml_add_node(pres, note);
+ return note;
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_get_first_note(pjpidf_pres *pres)
+{
+ return pj_xml_find_node( pres, &NOTE);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_pres_get_next_note(pjpidf_pres *t, pjpidf_note *note)
+{
+ return pj_xml_find_next_node(t, note, &NOTE);
+}
+
+
+/* Tuple */
+PJ_DEF(void) pjpidf_tuple_construct(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *id)
+{
+ pj_xml_attr *attr;
+ pjpidf_status *st;
+
+ xml_init_node(pool, t, &TUPLE, NULL);
+ attr = xml_create_attr(pool, &ID, id);
+ pj_xml_add_attr(t, attr);
+ st = pj_pool_alloc(pool, sizeof(*st));
+ pjpidf_status_construct(pool, st);
+ pj_xml_add_node(t, st);
+}
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_id(const pjpidf_tuple *t)
+{
+ const pj_xml_attr *attr = pj_xml_find_attr((pj_xml_node*)t, &ID, NULL);
+ pj_assert(attr);
+ return &attr->value;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_id(pj_pool_t *pool, pjpidf_tuple *t, const pj_str_t *id)
+{
+ pj_xml_attr *attr = pj_xml_find_attr(t, &ID, NULL);
+ pj_assert(attr);
+ pj_strdup(pool, &attr->value, id);
+}
+
+
+PJ_DEF(pjpidf_status*) pjpidf_tuple_get_status(pjpidf_tuple *t)
+{
+ pjpidf_status *st = (pjpidf_status*)pj_xml_find_node(t, &STATUS);
+ pj_assert(st);
+ return st;
+}
+
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact(const pjpidf_tuple *t)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
+ if (!node)
+ return &EMPTY_STRING;
+ return &node->content;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_contact(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *contact)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &CONTACT, contact);
+ pj_xml_add_node(t, node);
+ } else {
+ pj_strdup(pool, &node->content, contact);
+ }
+}
+
+PJ_DEF(void) pjpidf_tuple_set_contact_prio(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *prio)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &CONTACT);
+ pj_xml_attr *attr;
+
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &CONTACT, NULL);
+ pj_xml_add_node(t, node);
+ }
+ attr = pj_xml_find_attr(node, &PRIORITY, NULL);
+ if (!attr) {
+ attr = xml_create_attr(pool, &PRIORITY, prio);
+ pj_xml_add_attr(node, attr);
+ } else {
+ pj_strdup(pool, &attr->value, prio);
+ }
+}
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_contact_prio(const pjpidf_tuple *t)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &CONTACT);
+ pj_xml_attr *attr;
+
+ if (!node)
+ return &EMPTY_STRING;
+ attr = pj_xml_find_attr(node, &PRIORITY, NULL);
+ if (!attr)
+ return &EMPTY_STRING;
+ return &attr->value;
+}
+
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_add_note(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *text)
+{
+ pjpidf_note *note = pj_pool_alloc(pool, sizeof(*note));
+ xml_init_node(pool, note, &NOTE, text);
+ pj_xml_add_node(t, note);
+ return note;
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_get_first_note(pjpidf_tuple *t)
+{
+ return pj_xml_find_node(t, &NOTE);
+}
+
+PJ_DEF(pjpidf_note*) pjpidf_tuple_get_next_note(pjpidf_tuple *t, pjpidf_note *n)
+{
+ return pj_xml_find_next_node(t, n, &NOTE);
+}
+
+
+PJ_DEF(const pj_str_t*) pjpidf_tuple_get_timestamp(const pjpidf_tuple *t)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)t, &TIMESTAMP);
+ return node ? &node->content : &EMPTY_STRING;
+}
+
+PJ_DEF(void) pjpidf_tuple_set_timestamp(pj_pool_t *pool, pjpidf_tuple *t,
+ const pj_str_t *ts)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &TIMESTAMP, ts);
+ } else {
+ pj_strdup(pool, &node->content, ts);
+ }
+}
+
+
+PJ_DEF(void) pjpidf_tuple_set_timestamp_np(pj_pool_t *pool, pjpidf_tuple *t,
+ pj_str_t *ts)
+{
+ pj_xml_node *node = pj_xml_find_node(t, &TIMESTAMP);
+ if (!node) {
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &TIMESTAMP, ts);
+ } else {
+ node->content = *ts;
+ }
+}
+
+
+/* Status */
+PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st)
+{
+ pj_xml_node *node;
+
+ xml_init_node(pool, st, &STATUS, NULL);
+ node = pj_pool_alloc(pool, sizeof(*node));
+ xml_init_node(pool, node, &BASIC, &CLOSED);
+ pj_xml_add_node(st, node);
+}
+
+PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st)
+{
+ pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC);
+ pj_assert(node != NULL);
+ return pj_stricmp(&node->content, &OPEN)==0;
+}
+
+PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open)
+{
+ pj_xml_node *node = pj_xml_find_node(st, &BASIC);
+ pj_assert(node != NULL);
+ node->content = open ? OPEN : CLOSED;
+}
+
+PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity)
+{
+ pjpidf_pres *pres = pj_pool_alloc(pool, sizeof(*pres));
+ pjpidf_pres_construct(pool, pres, entity);
+ return pres;
+}
+
+PJ_DEF(pjpidf_pres*) pjpidf_parse(pj_pool_t *pool, char *text, int len)
+{
+ pjpidf_pres *pres = pj_xml_parse(pool, text, len);
+ if (pres) {
+ if (pj_stricmp(&pres->name, &PRESENCE) != 0)
+ return NULL;
+ }
+ return pres;
+}
+
+PJ_DEF(int) pjpidf_print(const pjpidf_pres* pres, char *buf, int len)
+{
+ return pj_xml_print(pres, buf, len, PJ_TRUE);
+}
+
diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c index 32ed8e3c..5f5893ac 100644 --- a/pjsip/src/pjsip-simple/presence.c +++ b/pjsip/src/pjsip-simple/presence.c @@ -1,384 +1,406 @@ -/* $Id$ - * - */ -#include <pjsip_simple/presence.h> -#include <pjsip/sip_transport.h> -#include <pj/pool.h> -#include <pj/string.h> -#include <pj/guid.h> -#include <pj/os.h> -#include <stdio.h> - -/* Forward declarations. */ -static void on_query_subscribe(pjsip_rx_data *rdata, int *status); -static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, - pjsip_event_sub_cb **cb, int *expires); -static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason); -static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata); -static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata); - -/* Some string constants. */ -static pj_str_t PRESENCE_EVENT = { "presence", 8 }; - -/* Accept types. */ -static pj_str_t accept_names[] = { - { "application/pidf+xml", 20 }, - { "application/xpidf+xml", 21 } -}; -static pjsip_media_type accept_types[] = { - { - { "application", 11 }, - { "pidf+xml", 8 } - }, - { - { "application", 11 }, - { "xpidf+xml", 9 } - } -}; - -/* Callback that is registered by application. */ -static pjsip_presence_cb cb; - -/* Package callback to be register to event_notify */ -static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe, - &on_subscribe }; - -/* Global/static callback to be registered to event_notify */ -static pjsip_event_sub_cb sub_cb = { &on_sub_terminated, - &on_sub_received_refresh, - NULL, - &on_received_notify, - NULL }; - -/* - * Initialize presence module. - * This will register event package "presence" to event framework. - */ -PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb) -{ - pj_memcpy(&cb, pcb, sizeof(*pcb)); - pjsip_event_sub_register_pkg( &PRESENCE_EVENT, - sizeof(accept_names)/sizeof(accept_names[0]), - accept_names, - &pkg_cb); -} - -/* - * Create presence subscription. - */ -PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, - const pj_str_t *local_url, - const pj_str_t *remote_url, - int expires, - void *user_data ) -{ - pjsip_event_sub *sub; - pjsip_presentity *pres; - - if (expires < 0) - expires = 300; - - /* Create event subscription */ - sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT, - expires, - sizeof(accept_names)/sizeof(accept_names[0]), - accept_names, - NULL, &sub_cb); - if (!sub) - return NULL; - - /* Allocate presence descriptor. */ - pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); - pres->sub = sub; - pres->user_data = user_data; - sub->user_data = pres; - - return pres; -} - -/* - * Send SUBSCRIBE. - */ -PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ) -{ - return pjsip_event_sub_subscribe( pres->sub ); -} - -/* - * Set credentials to be used for outgoing requests. - */ -PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, - int count, - const pjsip_cred_info cred[]) -{ - return pjsip_event_sub_set_credentials(pres->sub, count, cred); -} - -/* - * Set route-set. - */ -PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, - const pjsip_route_hdr *hdr ) -{ - return pjsip_event_sub_set_route_set( pres->sub, hdr ); -} - -/* - * Unsubscribe. - */ -PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ) -{ - return pjsip_event_sub_unsubscribe(pres->sub); -} - -/* - * This is the pjsip_msg_body callback to print XML body. - */ -static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size) -{ - return pj_xml_print( body->data, buf, size, PJ_TRUE ); -} - -/* - * Create and initialize PIDF document and msg body (notifier only). - */ -static pj_status_t init_presence_info( pjsip_presentity *pres ) -{ - pj_str_t uri; - pj_pool_t *pool = pres->sub->pool; - char tmp[PJSIP_MAX_URL_SIZE]; - pjpidf_tuple *tuple; - const pjsip_media_type *content_type = NULL; - - pj_assert(pres->uas_body == NULL); - - /* Make entity_id */ - uri.ptr = tmp; - uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri, - tmp, sizeof(tmp)); - if (uri.slen < 0) - return -1; - - if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { - pj_str_t s; - - /* Create <presence>. */ - pres->uas_data.pidf = pjpidf_create(pool, &s); - - /* Create <tuple> */ - pj_create_unique_string(pool, &s); - tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s); - - /* Set <contact> */ - s.ptr = tmp; - s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp)); - if (s.slen < 0) - return -1; - pjpidf_tuple_set_contact(pool, tuple, &s); - - /* Content-Type */ - content_type = &accept_types[PJSIP_PRES_TYPE_PIDF]; - - } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { - - /* Create XPIDF */ - pres->uas_data.xpidf = pjxpidf_create(pool, &uri); - - /* Content-Type. */ - content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF]; - } - - /* Create message body */ - pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); - pres->uas_body->content_type = *content_type; - pres->uas_body->data = pres->uas_data.pidf; - pres->uas_body->len = 0; - pres->uas_body->print_body = &print_xml; - - return 0; -} - -/* - * Send NOTIFY and set subscription state. - */ -PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, - pjsip_event_sub_state state, - pj_bool_t is_online ) -{ - pj_str_t reason = { "", 0 }; - - if (pres->uas_data.pidf == NULL) { - if (init_presence_info(pres) != 0) - return -1; - } - - /* Update basic status in PIDF/XPIDF document. */ - if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { - pjpidf_tuple *first; - pjpidf_status *status; - pj_time_val now; - pj_parsed_time pnow; - - first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf); - pj_assert(first); - status = pjpidf_op.tuple.get_status(first); - pj_assert(status); - pjpidf_op.status.set_basic_open(status, is_online); - - /* Update timestamp. */ - if (pres->timestamp.ptr == 0) { - pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24); - } - pj_gettimeofday(&now); - pj_time_decode(&now, &pnow); - pres->timestamp.slen = sprintf(pres->timestamp.ptr, - "%04d-%02d-%02dT%02d:%02d:%02dZ", - pnow.year, pnow.mon, pnow.day, - pnow.hour, pnow.min, pnow.sec); - pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp); - - } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { - pjxpidf_set_status( pres->uas_data.xpidf, is_online ); - - } else { - pj_assert(0); - } - - /* Send notify. */ - return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body); -} - -/* - * Destroy subscription (can be called for both subscriber and notifier). - */ -PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ) -{ - return pjsip_event_sub_destroy(pres->sub); -} - -/* - * This callback is called by event framework to query whether we want to - * accept an incoming subscription. - */ -static void on_query_subscribe(pjsip_rx_data *rdata, int *status) -{ - if (cb.accept_presence) { - (*cb.accept_presence)(rdata, status); - } -} - -/* - * This callback is called by event framework after we accept the incoming - * subscription, to notify about the new subscription instance. - */ -static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, - pjsip_event_sub_cb **set_sub_cb, int *expires) -{ - pjsip_presentity *pres; - pjsip_accept_hdr *accept; - - pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); - pres->sub = sub; - pres->pres_type = PJSIP_PRES_TYPE_PIDF; - sub->user_data = pres; - *set_sub_cb = &sub_cb; - - accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); - if (accept) { - unsigned i; - int found = 0; - for (i=0; i<accept->count && !found; ++i) { - int j; - for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) { - if (!pj_stricmp(&accept->values[i], &accept_names[j])) { - pres->pres_type = j; - found = 1; - break; - } - } - } - pj_assert(found ); - } - - (*cb.on_received_request)(pres, rdata, expires); -} - -/* - * This callback is called by event framework when the subscription is - * terminated. - */ -static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason) -{ - pjsip_presentity *pres = sub->user_data; - if (cb.on_terminated) - (*cb.on_terminated)(pres, reason); -} - -/* - * This callback is called by event framework when it receives incoming - * SUBSCRIBE request to refresh the subscription. - */ -static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata) -{ - pjsip_presentity *pres = sub->user_data; - if (cb.on_received_refresh) - (*cb.on_received_refresh)(pres, rdata); -} - -/* - * This callback is called by event framework when it receives incoming - * NOTIFY request. - */ -static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata) -{ - pjsip_presentity *pres = sub->user_data; - - if (cb.on_received_update) { - pj_status_t is_open; - pjsip_msg_body *body; - int i; - - body = rdata->msg->body; - if (!body) - return; - - for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) { - if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) && - !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype)) - { - break; - } - } - - if (i==PJSIP_PRES_TYPE_PIDF) { - pjpidf_pres *pres; - pjpidf_tuple *tuple; - pjpidf_status *status; - - pres = pjpidf_parse(rdata->pool, body->data, body->len); - if (!pres) - return; - tuple = pjpidf_pres_get_first_tuple(pres); - if (!tuple) - return; - status = pjpidf_tuple_get_status(tuple); - if (!status) - return; - is_open = pjpidf_status_is_basic_open(status); - - } else if (i==PJSIP_PRES_TYPE_XPIDF) { - pjxpidf_pres *pres; - - pres = pjxpidf_parse(rdata->pool, body->data, body->len); - if (!pres) - return; - is_open = pjxpidf_get_status(pres); - - } else { - return; - } - - (*cb.on_received_update)(pres, is_open); - } -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_simple/presence.h>
+#include <pjsip/sip_transport.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <stdio.h>
+
+/* Forward declarations. */
+static void on_query_subscribe(pjsip_rx_data *rdata, int *status);
+static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+ pjsip_event_sub_cb **cb, int *expires);
+static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason);
+static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata);
+
+/* Some string constants. */
+static pj_str_t PRESENCE_EVENT = { "presence", 8 };
+
+/* Accept types. */
+static pj_str_t accept_names[] = {
+ { "application/pidf+xml", 20 },
+ { "application/xpidf+xml", 21 }
+};
+static pjsip_media_type accept_types[] = {
+ {
+ { "application", 11 },
+ { "pidf+xml", 8 }
+ },
+ {
+ { "application", 11 },
+ { "xpidf+xml", 9 }
+ }
+};
+
+/* Callback that is registered by application. */
+static pjsip_presence_cb cb;
+
+/* Package callback to be register to event_notify */
+static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe,
+ &on_subscribe };
+
+/* Global/static callback to be registered to event_notify */
+static pjsip_event_sub_cb sub_cb = { &on_sub_terminated,
+ &on_sub_received_refresh,
+ NULL,
+ &on_received_notify,
+ NULL };
+
+/*
+ * Initialize presence module.
+ * This will register event package "presence" to event framework.
+ */
+PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb)
+{
+ pj_memcpy(&cb, pcb, sizeof(*pcb));
+ pjsip_event_sub_register_pkg( &PRESENCE_EVENT,
+ sizeof(accept_names)/sizeof(accept_names[0]),
+ accept_names,
+ &pkg_cb);
+}
+
+/*
+ * Create presence subscription.
+ */
+PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
+ const pj_str_t *local_url,
+ const pj_str_t *remote_url,
+ int expires,
+ void *user_data )
+{
+ pjsip_event_sub *sub;
+ pjsip_presentity *pres;
+
+ if (expires < 0)
+ expires = 300;
+
+ /* Create event subscription */
+ sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT,
+ expires,
+ sizeof(accept_names)/sizeof(accept_names[0]),
+ accept_names,
+ NULL, &sub_cb);
+ if (!sub)
+ return NULL;
+
+ /* Allocate presence descriptor. */
+ pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
+ pres->sub = sub;
+ pres->user_data = user_data;
+ sub->user_data = pres;
+
+ return pres;
+}
+
+/*
+ * Send SUBSCRIBE.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres )
+{
+ return pjsip_event_sub_subscribe( pres->sub );
+}
+
+/*
+ * Set credentials to be used for outgoing requests.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
+ int count,
+ const pjsip_cred_info cred[])
+{
+ return pjsip_event_sub_set_credentials(pres->sub, count, cred);
+}
+
+/*
+ * Set route-set.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
+ const pjsip_route_hdr *hdr )
+{
+ return pjsip_event_sub_set_route_set( pres->sub, hdr );
+}
+
+/*
+ * Unsubscribe.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres )
+{
+ return pjsip_event_sub_unsubscribe(pres->sub);
+}
+
+/*
+ * This is the pjsip_msg_body callback to print XML body.
+ */
+static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size)
+{
+ return pj_xml_print( body->data, buf, size, PJ_TRUE );
+}
+
+/*
+ * Create and initialize PIDF document and msg body (notifier only).
+ */
+static pj_status_t init_presence_info( pjsip_presentity *pres )
+{
+ pj_str_t uri;
+ pj_pool_t *pool = pres->sub->pool;
+ char tmp[PJSIP_MAX_URL_SIZE];
+ pjpidf_tuple *tuple;
+ const pjsip_media_type *content_type = NULL;
+
+ pj_assert(pres->uas_body == NULL);
+
+ /* Make entity_id */
+ uri.ptr = tmp;
+ uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri,
+ tmp, sizeof(tmp));
+ if (uri.slen < 0)
+ return -1;
+
+ if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
+ pj_str_t s;
+
+ /* Create <presence>. */
+ pres->uas_data.pidf = pjpidf_create(pool, &s);
+
+ /* Create <tuple> */
+ pj_create_unique_string(pool, &s);
+ tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s);
+
+ /* Set <contact> */
+ s.ptr = tmp;
+ s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp));
+ if (s.slen < 0)
+ return -1;
+ pjpidf_tuple_set_contact(pool, tuple, &s);
+
+ /* Content-Type */
+ content_type = &accept_types[PJSIP_PRES_TYPE_PIDF];
+
+ } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
+
+ /* Create XPIDF */
+ pres->uas_data.xpidf = pjxpidf_create(pool, &uri);
+
+ /* Content-Type. */
+ content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF];
+ }
+
+ /* Create message body */
+ pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
+ pres->uas_body->content_type = *content_type;
+ pres->uas_body->data = pres->uas_data.pidf;
+ pres->uas_body->len = 0;
+ pres->uas_body->print_body = &print_xml;
+
+ return 0;
+}
+
+/*
+ * Send NOTIFY and set subscription state.
+ */
+PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
+ pjsip_event_sub_state state,
+ pj_bool_t is_online )
+{
+ pj_str_t reason = { "", 0 };
+
+ if (pres->uas_data.pidf == NULL) {
+ if (init_presence_info(pres) != 0)
+ return -1;
+ }
+
+ /* Update basic status in PIDF/XPIDF document. */
+ if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
+ pjpidf_tuple *first;
+ pjpidf_status *status;
+ pj_time_val now;
+ pj_parsed_time pnow;
+
+ first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf);
+ pj_assert(first);
+ status = pjpidf_op.tuple.get_status(first);
+ pj_assert(status);
+ pjpidf_op.status.set_basic_open(status, is_online);
+
+ /* Update timestamp. */
+ if (pres->timestamp.ptr == 0) {
+ pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24);
+ }
+ pj_gettimeofday(&now);
+ pj_time_decode(&now, &pnow);
+ pres->timestamp.slen = sprintf(pres->timestamp.ptr,
+ "%04d-%02d-%02dT%02d:%02d:%02dZ",
+ pnow.year, pnow.mon, pnow.day,
+ pnow.hour, pnow.min, pnow.sec);
+ pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp);
+
+ } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
+ pjxpidf_set_status( pres->uas_data.xpidf, is_online );
+
+ } else {
+ pj_assert(0);
+ }
+
+ /* Send notify. */
+ return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body);
+}
+
+/*
+ * Destroy subscription (can be called for both subscriber and notifier).
+ */
+PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres )
+{
+ return pjsip_event_sub_destroy(pres->sub);
+}
+
+/*
+ * This callback is called by event framework to query whether we want to
+ * accept an incoming subscription.
+ */
+static void on_query_subscribe(pjsip_rx_data *rdata, int *status)
+{
+ if (cb.accept_presence) {
+ (*cb.accept_presence)(rdata, status);
+ }
+}
+
+/*
+ * This callback is called by event framework after we accept the incoming
+ * subscription, to notify about the new subscription instance.
+ */
+static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
+ pjsip_event_sub_cb **set_sub_cb, int *expires)
+{
+ pjsip_presentity *pres;
+ pjsip_accept_hdr *accept;
+
+ pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
+ pres->sub = sub;
+ pres->pres_type = PJSIP_PRES_TYPE_PIDF;
+ sub->user_data = pres;
+ *set_sub_cb = &sub_cb;
+
+ accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
+ if (accept) {
+ unsigned i;
+ int found = 0;
+ for (i=0; i<accept->count && !found; ++i) {
+ int j;
+ for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) {
+ if (!pj_stricmp(&accept->values[i], &accept_names[j])) {
+ pres->pres_type = j;
+ found = 1;
+ break;
+ }
+ }
+ }
+ pj_assert(found );
+ }
+
+ (*cb.on_received_request)(pres, rdata, expires);
+}
+
+/*
+ * This callback is called by event framework when the subscription is
+ * terminated.
+ */
+static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason)
+{
+ pjsip_presentity *pres = sub->user_data;
+ if (cb.on_terminated)
+ (*cb.on_terminated)(pres, reason);
+}
+
+/*
+ * This callback is called by event framework when it receives incoming
+ * SUBSCRIBE request to refresh the subscription.
+ */
+static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+{
+ pjsip_presentity *pres = sub->user_data;
+ if (cb.on_received_refresh)
+ (*cb.on_received_refresh)(pres, rdata);
+}
+
+/*
+ * This callback is called by event framework when it receives incoming
+ * NOTIFY request.
+ */
+static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+{
+ pjsip_presentity *pres = sub->user_data;
+
+ if (cb.on_received_update) {
+ pj_status_t is_open;
+ pjsip_msg_body *body;
+ int i;
+
+ body = rdata->msg->body;
+ if (!body)
+ return;
+
+ for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) {
+ if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) &&
+ !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype))
+ {
+ break;
+ }
+ }
+
+ if (i==PJSIP_PRES_TYPE_PIDF) {
+ pjpidf_pres *pres;
+ pjpidf_tuple *tuple;
+ pjpidf_status *status;
+
+ pres = pjpidf_parse(rdata->pool, body->data, body->len);
+ if (!pres)
+ return;
+ tuple = pjpidf_pres_get_first_tuple(pres);
+ if (!tuple)
+ return;
+ status = pjpidf_tuple_get_status(tuple);
+ if (!status)
+ return;
+ is_open = pjpidf_status_is_basic_open(status);
+
+ } else if (i==PJSIP_PRES_TYPE_XPIDF) {
+ pjxpidf_pres *pres;
+
+ pres = pjxpidf_parse(rdata->pool, body->data, body->len);
+ if (!pres)
+ return;
+ is_open = pjxpidf_get_status(pres);
+
+ } else {
+ return;
+ }
+
+ (*cb.on_received_update)(pres, is_open);
+ }
+}
+
diff --git a/pjsip/src/pjsip-simple/xpidf.c b/pjsip/src/pjsip-simple/xpidf.c index 5787f674..cc9aaf41 100644 --- a/pjsip/src/pjsip-simple/xpidf.c +++ b/pjsip/src/pjsip-simple/xpidf.c @@ -1,279 +1,301 @@ -/* $Id$ - * - */ -#include <pjsip_simple/xpidf.h> -#include <pj/pool.h> -#include <pj/string.h> -#include <pj/guid.h> - -static pj_str_t PRESENCE = { "presence", 8 }; -static pj_str_t STATUS = { "status", 6 }; -static pj_str_t OPEN = { "open", 4 }; -static pj_str_t CLOSED = { "closed", 6 }; -static pj_str_t URI = { "uri", 3 }; -static pj_str_t ATOM = { "atom", 4 }; -static pj_str_t ATOMID = { "atomid", 6 }; -static pj_str_t ADDRESS = { "address", 7 }; -static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 }; -static pj_str_t PRESENTITY = { "presentity", 10 }; -static pj_str_t EMPTY_STRING = { NULL, 0 }; - -static pj_xml_node* xml_create_node(pj_pool_t *pool, - pj_str_t *name, const pj_str_t *value) -{ - pj_xml_node *node; - - node = pj_pool_alloc(pool, sizeof(pj_xml_node)); - pj_list_init(&node->attr_head); - pj_list_init(&node->node_head); - node->name = *name; - if (value) pj_strdup(pool, &node->content, value); - else node->content.ptr=NULL, node->content.slen=0; - - return node; -} - -static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name, - const pj_str_t *value) -{ - pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr)); - attr->name = *name; - pj_strdup(pool, &attr->value, value); - return attr; -} - - -PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr) -{ - pjxpidf_pres *pres; - pj_xml_node *presentity; - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_node *status; - pj_xml_attr *attr; - pj_str_t uri; - pj_str_t tmp; - - /* <presence> */ - pres = xml_create_node(pool, &PRESENCE, NULL); - - /* <presentity> */ - presentity = xml_create_node(pool, &PRESENTITY, NULL); - pj_xml_add_node(pres, presentity); - - /* uri attribute */ - uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen); - pj_strcpy( &uri, uri_cstr); - pj_strcat( &uri, &SUBSCRIBE_PARAM); - attr = xml_create_attr(pool, &URI, &uri); - pj_xml_add_attr(presentity, attr); - - /* <atom> */ - atom = xml_create_node(pool, &ATOM, NULL); - pj_xml_add_node(pres, atom); - - /* atom id */ - pj_create_unique_string(pool, &tmp); - attr = xml_create_attr(pool, &ATOMID, &tmp); - pj_xml_add_attr(atom, attr); - - /* address */ - addr = xml_create_node(pool, &ADDRESS, NULL); - pj_xml_add_node(atom, addr); - - /* address'es uri */ - attr = xml_create_attr(pool, &URI, uri_cstr); - pj_xml_add_attr(addr, attr); - - /* status */ - status = xml_create_node(pool, &STATUS, NULL); - pj_xml_add_node(addr, status); - - /* status attr */ - attr = xml_create_attr(pool, &STATUS, &OPEN); - pj_xml_add_attr(status, attr); - - return pres; -} - - - -PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len) -{ - pjxpidf_pres *pres; - pj_xml_node *node; - - pres = pj_xml_parse(pool, text, len); - if (!pres) - return NULL; - - /* Validate <presence> */ - if (pj_stricmp(&pres->name, &PRESENCE) != 0) - return NULL; - if (pj_xml_find_attr(pres, &URI, NULL) == NULL) - return NULL; - - /* Validate <presentity> */ - node = pj_xml_find_node(pres, &PRESENTITY); - if (node == NULL) - return NULL; - - /* Validate <atom> */ - node = pj_xml_find_node(pres, &ATOM); - if (node == NULL) - return NULL; - if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL) - return NULL; - - /* Address */ - node = pj_xml_find_node(node, &ADDRESS); - if (node == NULL) - return NULL; - if (pj_xml_find_attr(node, &URI, NULL) == NULL) - return NULL; - - - /* Status */ - node = pj_xml_find_node(node, &STATUS); - if (node == NULL) - return NULL; - if (pj_xml_find_attr(node, &STATUS, NULL) == NULL) - return NULL; - - return pres; -} - - -PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len) -{ - return pj_xml_print(pres, text, len, PJ_TRUE); -} - - -PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres) -{ - pj_xml_node *presentity; - pj_xml_attr *attr; - - presentity = pj_xml_find_node(pres, &PRESENTITY); - if (!presentity) - return &EMPTY_STRING; - - attr = pj_xml_find_attr(presentity, &URI, NULL); - if (!attr) - return &EMPTY_STRING; - - return &attr->value; -} - - -PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres, - const pj_str_t *uri) -{ - pj_xml_node *presentity; - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_attr *attr; - pj_str_t dup_uri; - - presentity = pj_xml_find_node(pres, &PRESENTITY); - if (!presentity) { - pj_assert(0); - return -1; - } - atom = pj_xml_find_node(pres, &ATOM); - if (!atom) { - pj_assert(0); - return -1; - } - addr = pj_xml_find_node(atom, &ADDRESS); - if (!addr) { - pj_assert(0); - return -1; - } - - /* Set uri in presentity */ - attr = pj_xml_find_attr(presentity, &URI, NULL); - if (!attr) { - pj_assert(0); - return -1; - } - pj_strdup(pool, &dup_uri, uri); - attr->value = dup_uri; - - /* Set uri in address. */ - attr = pj_xml_find_attr(addr, &URI, NULL); - if (!attr) { - pj_assert(0); - return -1; - } - attr->value = dup_uri; - - return 0; -} - - -PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres) -{ - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_node *status; - pj_xml_attr *attr; - - atom = pj_xml_find_node(pres, &ATOM); - if (!atom) { - pj_assert(0); - return PJ_FALSE; - } - addr = pj_xml_find_node(atom, &ADDRESS); - if (!addr) { - pj_assert(0); - return PJ_FALSE; - } - status = pj_xml_find_node(atom, &STATUS); - if (!status) { - pj_assert(0); - return PJ_FALSE; - } - attr = pj_xml_find_attr(status, &STATUS, NULL); - if (!attr) { - pj_assert(0); - return PJ_FALSE; - } - - return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE; -} - - -PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status) -{ - pj_xml_node *atom; - pj_xml_node *addr; - pj_xml_node *status; - pj_xml_attr *attr; - - atom = pj_xml_find_node(pres, &ATOM); - if (!atom) { - pj_assert(0); - return -1; - } - addr = pj_xml_find_node(atom, &ADDRESS); - if (!addr) { - pj_assert(0); - return -1; - } - status = pj_xml_find_node(addr, &STATUS); - if (!status) { - pj_assert(0); - return -1; - } - attr = pj_xml_find_attr(status, &STATUS, NULL); - if (!attr) { - pj_assert(0); - return -1; - } - - attr->value = ( online_status ? OPEN : CLOSED ); - return 0; -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_simple/xpidf.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+
+static pj_str_t PRESENCE = { "presence", 8 };
+static pj_str_t STATUS = { "status", 6 };
+static pj_str_t OPEN = { "open", 4 };
+static pj_str_t CLOSED = { "closed", 6 };
+static pj_str_t URI = { "uri", 3 };
+static pj_str_t ATOM = { "atom", 4 };
+static pj_str_t ATOMID = { "atomid", 6 };
+static pj_str_t ADDRESS = { "address", 7 };
+static pj_str_t SUBSCRIBE_PARAM = { ";method=SUBSCRIBE", 17 };
+static pj_str_t PRESENTITY = { "presentity", 10 };
+static pj_str_t EMPTY_STRING = { NULL, 0 };
+
+static pj_xml_node* xml_create_node(pj_pool_t *pool,
+ pj_str_t *name, const pj_str_t *value)
+{
+ pj_xml_node *node;
+
+ node = pj_pool_alloc(pool, sizeof(pj_xml_node));
+ pj_list_init(&node->attr_head);
+ pj_list_init(&node->node_head);
+ node->name = *name;
+ if (value) pj_strdup(pool, &node->content, value);
+ else node->content.ptr=NULL, node->content.slen=0;
+
+ return node;
+}
+
+static pj_xml_attr* xml_create_attr(pj_pool_t *pool, pj_str_t *name,
+ const pj_str_t *value)
+{
+ pj_xml_attr *attr = pj_pool_alloc(pool, sizeof(*attr));
+ attr->name = *name;
+ pj_strdup(pool, &attr->value, value);
+ return attr;
+}
+
+
+PJ_DEF(pjxpidf_pres*) pjxpidf_create(pj_pool_t *pool, const pj_str_t *uri_cstr)
+{
+ pjxpidf_pres *pres;
+ pj_xml_node *presentity;
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_node *status;
+ pj_xml_attr *attr;
+ pj_str_t uri;
+ pj_str_t tmp;
+
+ /* <presence> */
+ pres = xml_create_node(pool, &PRESENCE, NULL);
+
+ /* <presentity> */
+ presentity = xml_create_node(pool, &PRESENTITY, NULL);
+ pj_xml_add_node(pres, presentity);
+
+ /* uri attribute */
+ uri.ptr = pj_pool_alloc(pool, uri_cstr->slen + SUBSCRIBE_PARAM.slen);
+ pj_strcpy( &uri, uri_cstr);
+ pj_strcat( &uri, &SUBSCRIBE_PARAM);
+ attr = xml_create_attr(pool, &URI, &uri);
+ pj_xml_add_attr(presentity, attr);
+
+ /* <atom> */
+ atom = xml_create_node(pool, &ATOM, NULL);
+ pj_xml_add_node(pres, atom);
+
+ /* atom id */
+ pj_create_unique_string(pool, &tmp);
+ attr = xml_create_attr(pool, &ATOMID, &tmp);
+ pj_xml_add_attr(atom, attr);
+
+ /* address */
+ addr = xml_create_node(pool, &ADDRESS, NULL);
+ pj_xml_add_node(atom, addr);
+
+ /* address'es uri */
+ attr = xml_create_attr(pool, &URI, uri_cstr);
+ pj_xml_add_attr(addr, attr);
+
+ /* status */
+ status = xml_create_node(pool, &STATUS, NULL);
+ pj_xml_add_node(addr, status);
+
+ /* status attr */
+ attr = xml_create_attr(pool, &STATUS, &OPEN);
+ pj_xml_add_attr(status, attr);
+
+ return pres;
+}
+
+
+
+PJ_DEF(pjxpidf_pres*) pjxpidf_parse(pj_pool_t *pool, char *text, pj_size_t len)
+{
+ pjxpidf_pres *pres;
+ pj_xml_node *node;
+
+ pres = pj_xml_parse(pool, text, len);
+ if (!pres)
+ return NULL;
+
+ /* Validate <presence> */
+ if (pj_stricmp(&pres->name, &PRESENCE) != 0)
+ return NULL;
+ if (pj_xml_find_attr(pres, &URI, NULL) == NULL)
+ return NULL;
+
+ /* Validate <presentity> */
+ node = pj_xml_find_node(pres, &PRESENTITY);
+ if (node == NULL)
+ return NULL;
+
+ /* Validate <atom> */
+ node = pj_xml_find_node(pres, &ATOM);
+ if (node == NULL)
+ return NULL;
+ if (pj_xml_find_attr(node, &ATOMID, NULL) == NULL)
+ return NULL;
+
+ /* Address */
+ node = pj_xml_find_node(node, &ADDRESS);
+ if (node == NULL)
+ return NULL;
+ if (pj_xml_find_attr(node, &URI, NULL) == NULL)
+ return NULL;
+
+
+ /* Status */
+ node = pj_xml_find_node(node, &STATUS);
+ if (node == NULL)
+ return NULL;
+ if (pj_xml_find_attr(node, &STATUS, NULL) == NULL)
+ return NULL;
+
+ return pres;
+}
+
+
+PJ_DEF(int) pjxpidf_print( pjxpidf_pres *pres, char *text, pj_size_t len)
+{
+ return pj_xml_print(pres, text, len, PJ_TRUE);
+}
+
+
+PJ_DEF(pj_str_t*) pjxpidf_get_uri(pjxpidf_pres *pres)
+{
+ pj_xml_node *presentity;
+ pj_xml_attr *attr;
+
+ presentity = pj_xml_find_node(pres, &PRESENTITY);
+ if (!presentity)
+ return &EMPTY_STRING;
+
+ attr = pj_xml_find_attr(presentity, &URI, NULL);
+ if (!attr)
+ return &EMPTY_STRING;
+
+ return &attr->value;
+}
+
+
+PJ_DEF(pj_status_t) pjxpidf_set_uri(pj_pool_t *pool, pjxpidf_pres *pres,
+ const pj_str_t *uri)
+{
+ pj_xml_node *presentity;
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_attr *attr;
+ pj_str_t dup_uri;
+
+ presentity = pj_xml_find_node(pres, &PRESENTITY);
+ if (!presentity) {
+ pj_assert(0);
+ return -1;
+ }
+ atom = pj_xml_find_node(pres, &ATOM);
+ if (!atom) {
+ pj_assert(0);
+ return -1;
+ }
+ addr = pj_xml_find_node(atom, &ADDRESS);
+ if (!addr) {
+ pj_assert(0);
+ return -1;
+ }
+
+ /* Set uri in presentity */
+ attr = pj_xml_find_attr(presentity, &URI, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return -1;
+ }
+ pj_strdup(pool, &dup_uri, uri);
+ attr->value = dup_uri;
+
+ /* Set uri in address. */
+ attr = pj_xml_find_attr(addr, &URI, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return -1;
+ }
+ attr->value = dup_uri;
+
+ return 0;
+}
+
+
+PJ_DEF(pj_bool_t) pjxpidf_get_status(pjxpidf_pres *pres)
+{
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_node *status;
+ pj_xml_attr *attr;
+
+ atom = pj_xml_find_node(pres, &ATOM);
+ if (!atom) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+ addr = pj_xml_find_node(atom, &ADDRESS);
+ if (!addr) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+ status = pj_xml_find_node(atom, &STATUS);
+ if (!status) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+ attr = pj_xml_find_attr(status, &STATUS, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+
+ return pj_stricmp(&attr->value, &OPEN) ? PJ_TRUE : PJ_FALSE;
+}
+
+
+PJ_DEF(pj_status_t) pjxpidf_set_status(pjxpidf_pres *pres, pj_bool_t online_status)
+{
+ pj_xml_node *atom;
+ pj_xml_node *addr;
+ pj_xml_node *status;
+ pj_xml_attr *attr;
+
+ atom = pj_xml_find_node(pres, &ATOM);
+ if (!atom) {
+ pj_assert(0);
+ return -1;
+ }
+ addr = pj_xml_find_node(atom, &ADDRESS);
+ if (!addr) {
+ pj_assert(0);
+ return -1;
+ }
+ status = pj_xml_find_node(addr, &STATUS);
+ if (!status) {
+ pj_assert(0);
+ return -1;
+ }
+ attr = pj_xml_find_attr(status, &STATUS, NULL);
+ if (!attr) {
+ pj_assert(0);
+ return -1;
+ }
+
+ attr->value = ( online_status ? OPEN : CLOSED );
+ return 0;
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_dialog.c b/pjsip/src/pjsip-ua/sip_dialog.c index 0c9db5ec..ade94b29 100644 --- a/pjsip/src/pjsip-ua/sip_dialog.c +++ b/pjsip/src/pjsip-ua/sip_dialog.c @@ -1,1786 +1,1808 @@ -/* $Id$ - * - */ -#include <pjsip_mod_ua/sip_dialog.h> -#include <pjsip_mod_ua/sip_ua.h> -#include <pjsip_mod_ua/sip_ua_private.h> -#include <pjsip/sip_transport.h> -#include <pjsip/sip_transaction.h> -#include <pjsip/sip_types.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_uri.h> -#include <pjsip/sip_misc.h> -#include <pjsip/sip_module.h> -#include <pjsip/sip_event.h> -#include <pjsip/sip_parser.h> -#include <pj/string.h> -#include <pj/log.h> -#include <pj/os.h> -#include <pj/guid.h> -#include <pj/except.h> -#include <pj/pool.h> - -/* TLS to keep dialog lock record (initialized by UA) */ -int pjsip_dlg_lock_tls_id; - -struct dialog_lock_data -{ - struct dialog_lock_data *prev; - pjsip_dlg *dlg; - int is_alive; -}; - -/* - * Static function prototypes. - */ -static void dlg_create_request_throw( pjsip_tx_data **p_tdata, - pjsip_dlg *dlg, - const pjsip_method *method, - int cseq ); -static int dlg_on_all_state_pre( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_all_state_post( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_null( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_incoming( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_calling( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_proceeding( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_connecting( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_established( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_disconnected( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); -static int dlg_on_state_terminated( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); - -/* - * Dialog state handlers. - */ -static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *, - pjsip_event *) = -{ - &dlg_on_state_null, - &dlg_on_state_incoming, - &dlg_on_state_calling, - &dlg_on_state_proceeding, - &dlg_on_state_connecting, - &dlg_on_state_established, - &dlg_on_state_disconnected, - &dlg_on_state_terminated, -}; - -/* - * Dialog state names. - */ -static const char* dlg_state_names[] = -{ - "STATE_NULL", - "STATE_INCOMING", - "STATE_CALLING", - "STATE_PROCEEDING", - "STATE_CONNECTING", - "STATE_ESTABLISHED", - "STATE_DISCONNECTED", - "STATE_TERMINATED", -}; - - -/* - * Get the dialog string state, normally for logging purpose. - */ -const char *pjsip_dlg_state_str(pjsip_dlg_state_e state) -{ - return dlg_state_names[state]; -} - -/* Lock dialog mutex. */ -static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) -{ - struct dialog_lock_data *prev; - - pj_mutex_lock(dlg->mutex); - prev = pj_thread_local_get(pjsip_dlg_lock_tls_id); - lck->prev = prev; - lck->dlg = dlg; - lck->is_alive = 1; - pj_thread_local_set(pjsip_dlg_lock_tls_id, lck); -} - -/* Carefully unlock dialog mutex, protect against situation when the dialog - * has already been destroyed. - */ -static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck) -{ - pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck); - pj_assert(dlg == lck->dlg); - - pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev); - if (lck->is_alive) - pj_mutex_unlock(dlg->mutex); - - return lck->is_alive ? 0 : -1; -} - -/* - * This is called by dialog's FSM to change dialog's state. - */ -static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state, - pjsip_event *event) -{ - PJ_UNUSED_ARG(event); - - PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)", - pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state), - event ? pjsip_event_str(event->type) : "", - event ? pjsip_event_str(event->src_type) : "", - event ? event->src.data : NULL)); - - dlg->state = state; - dlg->handle_tsx_event = dlg_state_handlers[state]; -} - -/* - * Invoke dialog's callback. - * This function is called by dialog's FSM, and interpret the event and call - * the corresponding callback registered by application. - */ -static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event, - pjsip_event *event ) -{ - pjsip_dlg_callback *cb = dlg->ua->dlg_cb; - if (!cb) { - PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered).")); - return; - } - - /* Low level event: call the all-events callback. */ - if (cb->on_all_events) { - (*cb->on_all_events)(dlg, dlg_event, event); - } - - /* Low level event: call the tx/rx callback if this is a tx/rx event. */ - if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx) - { - (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data); - } - else if (event->type == PJSIP_EVENT_TX_MSG && - event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg) - { - (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata); - } - else if (event->type == PJSIP_EVENT_RX_MSG && - event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) { - (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata); - } - - /* Now trigger high level events. - * High level event should only occurs when dialog's state has changed, - * except for on_provisional, which may be called multiple times whenever - * response message is sent - */ - if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING && - (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) && - event->obj.tsx == dlg->invite_tsx) - { - /* Sent/received provisional responses. */ - if (cb->on_provisional) - (*cb->on_provisional)(dlg, event->obj.tsx, event); - } - - if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) { - if (cb->on_mid_call_events) - (*cb->on_mid_call_events)(dlg, event); - return; - } - - if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED) - return; - - if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) { - - /* New incoming dialog. */ - pj_assert (event->src_type == PJSIP_EVENT_RX_MSG); - (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata); - - } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) { - - /* Dialog has just sent the first INVITE. */ - if (cb->on_calling) { - (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata); - } - - } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) { - - if (cb->on_disconnected) - (*cb->on_disconnected)(dlg, event); - - } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) { - - if (cb->on_terminated) - (*cb->on_terminated)(dlg); - - pjsip_ua_destroy_dialog(dlg); - - } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) { - - if (cb->on_connecting) - (*cb->on_connecting)(dlg, event); - - } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) { - - if (cb->on_established) - (*cb->on_established)(dlg, event); - } -} - -/* - * This callback receives event from the transaction layer (via User Agent), - * or from dialog timer (for 200/INVITE or ACK retransmission). - */ -void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - int status = 0; - struct dialog_lock_data lck; - - PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)", - pjsip_dlg_state_str(dlg->state), - pjsip_event_str(event->type), - pjsip_event_str(event->src_type))); - - - lock_dialog(dlg, &lck); - - status = dlg_on_all_state_pre( dlg, tsx, event); - - if (status==0) { - status = (*dlg->handle_tsx_event)(dlg, tsx, event); - } - if (status==0) { - status = dlg_on_all_state_post( dlg, tsx, event); - } - - unlock_dialog(dlg, &lck); -} - -/* - * This function contains common processing in all states, and it is called - * before the FSM is invoked. - */ -static int dlg_on_all_state_pre( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(event) - - if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED) - return 0; - - if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING || - tsx->state==PJSIP_TSX_STATE_TRYING)) - { - ++dlg->pending_tsx_count; - - } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) - { - --dlg->pending_tsx_count; - if (tsx == dlg->invite_tsx) - dlg->invite_tsx = NULL; - } - - if (tsx->method.id == PJSIP_INVITE_METHOD) { - tsx->handle_ack = 1; - } - return 0; -} - - -/* - * This function contains common processing in all states, and it is called - * after the FSM is invoked. - */ -static int dlg_on_all_state_post( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(event) - - if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) { - if (dlg->pending_tsx_count == 0 && - dlg->state != PJSIP_DIALOG_STATE_CONNECTING && - dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED && - dlg->state != PJSIP_DIALOG_STATE_TERMINATED) - { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - return -1; - } - } - - return 0; -} - - -/* - * Internal function to initialize dialog, contains common initialization - * for both UAS and UAC dialog. - */ -static pj_status_t dlg_init( pjsip_dlg *dlg ) -{ - /* Init mutex. */ - dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); - if (!dlg->mutex) { - PJ_PERROR((dlg->obj_name, "pj_mutex_create()")); - return -1; - } - - /* Init route-set (Initially empty) */ - pj_list_init(&dlg->route_set); - - /* Init auth credential list. */ - pj_list_init(&dlg->auth_sess); - - return PJ_SUCCESS; -} - -/* - * This one is called just before dialog is destroyed. - * It is called while mutex is held. - */ -PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ) -{ - struct dialog_lock_data *lck; - - //PJ_TODO(CHECK_THIS); - pj_assert(dlg->pending_tsx_count == 0); - - /* Mark dialog as dead. */ - lck = pj_thread_local_get(pjsip_dlg_lock_tls_id); - while (lck) { - if (lck->dlg == dlg) - lck->is_alive = 0; - lck = lck->prev; - } -} - -/* - * Initialize dialog from the received request. - * This is an internal function which is called by the User Agent (sip_ua.c), - * and it will initialize most of dialog's properties with values from the - * received message. - */ -pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata ) -{ - pjsip_msg *msg = rdata->msg; - pjsip_to_hdr *to; - pjsip_contact_hdr *contact; - pjsip_name_addr *name_addr; - pjsip_url *url; - unsigned flag; - pjsip_event event; - - pj_assert(dlg && rdata); - - PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata)); - - /* Must be an INVITE request. */ - pj_assert(msg->type == PJSIP_REQUEST_MSG && - msg->line.req.method.id == PJSIP_INVITE_METHOD); - - /* Init general dialog data. */ - if (dlg_init(dlg) != PJ_SUCCESS) { - return -1; - } - - /* Get the To header. */ - to = rdata->to; - - /* Copy URI in the To header as our local URI. */ - dlg->local.info = pjsip_hdr_clone( dlg->pool, to); - - /* Set tag in the local info. */ - dlg->local.info->tag = dlg->local.tag; - - /* Create local Contact to be advertised in the response. - * At the moment, just copy URI from the local URI as our contact. - */ - dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); - dlg->local.contact->star = 0; - name_addr = (pjsip_name_addr *)dlg->local.info->uri; - dlg->local.contact->uri = (pjsip_uri*) name_addr; - url = (pjsip_url*) name_addr->uri; - //url->port = rdata->via->sent_by.port; - //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) ); - - /* Save remote URI. */ - dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from ); - pjsip_fromto_set_to( dlg->remote.info ); - pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag ); - - /* Save remote Contact. */ - contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); - if (contact) { - dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); - } else { - PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s", - pj_sockaddr_get_str_addr(&rdata->addr))); - dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool ); - dlg->remote.contact->uri = dlg->remote.info->uri; - } - - /* Save Call-ID. */ - dlg->call_id = pjsip_cid_hdr_create(dlg->pool); - pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id ); - - /* Initialize local CSeq and save remote CSeq.*/ - dlg->local.cseq = rdata->timestamp.sec & 0xFFFF; - dlg->remote.cseq = rdata->cseq->cseq; - - /* Secure? */ - flag = pjsip_transport_get_flag(rdata->transport); - dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0; - - /* Initial state is NULL. */ - event.type = event.src_type = PJSIP_EVENT_RX_MSG; - event.src.rdata = rdata; - dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); - - PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata)); - return PJ_SUCCESS; -} - -/* - * Set the contact details. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg, - const pj_str_t *contact ) -{ - pjsip_uri *local_uri; - pj_str_t tmp; - - pj_strdup_with_null(dlg->pool, &tmp, contact); - local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (local_uri == NULL) { - PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI")); - return -1; - } - - dlg->local.contact->star = 0; - dlg->local.contact->uri = local_uri; - return 0; -} - -/* - * Set route set. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg, - const pjsip_route_hdr *route_set ) -{ - pjsip_route_hdr *hdr; - - pj_list_init(&dlg->route_set); - hdr = route_set->next; - while (hdr != route_set) { - pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr); - pj_list_insert_before( &dlg->route_set, cloned); - hdr = hdr->next; - } - return 0; -} - -/* - * Set route set without cloning the header. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg, - pjsip_route_hdr *route_set) -{ - pjsip_route_hdr *hdr; - - pj_list_init(&dlg->route_set); - hdr = route_set->next; - while (hdr != route_set) { - pj_list_insert_before( &dlg->route_set, hdr); - hdr = hdr->next; - } - return 0; -} - -/* - * Application calls this function when it wants to initiate an outgoing - * dialog (incoming dialogs are created automatically by UA when it receives - * INVITE, by calling pjsip_dlg_init_from_rdata()). - * This function should initialize most of the dialog's properties. - */ -PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg, - const pj_str_t *c_local_info, - const pj_str_t *c_remote_info, - const pj_str_t *c_target) -{ - pj_time_val tv; - pjsip_event event; - pj_str_t buf; - - if (!dlg || !c_local_info || !c_remote_info) { - pj_assert(dlg && c_local_info && c_remote_info); - return -1; - } - - PJ_LOG(5, (dlg->obj_name, "initializing")); - - /* Init general dialog */ - if (dlg_init(dlg) != PJ_SUCCESS) { - return -1; - } - - /* Duplicate local info. */ - pj_strdup_with_null( dlg->pool, &buf, c_local_info); - - /* Build local URI. */ - dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (dlg->local.target == NULL) { - PJ_LOG(2, (dlg->obj_name, - "pjsip_dlg_init: invalid local URI %s", buf.ptr)); - return -1; - } - - /* Set local URI. */ - dlg->local.info = pjsip_from_hdr_create(dlg->pool); - dlg->local.info->uri = dlg->local.target; - dlg->local.info->tag = dlg->local.tag; - - /* Create local Contact to be advertised in the response. */ - dlg->local.contact = pjsip_contact_hdr_create( dlg->pool ); - dlg->local.contact->star = 0; - dlg->local.contact->uri = dlg->local.target; - - /* Set remote URI. */ - dlg->remote.info = pjsip_to_hdr_create(dlg->pool); - - /* Duplicate to buffer. */ - pj_strdup_with_null( dlg->pool, &buf, c_remote_info); - - /* Build remote info. */ - dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (dlg->remote.info->uri == NULL) { - PJ_LOG(2, (dlg->obj_name, - "pjsip_dlg_init: invalid remote URI %s", buf.ptr)); - return -1; - } - - /* Set remote Contact initially equal to the remote URI. */ - dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); - dlg->remote.contact->star = 0; - dlg->remote.contact->uri = dlg->remote.info->uri; - - /* Set initial remote target. */ - if (c_target != NULL) { - pj_strdup_with_null( dlg->pool, &buf, c_target); - dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0); - if (dlg->remote.target == NULL) { - PJ_LOG(2, (dlg->obj_name, - "pjsip_dlg_init: invalid remote target %s", buf.ptr)); - return -1; - } - } else { - dlg->remote.target = dlg->remote.info->uri; - } - - /* Create globally unique Call-ID */ - dlg->call_id = pjsip_cid_hdr_create(dlg->pool); - pj_create_unique_string( dlg->pool, &dlg->call_id->id ); - - /* Local and remote CSeq */ - pj_gettimeofday(&tv); - dlg->local.cseq = tv.sec & 0xFFFF; - dlg->remote.cseq = 0; - - /* Initial state is NULL. */ - event.type = event.src_type = PJSIP_EVENT_TX_MSG; - event.src.data = NULL; - dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event); - - /* Done. */ - PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s", - pjsip_role_name(dlg->role), - c_local_info->slen, c_local_info->ptr, - c_remote_info->slen, c_remote_info->ptr)); - - return PJ_SUCCESS; -} - -/* - * Set credentials. - */ -PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg, - int count, - const pjsip_cred_info *cred) -{ - if (count > 0) { - dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info)); - pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info)); - } - dlg->cred_count = count; - return 0; -} - -/* - * Create a new request within dialog (i.e. after the dialog session has been - * established). The construction of such requests follows the rule in - * RFC3261 section 12.2.1. - */ -static void dlg_create_request_throw( pjsip_tx_data **p_tdata, - pjsip_dlg *dlg, - const pjsip_method *method, - int cseq ) -{ - pjsip_tx_data *tdata; - pjsip_contact_hdr *contact; - pjsip_route_hdr *route, *end_list; - - /* Contact Header field. - * Contact can only be present in requests that establish dialog (in the - * core SIP spec, only INVITE). - */ - if (method->id == PJSIP_INVITE_METHOD) - contact = dlg->local.contact; - else - contact = NULL; - - tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt, - method, - dlg->remote.target, - dlg->local.info, - dlg->remote.info, - contact, - dlg->call_id, - cseq, - NULL); - if (!tdata) { - PJ_THROW(1); - return; - } - - /* Just copy dialog route-set to Route header. - * The transaction will do the processing as specified in Section 12.2.1 - * of RFC 3261 in function tsx_process_route() in sip_transaction.c. - */ - route = dlg->route_set.next; - end_list = &dlg->route_set; - for (; route != end_list; route = route->next ) { - pjsip_route_hdr *r; - r = pjsip_hdr_shallow_clone( tdata->pool, route ); - pjsip_routing_hdr_set_route(r); - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r); - } - - /* Copy authorization headers. */ - pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess, - dlg->cred_count, dlg->cred_info); - - *p_tdata = tdata; -} - - -/* - * This function is called by application to create new outgoing request - * message for this dialog. After the request is created, application can - * modify the message (such adding headers), and eventually send the request. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg, - const pjsip_method *method, - int cseq) -{ - PJ_USE_EXCEPTION; - struct dialog_lock_data lck; - pjsip_tx_data *tdata = NULL; - - pj_assert(dlg != NULL && method != NULL); - if (!dlg || !method) { - return NULL; - } - - PJ_LOG(5, (dlg->obj_name, "Creating request")); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Use outgoing CSeq and increment it by one. */ - if (cseq < 0) - cseq = dlg->local.cseq + 1; - - PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d", - method->name.slen, method->name.ptr, cseq)); - - /* Create the request. */ - PJ_TRY { - dlg_create_request_throw(&tdata, dlg, method, cseq); - PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name)); - } - PJ_DEFAULT { - /* Failed! Delete transmit data. */ - if (tdata) { - pjsip_tx_data_dec_ref( tdata ); - tdata = NULL; - } - } - PJ_END; - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return tdata; -} - -/* - * Sends request. - * Select the transport for the request message - */ -static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata ) -{ - pjsip_transaction *tsx; - pj_status_t status = PJ_SUCCESS; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL && tdata != NULL); - if (!dlg || !tdata) { - return -1; - } - - PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name)); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Create a new transaction. */ - tsx = pjsip_endpt_create_tsx( dlg->ua->endpt ); - if (!tsx) { - unlock_dialog(dlg, &lck); - return -1; - } - - PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name)); - - /* Initialize transaction */ - tsx->module_data[dlg->ua->mod_id] = dlg; - status = pjsip_tsx_init_uac( tsx, tdata ); - if (status != PJ_SUCCESS) { - unlock_dialog(dlg, &lck); - pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx ); - return -1; - } - pjsip_endpt_register_tsx( dlg->ua->endpt, tsx ); - - /* Start the transaction. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return status; -} - -/* - * This function can be called by application to send ANY outgoing message - * to remote party. - */ -PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata ) -{ - pj_status_t status; - int tsx_status; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL && tdata != NULL); - if (!dlg || !tdata) { - return -1; - } - - lock_dialog(dlg, &lck); - - if (tdata->msg->type == PJSIP_REQUEST_MSG) { - int request_cseq; - pjsip_msg *msg = tdata->msg; - pjsip_cseq_hdr *cseq_hdr; - - switch (msg->line.req.method.id) { - case PJSIP_CANCEL_METHOD: - - /* Check the INVITE transaction state. */ - tsx_status = dlg->invite_tsx->status_code; - if (tsx_status >= 200) { - /* Already terminated. Can't cancel. */ - status = -1; - goto on_return; - } - - /* If we've got provisional response, then send CANCEL and wait for - * the response to INVITE to arrive. Otherwise just send CANCEL and - * terminate the INVITE. - */ - if (tsx_status < 100) { - pjsip_tsx_terminate( dlg->invite_tsx, - PJSIP_SC_REQUEST_TERMINATED); - status = 0; - goto on_return; - } - - status = 0; - request_cseq = dlg->invite_tsx->cseq; - break; - - case PJSIP_ACK_METHOD: - /* Sending ACK outside of transaction is not supported at present! */ - pj_assert(0); - status = 0; - request_cseq = dlg->local.cseq; - break; - - case PJSIP_INVITE_METHOD: - /* For an initial INVITE, reset dialog state to NULL so we get - * 'normal' UAC notifications such as on_provisional(), etc. - * Initial INVITE is the request that is sent when the dialog has - * not been established yet. It's not necessarily the first INVITE - * sent, as when the Authorization fails, subsequent INVITE are also - * considered as an initial INVITE. - */ - if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { - /* Set state to NULL. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL); - - } else { - /* This is a re-INVITE */ - } - status = 0; - request_cseq = dlg->local.cseq + 1; - break; - - default: - status = 0; - request_cseq = dlg->local.cseq + 1; - break; - } - - if (status != 0) - goto on_return; - - /* Update dialog's local CSeq, if necessary. */ - if (request_cseq != dlg->local.cseq) - dlg->local.cseq = request_cseq; - - /* Update CSeq header in the request. */ - cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, - PJSIP_H_CSEQ, NULL); - pj_assert(cseq_hdr != NULL); - - /* Update the CSeq */ - cseq_hdr->cseq = request_cseq; - - /* Force the whole message to be re-printed. */ - pjsip_tx_data_invalidate_msg( tdata ); - - /* Now send the request. */ - status = dlg_send_request(dlg, tdata); - - } else { - /* - * This is only valid for sending response to INVITE! - */ - pjsip_cseq_hdr *cseq_hdr; - - if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) { - status = -1; - goto on_return; - } - - cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg, - PJSIP_H_CSEQ, NULL); - pj_assert(cseq_hdr); - - if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) { - status = -1; - goto on_return; - } - - pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq); - - pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata); - status = 0; - } - -on_return: - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - /* Whatever happen delete the message. */ - pjsip_tx_data_dec_ref( tdata ); - - return status; -} - -/* - * Sends outgoing invitation. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg ) -{ - pjsip_method method; - struct dialog_lock_data lck; - const pjsip_allow_hdr *allow_hdr; - pjsip_tx_data *tdata; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "request to send invitation")); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Create request. */ - pjsip_method_set( &method, PJSIP_INVITE_METHOD); - tdata = pjsip_dlg_create_request( dlg, &method, -1 ); - if (tdata == NULL) { - unlock_dialog(dlg, &lck); - return NULL; - } - - /* Invite SHOULD contain "Allow" header. */ - allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); - if (allow_hdr) { - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); - } - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return tdata; -} - -/* - * Cancel pending outgoing dialog invitation. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg ) -{ - pjsip_tx_data *tdata = NULL; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "request to cancel invitation")); - - lock_dialog(dlg, &lck); - - /* Check the INVITE transaction. */ - if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " - "no INVITE transaction found")); - goto on_return; - } - - /* Construct the CANCEL request. */ - tdata = pjsip_endpt_create_cancel( dlg->ua->endpt, - dlg->invite_tsx->last_tx ); - if (tdata == NULL) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: " - "unable to construct request")); - goto on_return; - } - - /* Add reference counter to tdata. */ - pjsip_tx_data_add_ref(tdata); - -on_return: - unlock_dialog(dlg, &lck); - return tdata; -} - - -/* - * Answer incoming dialog invitation, with either provisional responses - * or a final response. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code ) -{ - pjsip_tx_data *tdata = NULL; - pjsip_msg *msg; - struct dialog_lock_data lck; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code)); - - /* Lock dialog. */ - lock_dialog(dlg, &lck); - - /* Must have pending INVITE. */ - if (dlg->invite_tsx == NULL) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found")); - goto on_return; - } - /* Must be UAS. */ - if (dlg->role != PJSIP_ROLE_UAS) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS")); - goto on_return; - } - /* Must have not answered with final response before. */ - if (dlg->invite_tsx->status_code >= 200) { - PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated " - "with status %d", dlg->invite_tsx->status_code)); - goto on_return; - } - - /* Get transmit data and the message. - * We will rewrite the message with a new status code. - */ - tdata = dlg->invite_tsx->last_tx; - msg = tdata->msg; - - /* Set status code and reason phrase. */ - if (code < 100 || code >= 700) code = 500; - msg->line.status.code = code; - msg->line.status.reason = *pjsip_get_status_text(code); - - /* For 2xx response, Contact and Record-Route must be added. */ - if (PJSIP_IS_STATUS_IN_CLASS(code,200)) { - const pjsip_allow_hdr *allow_hdr; - - if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) { - pjsip_contact_hdr *contact; - contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact); - pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact ); - } - - /* 2xx response MUST contain "Allow" header. */ - allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt ); - if (allow_hdr) { - pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr)); - } - } - - /* for all but 100 responses, To-tag must be set. */ - if (code != 100) { - pjsip_to_hdr *to; - to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL); - to->tag = dlg->local.tag; - } - - /* Reset packet buffer. */ - pjsip_tx_data_invalidate_msg(tdata); - - /* Add reference counter */ - pjsip_tx_data_add_ref(tdata); - -on_return: - - /* Unlock dialog. */ - unlock_dialog(dlg, &lck); - - return tdata; -} - - -/* - * Send BYE request to terminate the dialog's session. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg ) -{ - pjsip_method method; - struct dialog_lock_data lck; - pjsip_tx_data *tdata; - - if (!dlg) { - pj_assert(dlg != NULL); - return NULL; - } - - PJ_LOG(4, (dlg->obj_name, "request to terminate session")); - - lock_dialog(dlg, &lck); - - pjsip_method_set( &method, PJSIP_BYE_METHOD); - tdata = pjsip_dlg_create_request( dlg, &method, -1 ); - - unlock_dialog(dlg, &lck); - - return tdata; -} - -/* - * High level function to disconnect dialog's session. Depending on dialog's - * state, this function will either send CANCEL, final response, or BYE to - * trigger the disconnection. A status code must be supplied, which will be - * sent if dialog will be transmitting a final response to INVITE. - */ -PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg, - int status_code ) -{ - pjsip_tx_data *tdata = NULL; - - pj_assert(dlg != NULL); - if (!dlg) { - return NULL; - } - - switch (dlg->state) { - case PJSIP_DIALOG_STATE_INCOMING: - tdata = pjsip_dlg_answer(dlg, status_code); - break; - - case PJSIP_DIALOG_STATE_CALLING: - tdata = pjsip_dlg_cancel(dlg); - break; - - case PJSIP_DIALOG_STATE_PROCEEDING: - if (dlg->role == PJSIP_ROLE_UAC) { - tdata = pjsip_dlg_cancel(dlg); - } else { - tdata = pjsip_dlg_answer(dlg, status_code); - } - break; - - case PJSIP_DIALOG_STATE_ESTABLISHED: - tdata = pjsip_dlg_bye(dlg); - break; - - default: - PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()", - dlg_state_names[dlg->state])); - break; - } - - return tdata; -} - -/* - * Handling of the receipt of 2xx/INVITE response. - */ -static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg, - pjsip_event *event ) -{ - pjsip_msg *msg; - pjsip_contact_hdr *contact; - pjsip_hdr *hdr, *end_hdr; - pjsip_method method; - pjsip_tx_data *ack_tdata; - - /* Get the message */ - msg = event->src.rdata->msg; - - /* Update remote's tag information. */ - pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag); - - /* Copy Contact information in the 2xx/INVITE response to dialog's. - * remote contact - */ - contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); - if (contact) { - dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact ); - } else { - /* duplicate contact from "From" header (?) */ - PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!")); - dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool); - dlg->remote.contact->uri = dlg->remote.info->uri; - } - - /* Copy Record-Route header (in reverse order) as dialog's route-set, - * overwriting previous route-set, if any, even if the received route-set - * is empty. - */ - pj_list_init(&dlg->route_set); - end_hdr = &msg->hdr; - for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) { - if (hdr->type == PJSIP_H_RECORD_ROUTE) { - pjsip_route_hdr *r; - r = pjsip_hdr_clone(dlg->pool, hdr); - pjsip_routing_hdr_set_route(r); - pj_list_insert_before(&dlg->route_set, r); - } - } - - /* On receipt of 200/INVITE response, send ACK. - * This ack must be saved and retransmitted whenever we receive - * 200/INVITE retransmission, until 64*T1 seconds elapsed. - */ - pjsip_method_set( &method, PJSIP_ACK_METHOD); - ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq); - if (ack_tdata == NULL) { - //PJ_TODO(HANDLE_CREATE_ACK_FAILURE) - PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request")); - return; - } - - /* Send with the transaction. */ - pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata); - - /* Decrement reference counter because pjsip_dlg_create_request - * automatically increments the request. - */ - pjsip_tx_data_dec_ref( ack_tdata ); -} - -/* - * State NULL, before any events have been received. - */ -static int dlg_on_state_null( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG) - { - pjsip_hdr *hdr, *hdr_list; - - pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); - - /* Save the INVITE transaction. */ - dlg->invite_tsx = tsx; - - /* Change state to INCOMING */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event); - - /* Create response buffer. */ - tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100); - pjsip_tx_data_add_ref(tsx->last_tx); - - /* Copy the Record-Route headers into dialog's route_set, maintaining - * the order. - */ - pj_list_init(&dlg->route_set); - hdr_list = &event->src.rdata->msg->hdr; - hdr = hdr_list->next; - while (hdr != hdr_list) { - if (hdr->type == PJSIP_H_RECORD_ROUTE) { - pjsip_route_hdr *route; - route = pjsip_hdr_clone(dlg->pool, hdr); - pjsip_routing_hdr_set_route(route); - pj_list_insert_before(&dlg->route_set, route); - } - hdr = hdr->next; - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_TX_MSG) - { - pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); - - /* Save the INVITE transaction. */ - dlg->invite_tsx = tsx; - - /* Change state to CALLING. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return 0; -} - -/* - * State INCOMING is after the (callee) dialog has been initialized with - * the incoming request, but before any responses is sent by the dialog. - */ -static int dlg_on_state_incoming( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - return dlg_on_state_proceeding_callee( dlg, tsx, event ); -} - -/* - * State CALLING is after the (caller) dialog has sent outgoing invitation - * but before any responses are received. - */ -static int dlg_on_state_calling( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (tsx == dlg->invite_tsx) { - return dlg_on_state_proceeding_caller( dlg, tsx, event ); - } - return 0; -} - -/* - * State PROCEEDING is after provisional response is received. - * Since the processing is similar to state CALLING, this function is also - * called for CALLING state. - */ -static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - int dlg_is_terminated = 0; - - /* We only care about our INVITE transaction. - * Ignore other transaction progression (such as CANCEL). - */ - if (tsx != dlg->invite_tsx) { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - return 0; - } - - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) { - switch (tsx->state) { - case PJSIP_TSX_STATE_PROCEEDING: - if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) { - /* Change state to PROCEEDING */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - } else { - /* Also notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - break; - - case PJSIP_TSX_STATE_COMPLETED: - /* Change dialog state. */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - /* Update remote target, take it from the contact hdr. */ - pjsip_contact_hdr *contact; - contact = pjsip_msg_find_hdr(event->src.rdata->msg, - PJSIP_H_CONTACT, NULL); - if (contact) { - dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri); - } else { - PJ_LOG(4,(dlg->obj_name, - "Warning: found no Contact hdr in 200/OK")); - } - dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); - } else if (tsx->status_code==401 || tsx->status_code==407) { - /* Handle Authentication challenge. */ - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( dlg->ua->endpt, - dlg->pool, &dlg->auth_sess, - dlg->cred_count, dlg->cred_info, - tsx->last_tx, event->src.rdata); - if (tdata) { - /* Re-use original request, with a new transaction. - * Need not to worry about CSeq, dialog will take care. - */ - pjsip_dlg_send_msg(dlg, tdata); - return 0; - } else { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - } else { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* Send ACK when dialog is connected. */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - pj_assert(event->src_type == PJSIP_EVENT_RX_MSG); - dlg_on_recv_2xx_invite(dlg, event); - } - break; - - case PJSIP_TSX_STATE_TERMINATED: - /* - * Transaction is terminated because of timeout or transport error. - * To let the application go to normal state progression, call the - * callback twice. First is to emulate disconnection, and then call - * again (with state TERMINATED) to destroy the dialog. - */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* The INVITE transaction will be destroyed, so release reference - * to it. - */ - dlg->invite_tsx = NULL; - - /* We should terminate the dialog now. - * But it's possible that we have other pending transactions (for - * example, outgoing CANCEL is in progress). - * So destroy the dialog only if there's no other transaction. - */ - if (dlg->pending_tsx_count == 0) { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - dlg_is_terminated = 1; - } - break; - - default: - pj_assert(0); - break; - } - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - return dlg_is_terminated ? -1 : 0; -} - -/* - * State PROCEEDING for UAS is after the callee send provisional response. - * This function is also called for INCOMING state. - */ -static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - int dlg_is_terminated = 0; - - pj_assert( dlg->invite_tsx != NULL ); - - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_TX_MSG && - tsx == dlg->invite_tsx) - { - switch (tsx->state) { - case PJSIP_TSX_STATE_PROCEEDING: - /* Change state to PROCEEDING */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - break; - - case PJSIP_TSX_STATE_COMPLETED: - case PJSIP_TSX_STATE_TERMINATED: - /* Change dialog state. */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event); - } else { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* If transaction is terminated in non-2xx situation, - * terminate dialog as well. This happens when something unexpected - * occurs, such as transport error. - */ - if (tsx->state == PJSIP_TSX_STATE_TERMINATED && - !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) - { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - dlg_is_terminated = 1; - } - break; - - default: - pj_assert(0); - break; - } - - } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->method.id == PJSIP_CANCEL_METHOD) - { - pjsip_tx_data *tdata; - - /* Check if sequence number matches the pending INVITE. */ - if (dlg->invite_tsx==NULL || - pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0) - { - PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE")); - - /* No matching INVITE transaction found. */ - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - PJSIP_SC_CALL_TSX_DOES_NOT_EXIST ); - pjsip_tsx_on_tx_msg(tsx, tdata); - return 0; - } - - /* Always respond the CANCEL with 200/CANCEL no matter what. */ - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - 200 ); - pjsip_tsx_on_tx_msg( tsx, tdata ); - - /* Respond the INVITE transaction with 487, only if transaction has - * not completed. - */ - if (dlg->invite_tsx->last_tx) { - if (dlg->invite_tsx->status_code < 200) { - tdata = dlg->invite_tsx->last_tx; - tdata->msg->line.status.code = 487; - tdata->msg->line.status.reason = *pjsip_get_status_text(487); - /* Reset packet buffer. */ - pjsip_tx_data_invalidate_msg(tdata); - pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); - } else { - PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, " - "Transaction already terminated " - "with status %d", - dlg->invite_tsx->status_code)); - } - } else { - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - 487); - pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata ); - } - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return dlg_is_terminated ? -1 : 0; -} - -static int dlg_on_state_proceeding( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (dlg->role == PJSIP_ROLE_UAC) { - return dlg_on_state_proceeding_caller( dlg, tsx, event ); - } else { - return dlg_on_state_proceeding_callee( dlg, tsx, event ); - } -} - -static int dlg_on_state_connecting( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - if (tsx == dlg->invite_tsx) { - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - (tsx->state == PJSIP_TSX_STATE_TERMINATED || - tsx->state == PJSIP_TSX_STATE_COMPLETED || - tsx->state == PJSIP_TSX_STATE_CONFIRMED)) - { - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event); - } else { - /* Probably because we never get the ACK, or transport error - * when sending ACK. - */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - } - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - } else { - /* Handle case when transaction is started when dialog is connecting - * (e.g. BYE requests cross wire. - */ - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->role == PJSIP_ROLE_UAS) - { - pjsip_tx_data *response; - - if (tsx->status_code >= 200) - return 0; - - if (tsx->method.id == PJSIP_BYE_METHOD) { - /* Set state to DISCONNECTED. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - response = pjsip_endpt_create_response( dlg->ua->endpt, - event->src.rdata, 200); - } else { - response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, - PJSIP_SC_INTERNAL_SERVER_ERROR); - } - - if (response) - pjsip_tsx_on_tx_msg(tsx, response); - - return 0; - } - } - return 0; -} - -static int dlg_on_state_established( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(tsx) - - if (tsx && tsx->method.id == PJSIP_BYE_METHOD) { - /* Set state to DISCONNECTED. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - /* Answer with 200/BYE. */ - if (event->src_type == PJSIP_EVENT_RX_MSG) { - pjsip_tx_data *tdata; - tdata = pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, - 200 ); - if (tdata) - pjsip_tsx_on_tx_msg( tsx, tdata ); - } - } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) { - pjsip_method_e method = event->src.rdata->cseq->method.id; - - PJ_TODO(PROPERLY_HANDLE_REINVITATION) - - /* Reinvitation. The message may be INVITE or an ACK. */ - if (method == PJSIP_INVITE_METHOD) { - if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) { - /* Section 14.2: A UAS that receives a second INVITE before it - * sends the final response to a first INVITE with a lower - * CSeq sequence number on the same dialog MUST return a 500 - * (Server Internal Error) response to the second INVITE and - * MUST include a Retry-After header field with a randomly - * chosen value of between 0 and 10 seconds. - */ - pjsip_retry_after_hdr *hdr; - pjsip_tx_data *tdata = - pjsip_endpt_create_response(dlg->ua->endpt, - event->src.rdata, 500); - - if (!tdata) - return 0; - - /* Add Retry-After. */ - hdr = pjsip_retry_after_hdr_create(tdata->pool); - hdr->ivalue = 9; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr); - - /* Send. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - return 0; - } - - /* Keep this as our current INVITE transaction. */ - dlg->invite_tsx = tsx; - - /* Create response buffer. */ - tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, - event->src.rdata, 100); - pjsip_tx_data_add_ref(tsx->last_tx); - - } - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event); - - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return 0; -} - -static int dlg_on_state_disconnected( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(tsx) - - /* Handle case when transaction is started when dialog is disconnected - * (e.g. BYE requests cross wire. - */ - if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->role == PJSIP_ROLE_UAS) - { - pjsip_tx_data *response = NULL; - - if (tsx->status_code >= 200) - return 0; - - if (tsx->method.id == PJSIP_BYE_METHOD) { - response = pjsip_endpt_create_response( dlg->ua->endpt, - event->src.rdata, 200); - } else { - response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, - PJSIP_SC_INTERNAL_SERVER_ERROR); - } - if (response) - pjsip_tsx_on_tx_msg(tsx, response); - - return 0; - } - /* Handle case when outgoing BYE was rejected with 401/407 */ - else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED && - event->src_type == PJSIP_EVENT_RX_MSG && - tsx->role == PJSIP_ROLE_UAC) - { - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool, - &dlg->auth_sess, - dlg->cred_count, dlg->cred_info, - tsx->last_tx, event->src.rdata); - if (tdata) { - pjsip_dlg_send_msg(dlg, tdata); - } - } - } - - - if (dlg->pending_tsx_count == 0) { - /* Set state to TERMINATED. */ - dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event); - - /* Notify application. */ - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event); - - return -1; - } else { - dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event); - } - - return 0; -} - -static int dlg_on_state_terminated( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(dlg) - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(event) - - return -1; -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_types.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_parser.h>
+#include <pj/string.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/guid.h>
+#include <pj/except.h>
+#include <pj/pool.h>
+
+/* TLS to keep dialog lock record (initialized by UA) */
+int pjsip_dlg_lock_tls_id;
+
+struct dialog_lock_data
+{
+ struct dialog_lock_data *prev;
+ pjsip_dlg *dlg;
+ int is_alive;
+};
+
+/*
+ * Static function prototypes.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+ pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq );
+static int dlg_on_all_state_pre( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_all_state_post( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_null( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_incoming( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_calling( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_connecting( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_established( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_disconnected( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+static int dlg_on_state_terminated( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event);
+
+/*
+ * Dialog state handlers.
+ */
+static int (*dlg_state_handlers[])(struct pjsip_dlg *, pjsip_transaction *,
+ pjsip_event *) =
+{
+ &dlg_on_state_null,
+ &dlg_on_state_incoming,
+ &dlg_on_state_calling,
+ &dlg_on_state_proceeding,
+ &dlg_on_state_connecting,
+ &dlg_on_state_established,
+ &dlg_on_state_disconnected,
+ &dlg_on_state_terminated,
+};
+
+/*
+ * Dialog state names.
+ */
+static const char* dlg_state_names[] =
+{
+ "STATE_NULL",
+ "STATE_INCOMING",
+ "STATE_CALLING",
+ "STATE_PROCEEDING",
+ "STATE_CONNECTING",
+ "STATE_ESTABLISHED",
+ "STATE_DISCONNECTED",
+ "STATE_TERMINATED",
+};
+
+
+/*
+ * Get the dialog string state, normally for logging purpose.
+ */
+const char *pjsip_dlg_state_str(pjsip_dlg_state_e state)
+{
+ return dlg_state_names[state];
+}
+
+/* Lock dialog mutex. */
+static void lock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+ struct dialog_lock_data *prev;
+
+ pj_mutex_lock(dlg->mutex);
+ prev = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+ lck->prev = prev;
+ lck->dlg = dlg;
+ lck->is_alive = 1;
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, lck);
+}
+
+/* Carefully unlock dialog mutex, protect against situation when the dialog
+ * has already been destroyed.
+ */
+static pj_status_t unlock_dialog(pjsip_dlg *dlg, struct dialog_lock_data *lck)
+{
+ pj_assert(pj_thread_local_get(pjsip_dlg_lock_tls_id) == lck);
+ pj_assert(dlg == lck->dlg);
+
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, lck->prev);
+ if (lck->is_alive)
+ pj_mutex_unlock(dlg->mutex);
+
+ return lck->is_alive ? 0 : -1;
+}
+
+/*
+ * This is called by dialog's FSM to change dialog's state.
+ */
+static void dlg_set_state( pjsip_dlg *dlg, pjsip_dlg_state_e state,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event);
+
+ PJ_LOG(4, (dlg->obj_name, "State %s-->%s (ev=%s, src=%s, data=%p)",
+ pjsip_dlg_state_str(dlg->state), pjsip_dlg_state_str(state),
+ event ? pjsip_event_str(event->type) : "",
+ event ? pjsip_event_str(event->src_type) : "",
+ event ? event->src.data : NULL));
+
+ dlg->state = state;
+ dlg->handle_tsx_event = dlg_state_handlers[state];
+}
+
+/*
+ * Invoke dialog's callback.
+ * This function is called by dialog's FSM, and interpret the event and call
+ * the corresponding callback registered by application.
+ */
+static void dlg_call_dlg_callback( pjsip_dlg *dlg, pjsip_dlg_event_e dlg_event,
+ pjsip_event *event )
+{
+ pjsip_dlg_callback *cb = dlg->ua->dlg_cb;
+ if (!cb) {
+ PJ_LOG(4,(dlg->obj_name, "Can not call callback (none registered)."));
+ return;
+ }
+
+ /* Low level event: call the all-events callback. */
+ if (cb->on_all_events) {
+ (*cb->on_all_events)(dlg, dlg_event, event);
+ }
+
+ /* Low level event: call the tx/rx callback if this is a tx/rx event. */
+ if (event->type == PJSIP_EVENT_BEFORE_TX && cb->on_before_tx)
+ {
+ (*cb->on_before_tx)(dlg, event->obj.tsx, event->src.tdata, event->data.long_data);
+ }
+ else if (event->type == PJSIP_EVENT_TX_MSG &&
+ event->src_type == PJSIP_EVENT_TX_MSG && cb->on_tx_msg)
+ {
+ (*cb->on_tx_msg)(dlg, event->obj.tsx, event->src.tdata);
+ }
+ else if (event->type == PJSIP_EVENT_RX_MSG &&
+ event->src_type == PJSIP_EVENT_RX_MSG && cb->on_rx_msg) {
+ (*cb->on_rx_msg)(dlg, event->obj.tsx, event->src.rdata);
+ }
+
+ /* Now trigger high level events.
+ * High level event should only occurs when dialog's state has changed,
+ * except for on_provisional, which may be called multiple times whenever
+ * response message is sent
+ */
+ if (dlg->state == PJSIP_DIALOG_STATE_PROCEEDING &&
+ (event->type== PJSIP_EVENT_TSX_STATE_CHANGED) &&
+ event->obj.tsx == dlg->invite_tsx)
+ {
+ /* Sent/received provisional responses. */
+ if (cb->on_provisional)
+ (*cb->on_provisional)(dlg, event->obj.tsx, event);
+ }
+
+ if (dlg_event == PJSIP_DIALOG_EVENT_MID_CALL_REQUEST) {
+ if (cb->on_mid_call_events)
+ (*cb->on_mid_call_events)(dlg, event);
+ return;
+ }
+
+ if (dlg_event != PJSIP_DIALOG_EVENT_STATE_CHANGED)
+ return;
+
+ if (dlg->state == PJSIP_DIALOG_STATE_INCOMING) {
+
+ /* New incoming dialog. */
+ pj_assert (event->src_type == PJSIP_EVENT_RX_MSG);
+ (*cb->on_incoming)(dlg, event->obj.tsx, event->src.rdata);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_CALLING) {
+
+ /* Dialog has just sent the first INVITE. */
+ if (cb->on_calling) {
+ (*cb->on_calling)(dlg, event->obj.tsx, event->src.tdata);
+ }
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_DISCONNECTED) {
+
+ if (cb->on_disconnected)
+ (*cb->on_disconnected)(dlg, event);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_TERMINATED) {
+
+ if (cb->on_terminated)
+ (*cb->on_terminated)(dlg);
+
+ pjsip_ua_destroy_dialog(dlg);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_CONNECTING) {
+
+ if (cb->on_connecting)
+ (*cb->on_connecting)(dlg, event);
+
+ } else if (dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED) {
+
+ if (cb->on_established)
+ (*cb->on_established)(dlg, event);
+ }
+}
+
+/*
+ * This callback receives event from the transaction layer (via User Agent),
+ * or from dialog timer (for 200/INVITE or ACK retransmission).
+ */
+void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int status = 0;
+ struct dialog_lock_data lck;
+
+ PJ_LOG(4, (dlg->obj_name, "state=%s (evt=%s, src=%s)",
+ pjsip_dlg_state_str(dlg->state),
+ pjsip_event_str(event->type),
+ pjsip_event_str(event->src_type)));
+
+
+ lock_dialog(dlg, &lck);
+
+ status = dlg_on_all_state_pre( dlg, tsx, event);
+
+ if (status==0) {
+ status = (*dlg->handle_tsx_event)(dlg, tsx, event);
+ }
+ if (status==0) {
+ status = dlg_on_all_state_post( dlg, tsx, event);
+ }
+
+ unlock_dialog(dlg, &lck);
+}
+
+/*
+ * This function contains common processing in all states, and it is called
+ * before the FSM is invoked.
+ */
+static int dlg_on_all_state_pre( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event)
+
+ if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED)
+ return 0;
+
+ if (tsx && (tsx->state==PJSIP_TSX_STATE_CALLING ||
+ tsx->state==PJSIP_TSX_STATE_TRYING))
+ {
+ ++dlg->pending_tsx_count;
+
+ } else if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED)
+ {
+ --dlg->pending_tsx_count;
+ if (tsx == dlg->invite_tsx)
+ dlg->invite_tsx = NULL;
+ }
+
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ tsx->handle_ack = 1;
+ }
+ return 0;
+}
+
+
+/*
+ * This function contains common processing in all states, and it is called
+ * after the FSM is invoked.
+ */
+static int dlg_on_all_state_post( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(event)
+
+ if (tsx && tsx->state==PJSIP_TSX_STATE_DESTROYED) {
+ if (dlg->pending_tsx_count == 0 &&
+ dlg->state != PJSIP_DIALOG_STATE_CONNECTING &&
+ dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED &&
+ dlg->state != PJSIP_DIALOG_STATE_TERMINATED)
+ {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ * Internal function to initialize dialog, contains common initialization
+ * for both UAS and UAC dialog.
+ */
+static pj_status_t dlg_init( pjsip_dlg *dlg )
+{
+ /* Init mutex. */
+ dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+ if (!dlg->mutex) {
+ PJ_PERROR((dlg->obj_name, "pj_mutex_create()"));
+ return -1;
+ }
+
+ /* Init route-set (Initially empty) */
+ pj_list_init(&dlg->route_set);
+
+ /* Init auth credential list. */
+ pj_list_init(&dlg->auth_sess);
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * This one is called just before dialog is destroyed.
+ * It is called while mutex is held.
+ */
+PJ_DEF(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg )
+{
+ struct dialog_lock_data *lck;
+
+ //PJ_TODO(CHECK_THIS);
+ pj_assert(dlg->pending_tsx_count == 0);
+
+ /* Mark dialog as dead. */
+ lck = pj_thread_local_get(pjsip_dlg_lock_tls_id);
+ while (lck) {
+ if (lck->dlg == dlg)
+ lck->is_alive = 0;
+ lck = lck->prev;
+ }
+}
+
+/*
+ * Initialize dialog from the received request.
+ * This is an internal function which is called by the User Agent (sip_ua.c),
+ * and it will initialize most of dialog's properties with values from the
+ * received message.
+ */
+pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, pjsip_rx_data *rdata )
+{
+ pjsip_msg *msg = rdata->msg;
+ pjsip_to_hdr *to;
+ pjsip_contact_hdr *contact;
+ pjsip_name_addr *name_addr;
+ pjsip_url *url;
+ unsigned flag;
+ pjsip_event event;
+
+ pj_assert(dlg && rdata);
+
+ PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p)", rdata));
+
+ /* Must be an INVITE request. */
+ pj_assert(msg->type == PJSIP_REQUEST_MSG &&
+ msg->line.req.method.id == PJSIP_INVITE_METHOD);
+
+ /* Init general dialog data. */
+ if (dlg_init(dlg) != PJ_SUCCESS) {
+ return -1;
+ }
+
+ /* Get the To header. */
+ to = rdata->to;
+
+ /* Copy URI in the To header as our local URI. */
+ dlg->local.info = pjsip_hdr_clone( dlg->pool, to);
+
+ /* Set tag in the local info. */
+ dlg->local.info->tag = dlg->local.tag;
+
+ /* Create local Contact to be advertised in the response.
+ * At the moment, just copy URI from the local URI as our contact.
+ */
+ dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->local.contact->star = 0;
+ name_addr = (pjsip_name_addr *)dlg->local.info->uri;
+ dlg->local.contact->uri = (pjsip_uri*) name_addr;
+ url = (pjsip_url*) name_addr->uri;
+ //url->port = rdata->via->sent_by.port;
+ //url->port = pj_sockaddr_get_port( pjsip_transport_get_local_addr(rdata->transport) );
+
+ /* Save remote URI. */
+ dlg->remote.info = pjsip_hdr_clone( dlg->pool, rdata->from );
+ pjsip_fromto_set_to( dlg->remote.info );
+ pj_strdup( dlg->pool, &dlg->remote.tag, &rdata->from->tag );
+
+ /* Save remote Contact. */
+ contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+ } else {
+ PJ_LOG(3,(dlg->obj_name, "No Contact header in INVITE from %s",
+ pj_sockaddr_get_str_addr(&rdata->addr)));
+ dlg->remote.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+ }
+
+ /* Save Call-ID. */
+ dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+ pj_strdup( dlg->pool, &dlg->call_id->id, &rdata->call_id );
+
+ /* Initialize local CSeq and save remote CSeq.*/
+ dlg->local.cseq = rdata->timestamp.sec & 0xFFFF;
+ dlg->remote.cseq = rdata->cseq->cseq;
+
+ /* Secure? */
+ flag = pjsip_transport_get_flag(rdata->transport);
+ dlg->secure = (flag & PJSIP_TRANSPORT_SECURE) != 0;
+
+ /* Initial state is NULL. */
+ event.type = event.src_type = PJSIP_EVENT_RX_MSG;
+ event.src.rdata = rdata;
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+ PJ_LOG(5, (dlg->obj_name, "init_from_rdata(%p) complete", rdata));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set the contact details.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_contact( pjsip_dlg *dlg,
+ const pj_str_t *contact )
+{
+ pjsip_uri *local_uri;
+ pj_str_t tmp;
+
+ pj_strdup_with_null(dlg->pool, &tmp, contact);
+ local_uri = pjsip_parse_uri( dlg->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (local_uri == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "set_contact: invalid URI"));
+ return -1;
+ }
+
+ dlg->local.contact->star = 0;
+ dlg->local.contact->uri = local_uri;
+ return 0;
+}
+
+/*
+ * Set route set.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set( pjsip_dlg *dlg,
+ const pjsip_route_hdr *route_set )
+{
+ pjsip_route_hdr *hdr;
+
+ pj_list_init(&dlg->route_set);
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pjsip_route_hdr *cloned = pjsip_hdr_clone(dlg->pool, hdr);
+ pj_list_insert_before( &dlg->route_set, cloned);
+ hdr = hdr->next;
+ }
+ return 0;
+}
+
+/*
+ * Set route set without cloning the header.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_route_set_np( pjsip_dlg *dlg,
+ pjsip_route_hdr *route_set)
+{
+ pjsip_route_hdr *hdr;
+
+ pj_list_init(&dlg->route_set);
+ hdr = route_set->next;
+ while (hdr != route_set) {
+ pj_list_insert_before( &dlg->route_set, hdr);
+ hdr = hdr->next;
+ }
+ return 0;
+}
+
+/*
+ * Application calls this function when it wants to initiate an outgoing
+ * dialog (incoming dialogs are created automatically by UA when it receives
+ * INVITE, by calling pjsip_dlg_init_from_rdata()).
+ * This function should initialize most of the dialog's properties.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_init( pjsip_dlg *dlg,
+ const pj_str_t *c_local_info,
+ const pj_str_t *c_remote_info,
+ const pj_str_t *c_target)
+{
+ pj_time_val tv;
+ pjsip_event event;
+ pj_str_t buf;
+
+ if (!dlg || !c_local_info || !c_remote_info) {
+ pj_assert(dlg && c_local_info && c_remote_info);
+ return -1;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "initializing"));
+
+ /* Init general dialog */
+ if (dlg_init(dlg) != PJ_SUCCESS) {
+ return -1;
+ }
+
+ /* Duplicate local info. */
+ pj_strdup_with_null( dlg->pool, &buf, c_local_info);
+
+ /* Build local URI. */
+ dlg->local.target = pjsip_parse_uri(dlg->pool, buf.ptr, buf.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (dlg->local.target == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid local URI %s", buf.ptr));
+ return -1;
+ }
+
+ /* Set local URI. */
+ dlg->local.info = pjsip_from_hdr_create(dlg->pool);
+ dlg->local.info->uri = dlg->local.target;
+ dlg->local.info->tag = dlg->local.tag;
+
+ /* Create local Contact to be advertised in the response. */
+ dlg->local.contact = pjsip_contact_hdr_create( dlg->pool );
+ dlg->local.contact->star = 0;
+ dlg->local.contact->uri = dlg->local.target;
+
+ /* Set remote URI. */
+ dlg->remote.info = pjsip_to_hdr_create(dlg->pool);
+
+ /* Duplicate to buffer. */
+ pj_strdup_with_null( dlg->pool, &buf, c_remote_info);
+
+ /* Build remote info. */
+ dlg->remote.info->uri = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (dlg->remote.info->uri == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid remote URI %s", buf.ptr));
+ return -1;
+ }
+
+ /* Set remote Contact initially equal to the remote URI. */
+ dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+ dlg->remote.contact->star = 0;
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+
+ /* Set initial remote target. */
+ if (c_target != NULL) {
+ pj_strdup_with_null( dlg->pool, &buf, c_target);
+ dlg->remote.target = pjsip_parse_uri( dlg->pool, buf.ptr, buf.slen, 0);
+ if (dlg->remote.target == NULL) {
+ PJ_LOG(2, (dlg->obj_name,
+ "pjsip_dlg_init: invalid remote target %s", buf.ptr));
+ return -1;
+ }
+ } else {
+ dlg->remote.target = dlg->remote.info->uri;
+ }
+
+ /* Create globally unique Call-ID */
+ dlg->call_id = pjsip_cid_hdr_create(dlg->pool);
+ pj_create_unique_string( dlg->pool, &dlg->call_id->id );
+
+ /* Local and remote CSeq */
+ pj_gettimeofday(&tv);
+ dlg->local.cseq = tv.sec & 0xFFFF;
+ dlg->remote.cseq = 0;
+
+ /* Initial state is NULL. */
+ event.type = event.src_type = PJSIP_EVENT_TX_MSG;
+ event.src.data = NULL;
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, &event);
+
+ /* Done. */
+ PJ_LOG(4, (dlg->obj_name, "%s dialog initialized, From: %.*s, To: %.*s",
+ pjsip_role_name(dlg->role),
+ c_local_info->slen, c_local_info->ptr,
+ c_remote_info->slen, c_remote_info->ptr));
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Set credentials.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_set_credentials( pjsip_dlg *dlg,
+ int count,
+ const pjsip_cred_info *cred)
+{
+ if (count > 0) {
+ dlg->cred_info = pj_pool_alloc(dlg->pool, count * sizeof(pjsip_cred_info));
+ pj_memcpy(dlg->cred_info, cred, count * sizeof(pjsip_cred_info));
+ }
+ dlg->cred_count = count;
+ return 0;
+}
+
+/*
+ * Create a new request within dialog (i.e. after the dialog session has been
+ * established). The construction of such requests follows the rule in
+ * RFC3261 section 12.2.1.
+ */
+static void dlg_create_request_throw( pjsip_tx_data **p_tdata,
+ pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq )
+{
+ pjsip_tx_data *tdata;
+ pjsip_contact_hdr *contact;
+ pjsip_route_hdr *route, *end_list;
+
+ /* Contact Header field.
+ * Contact can only be present in requests that establish dialog (in the
+ * core SIP spec, only INVITE).
+ */
+ if (method->id == PJSIP_INVITE_METHOD)
+ contact = dlg->local.contact;
+ else
+ contact = NULL;
+
+ tdata = pjsip_endpt_create_request_from_hdr( dlg->ua->endpt,
+ method,
+ dlg->remote.target,
+ dlg->local.info,
+ dlg->remote.info,
+ contact,
+ dlg->call_id,
+ cseq,
+ NULL);
+ if (!tdata) {
+ PJ_THROW(1);
+ return;
+ }
+
+ /* Just copy dialog route-set to Route header.
+ * The transaction will do the processing as specified in Section 12.2.1
+ * of RFC 3261 in function tsx_process_route() in sip_transaction.c.
+ */
+ route = dlg->route_set.next;
+ end_list = &dlg->route_set;
+ for (; route != end_list; route = route->next ) {
+ pjsip_route_hdr *r;
+ r = pjsip_hdr_shallow_clone( tdata->pool, route );
+ pjsip_routing_hdr_set_route(r);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r);
+ }
+
+ /* Copy authorization headers. */
+ pjsip_auth_init_req( dlg->pool, tdata, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info);
+
+ *p_tdata = tdata;
+}
+
+
+/*
+ * This function is called by application to create new outgoing request
+ * message for this dialog. After the request is created, application can
+ * modify the message (such adding headers), and eventually send the request.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_create_request( pjsip_dlg *dlg,
+ const pjsip_method *method,
+ int cseq)
+{
+ PJ_USE_EXCEPTION;
+ struct dialog_lock_data lck;
+ pjsip_tx_data *tdata = NULL;
+
+ pj_assert(dlg != NULL && method != NULL);
+ if (!dlg || !method) {
+ return NULL;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "Creating request"));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Use outgoing CSeq and increment it by one. */
+ if (cseq < 0)
+ cseq = dlg->local.cseq + 1;
+
+ PJ_LOG(5, (dlg->obj_name, "creating request %.*s cseq=%d",
+ method->name.slen, method->name.ptr, cseq));
+
+ /* Create the request. */
+ PJ_TRY {
+ dlg_create_request_throw(&tdata, dlg, method, cseq);
+ PJ_LOG(5, (dlg->obj_name, "request data %s created", tdata->obj_name));
+ }
+ PJ_DEFAULT {
+ /* Failed! Delete transmit data. */
+ if (tdata) {
+ pjsip_tx_data_dec_ref( tdata );
+ tdata = NULL;
+ }
+ }
+ PJ_END;
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * Sends request.
+ * Select the transport for the request message
+ */
+static pj_status_t dlg_send_request( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+ pjsip_transaction *tsx;
+ pj_status_t status = PJ_SUCCESS;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL && tdata != NULL);
+ if (!dlg || !tdata) {
+ return -1;
+ }
+
+ PJ_LOG(5, (dlg->obj_name, "sending request %s", tdata->obj_name));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Create a new transaction. */
+ tsx = pjsip_endpt_create_tsx( dlg->ua->endpt );
+ if (!tsx) {
+ unlock_dialog(dlg, &lck);
+ return -1;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "Created new UAC transaction: %s", tsx->obj_name));
+
+ /* Initialize transaction */
+ tsx->module_data[dlg->ua->mod_id] = dlg;
+ status = pjsip_tsx_init_uac( tsx, tdata );
+ if (status != PJ_SUCCESS) {
+ unlock_dialog(dlg, &lck);
+ pjsip_endpt_destroy_tsx( dlg->ua->endpt, tsx );
+ return -1;
+ }
+ pjsip_endpt_register_tsx( dlg->ua->endpt, tsx );
+
+ /* Start the transaction. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return status;
+}
+
+/*
+ * This function can be called by application to send ANY outgoing message
+ * to remote party.
+ */
+PJ_DEF(pj_status_t) pjsip_dlg_send_msg( pjsip_dlg *dlg, pjsip_tx_data *tdata )
+{
+ pj_status_t status;
+ int tsx_status;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL && tdata != NULL);
+ if (!dlg || !tdata) {
+ return -1;
+ }
+
+ lock_dialog(dlg, &lck);
+
+ if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+ int request_cseq;
+ pjsip_msg *msg = tdata->msg;
+ pjsip_cseq_hdr *cseq_hdr;
+
+ switch (msg->line.req.method.id) {
+ case PJSIP_CANCEL_METHOD:
+
+ /* Check the INVITE transaction state. */
+ tsx_status = dlg->invite_tsx->status_code;
+ if (tsx_status >= 200) {
+ /* Already terminated. Can't cancel. */
+ status = -1;
+ goto on_return;
+ }
+
+ /* If we've got provisional response, then send CANCEL and wait for
+ * the response to INVITE to arrive. Otherwise just send CANCEL and
+ * terminate the INVITE.
+ */
+ if (tsx_status < 100) {
+ pjsip_tsx_terminate( dlg->invite_tsx,
+ PJSIP_SC_REQUEST_TERMINATED);
+ status = 0;
+ goto on_return;
+ }
+
+ status = 0;
+ request_cseq = dlg->invite_tsx->cseq;
+ break;
+
+ case PJSIP_ACK_METHOD:
+ /* Sending ACK outside of transaction is not supported at present! */
+ pj_assert(0);
+ status = 0;
+ request_cseq = dlg->local.cseq;
+ break;
+
+ case PJSIP_INVITE_METHOD:
+ /* For an initial INVITE, reset dialog state to NULL so we get
+ * 'normal' UAC notifications such as on_provisional(), etc.
+ * Initial INVITE is the request that is sent when the dialog has
+ * not been established yet. It's not necessarily the first INVITE
+ * sent, as when the Authorization fails, subsequent INVITE are also
+ * considered as an initial INVITE.
+ */
+ if (dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+ /* Set state to NULL. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_NULL, NULL);
+
+ } else {
+ /* This is a re-INVITE */
+ }
+ status = 0;
+ request_cseq = dlg->local.cseq + 1;
+ break;
+
+ default:
+ status = 0;
+ request_cseq = dlg->local.cseq + 1;
+ break;
+ }
+
+ if (status != 0)
+ goto on_return;
+
+ /* Update dialog's local CSeq, if necessary. */
+ if (request_cseq != dlg->local.cseq)
+ dlg->local.cseq = request_cseq;
+
+ /* Update CSeq header in the request. */
+ cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_CSEQ, NULL);
+ pj_assert(cseq_hdr != NULL);
+
+ /* Update the CSeq */
+ cseq_hdr->cseq = request_cseq;
+
+ /* Force the whole message to be re-printed. */
+ pjsip_tx_data_invalidate_msg( tdata );
+
+ /* Now send the request. */
+ status = dlg_send_request(dlg, tdata);
+
+ } else {
+ /*
+ * This is only valid for sending response to INVITE!
+ */
+ pjsip_cseq_hdr *cseq_hdr;
+
+ if (dlg->invite_tsx == NULL || dlg->invite_tsx->status_code >= 200) {
+ status = -1;
+ goto on_return;
+ }
+
+ cseq_hdr = (pjsip_cseq_hdr*) pjsip_msg_find_hdr( tdata->msg,
+ PJSIP_H_CSEQ, NULL);
+ pj_assert(cseq_hdr);
+
+ if (cseq_hdr->method.id != PJSIP_INVITE_METHOD) {
+ status = -1;
+ goto on_return;
+ }
+
+ pj_assert(cseq_hdr->cseq == dlg->invite_tsx->cseq);
+
+ pjsip_tsx_on_tx_msg(dlg->invite_tsx, tdata);
+ status = 0;
+ }
+
+on_return:
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ /* Whatever happen delete the message. */
+ pjsip_tx_data_dec_ref( tdata );
+
+ return status;
+}
+
+/*
+ * Sends outgoing invitation.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_invite( pjsip_dlg *dlg )
+{
+ pjsip_method method;
+ struct dialog_lock_data lck;
+ const pjsip_allow_hdr *allow_hdr;
+ pjsip_tx_data *tdata;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to send invitation"));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Create request. */
+ pjsip_method_set( &method, PJSIP_INVITE_METHOD);
+ tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+ if (tdata == NULL) {
+ unlock_dialog(dlg, &lck);
+ return NULL;
+ }
+
+ /* Invite SHOULD contain "Allow" header. */
+ allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+ if (allow_hdr) {
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+ }
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * Cancel pending outgoing dialog invitation.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_cancel( pjsip_dlg *dlg )
+{
+ pjsip_tx_data *tdata = NULL;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to cancel invitation"));
+
+ lock_dialog(dlg, &lck);
+
+ /* Check the INVITE transaction. */
+ if (dlg->invite_tsx == NULL || dlg->role != PJSIP_ROLE_UAC) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+ "no INVITE transaction found"));
+ goto on_return;
+ }
+
+ /* Construct the CANCEL request. */
+ tdata = pjsip_endpt_create_cancel( dlg->ua->endpt,
+ dlg->invite_tsx->last_tx );
+ if (tdata == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_cancel failed: "
+ "unable to construct request"));
+ goto on_return;
+ }
+
+ /* Add reference counter to tdata. */
+ pjsip_tx_data_add_ref(tdata);
+
+on_return:
+ unlock_dialog(dlg, &lck);
+ return tdata;
+}
+
+
+/*
+ * Answer incoming dialog invitation, with either provisional responses
+ * or a final response.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_answer( pjsip_dlg *dlg, int code )
+{
+ pjsip_tx_data *tdata = NULL;
+ pjsip_msg *msg;
+ struct dialog_lock_data lck;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "pjsip_dlg_answer: code=%d", code));
+
+ /* Lock dialog. */
+ lock_dialog(dlg, &lck);
+
+ /* Must have pending INVITE. */
+ if (dlg->invite_tsx == NULL) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: no INVITE transaction found"));
+ goto on_return;
+ }
+ /* Must be UAS. */
+ if (dlg->role != PJSIP_ROLE_UAS) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: not UAS"));
+ goto on_return;
+ }
+ /* Must have not answered with final response before. */
+ if (dlg->invite_tsx->status_code >= 200) {
+ PJ_LOG(2, (dlg->obj_name, "pjsip_dlg_answer: transaction already terminated "
+ "with status %d", dlg->invite_tsx->status_code));
+ goto on_return;
+ }
+
+ /* Get transmit data and the message.
+ * We will rewrite the message with a new status code.
+ */
+ tdata = dlg->invite_tsx->last_tx;
+ msg = tdata->msg;
+
+ /* Set status code and reason phrase. */
+ if (code < 100 || code >= 700) code = 500;
+ msg->line.status.code = code;
+ msg->line.status.reason = *pjsip_get_status_text(code);
+
+ /* For 2xx response, Contact and Record-Route must be added. */
+ if (PJSIP_IS_STATUS_IN_CLASS(code,200)) {
+ const pjsip_allow_hdr *allow_hdr;
+
+ if (pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, NULL) == NULL) {
+ pjsip_contact_hdr *contact;
+ contact = pjsip_hdr_shallow_clone( tdata->pool, dlg->local.contact);
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)contact );
+ }
+
+ /* 2xx response MUST contain "Allow" header. */
+ allow_hdr = pjsip_endpt_get_allow_hdr( dlg->ua->endpt );
+ if (allow_hdr) {
+ pjsip_msg_add_hdr( msg, pjsip_hdr_shallow_clone( tdata->pool, allow_hdr));
+ }
+ }
+
+ /* for all but 100 responses, To-tag must be set. */
+ if (code != 100) {
+ pjsip_to_hdr *to;
+ to = pjsip_msg_find_hdr( msg, PJSIP_H_TO, NULL);
+ to->tag = dlg->local.tag;
+ }
+
+ /* Reset packet buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Add reference counter */
+ pjsip_tx_data_add_ref(tdata);
+
+on_return:
+
+ /* Unlock dialog. */
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+
+/*
+ * Send BYE request to terminate the dialog's session.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_bye( pjsip_dlg *dlg )
+{
+ pjsip_method method;
+ struct dialog_lock_data lck;
+ pjsip_tx_data *tdata;
+
+ if (!dlg) {
+ pj_assert(dlg != NULL);
+ return NULL;
+ }
+
+ PJ_LOG(4, (dlg->obj_name, "request to terminate session"));
+
+ lock_dialog(dlg, &lck);
+
+ pjsip_method_set( &method, PJSIP_BYE_METHOD);
+ tdata = pjsip_dlg_create_request( dlg, &method, -1 );
+
+ unlock_dialog(dlg, &lck);
+
+ return tdata;
+}
+
+/*
+ * High level function to disconnect dialog's session. Depending on dialog's
+ * state, this function will either send CANCEL, final response, or BYE to
+ * trigger the disconnection. A status code must be supplied, which will be
+ * sent if dialog will be transmitting a final response to INVITE.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_dlg_disconnect( pjsip_dlg *dlg,
+ int status_code )
+{
+ pjsip_tx_data *tdata = NULL;
+
+ pj_assert(dlg != NULL);
+ if (!dlg) {
+ return NULL;
+ }
+
+ switch (dlg->state) {
+ case PJSIP_DIALOG_STATE_INCOMING:
+ tdata = pjsip_dlg_answer(dlg, status_code);
+ break;
+
+ case PJSIP_DIALOG_STATE_CALLING:
+ tdata = pjsip_dlg_cancel(dlg);
+ break;
+
+ case PJSIP_DIALOG_STATE_PROCEEDING:
+ if (dlg->role == PJSIP_ROLE_UAC) {
+ tdata = pjsip_dlg_cancel(dlg);
+ } else {
+ tdata = pjsip_dlg_answer(dlg, status_code);
+ }
+ break;
+
+ case PJSIP_DIALOG_STATE_ESTABLISHED:
+ tdata = pjsip_dlg_bye(dlg);
+ break;
+
+ default:
+ PJ_LOG(4,(dlg->obj_name, "Invalid state %s in pjsip_dlg_disconnect()",
+ dlg_state_names[dlg->state]));
+ break;
+ }
+
+ return tdata;
+}
+
+/*
+ * Handling of the receipt of 2xx/INVITE response.
+ */
+static void dlg_on_recv_2xx_invite( pjsip_dlg *dlg,
+ pjsip_event *event )
+{
+ pjsip_msg *msg;
+ pjsip_contact_hdr *contact;
+ pjsip_hdr *hdr, *end_hdr;
+ pjsip_method method;
+ pjsip_tx_data *ack_tdata;
+
+ /* Get the message */
+ msg = event->src.rdata->msg;
+
+ /* Update remote's tag information. */
+ pj_strdup(dlg->pool, &dlg->remote.info->tag, &event->src.rdata->to_tag);
+
+ /* Copy Contact information in the 2xx/INVITE response to dialog's.
+ * remote contact
+ */
+ contact = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.contact = pjsip_hdr_clone( dlg->pool, contact );
+ } else {
+ /* duplicate contact from "From" header (?) */
+ PJ_LOG(4,(dlg->obj_name, "Received 200/OK to INVITE with no Contact!"));
+ dlg->remote.contact = pjsip_contact_hdr_create(dlg->pool);
+ dlg->remote.contact->uri = dlg->remote.info->uri;
+ }
+
+ /* Copy Record-Route header (in reverse order) as dialog's route-set,
+ * overwriting previous route-set, if any, even if the received route-set
+ * is empty.
+ */
+ pj_list_init(&dlg->route_set);
+ end_hdr = &msg->hdr;
+ for (hdr = msg->hdr.prev; hdr!=end_hdr; hdr = hdr->prev) {
+ if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+ pjsip_route_hdr *r;
+ r = pjsip_hdr_clone(dlg->pool, hdr);
+ pjsip_routing_hdr_set_route(r);
+ pj_list_insert_before(&dlg->route_set, r);
+ }
+ }
+
+ /* On receipt of 200/INVITE response, send ACK.
+ * This ack must be saved and retransmitted whenever we receive
+ * 200/INVITE retransmission, until 64*T1 seconds elapsed.
+ */
+ pjsip_method_set( &method, PJSIP_ACK_METHOD);
+ ack_tdata = pjsip_dlg_create_request( dlg, &method, dlg->invite_tsx->cseq);
+ if (ack_tdata == NULL) {
+ //PJ_TODO(HANDLE_CREATE_ACK_FAILURE)
+ PJ_LOG(2, (dlg->obj_name, "Error sending ACK msg: can't create request"));
+ return;
+ }
+
+ /* Send with the transaction. */
+ pjsip_tsx_on_tx_ack( dlg->invite_tsx, ack_tdata);
+
+ /* Decrement reference counter because pjsip_dlg_create_request
+ * automatically increments the request.
+ */
+ pjsip_tx_data_dec_ref( ack_tdata );
+}
+
+/*
+ * State NULL, before any events have been received.
+ */
+static int dlg_on_state_null( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG)
+ {
+ pjsip_hdr *hdr, *hdr_list;
+
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Save the INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Change state to INCOMING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_INCOMING, event);
+
+ /* Create response buffer. */
+ tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata, 100);
+ pjsip_tx_data_add_ref(tsx->last_tx);
+
+ /* Copy the Record-Route headers into dialog's route_set, maintaining
+ * the order.
+ */
+ pj_list_init(&dlg->route_set);
+ hdr_list = &event->src.rdata->msg->hdr;
+ hdr = hdr_list->next;
+ while (hdr != hdr_list) {
+ if (hdr->type == PJSIP_H_RECORD_ROUTE) {
+ pjsip_route_hdr *route;
+ route = pjsip_hdr_clone(dlg->pool, hdr);
+ pjsip_routing_hdr_set_route(route);
+ pj_list_insert_before(&dlg->route_set, route);
+ }
+ hdr = hdr->next;
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_TX_MSG)
+ {
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Save the INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Change state to CALLING. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CALLING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+/*
+ * State INCOMING is after the (callee) dialog has been initialized with
+ * the incoming request, but before any responses is sent by the dialog.
+ */
+static int dlg_on_state_incoming( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ return dlg_on_state_proceeding_callee( dlg, tsx, event );
+}
+
+/*
+ * State CALLING is after the (caller) dialog has sent outgoing invitation
+ * but before any responses are received.
+ */
+static int dlg_on_state_calling( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (tsx == dlg->invite_tsx) {
+ return dlg_on_state_proceeding_caller( dlg, tsx, event );
+ }
+ return 0;
+}
+
+/*
+ * State PROCEEDING is after provisional response is received.
+ * Since the processing is similar to state CALLING, this function is also
+ * called for CALLING state.
+ */
+static int dlg_on_state_proceeding_caller( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int dlg_is_terminated = 0;
+
+ /* We only care about our INVITE transaction.
+ * Ignore other transaction progression (such as CANCEL).
+ */
+ if (tsx != dlg->invite_tsx) {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ return 0;
+ }
+
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED) {
+ switch (tsx->state) {
+ case PJSIP_TSX_STATE_PROCEEDING:
+ if (dlg->state != PJSIP_DIALOG_STATE_PROCEEDING) {
+ /* Change state to PROCEEDING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ } else {
+ /* Also notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ break;
+
+ case PJSIP_TSX_STATE_COMPLETED:
+ /* Change dialog state. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ /* Update remote target, take it from the contact hdr. */
+ pjsip_contact_hdr *contact;
+ contact = pjsip_msg_find_hdr(event->src.rdata->msg,
+ PJSIP_H_CONTACT, NULL);
+ if (contact) {
+ dlg->remote.target = pjsip_uri_clone(dlg->pool, contact->uri);
+ } else {
+ PJ_LOG(4,(dlg->obj_name,
+ "Warning: found no Contact hdr in 200/OK"));
+ }
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+ } else if (tsx->status_code==401 || tsx->status_code==407) {
+ /* Handle Authentication challenge. */
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( dlg->ua->endpt,
+ dlg->pool, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ /* Re-use original request, with a new transaction.
+ * Need not to worry about CSeq, dialog will take care.
+ */
+ pjsip_dlg_send_msg(dlg, tdata);
+ return 0;
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* Send ACK when dialog is connected. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ pj_assert(event->src_type == PJSIP_EVENT_RX_MSG);
+ dlg_on_recv_2xx_invite(dlg, event);
+ }
+ break;
+
+ case PJSIP_TSX_STATE_TERMINATED:
+ /*
+ * Transaction is terminated because of timeout or transport error.
+ * To let the application go to normal state progression, call the
+ * callback twice. First is to emulate disconnection, and then call
+ * again (with state TERMINATED) to destroy the dialog.
+ */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* The INVITE transaction will be destroyed, so release reference
+ * to it.
+ */
+ dlg->invite_tsx = NULL;
+
+ /* We should terminate the dialog now.
+ * But it's possible that we have other pending transactions (for
+ * example, outgoing CANCEL is in progress).
+ * So destroy the dialog only if there's no other transaction.
+ */
+ if (dlg->pending_tsx_count == 0) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ dlg_is_terminated = 1;
+ }
+ break;
+
+ default:
+ pj_assert(0);
+ break;
+ }
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ return dlg_is_terminated ? -1 : 0;
+}
+
+/*
+ * State PROCEEDING for UAS is after the callee send provisional response.
+ * This function is also called for INCOMING state.
+ */
+static int dlg_on_state_proceeding_callee( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ int dlg_is_terminated = 0;
+
+ pj_assert( dlg->invite_tsx != NULL );
+
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_TX_MSG &&
+ tsx == dlg->invite_tsx)
+ {
+ switch (tsx->state) {
+ case PJSIP_TSX_STATE_PROCEEDING:
+ /* Change state to PROCEEDING */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_PROCEEDING, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ break;
+
+ case PJSIP_TSX_STATE_COMPLETED:
+ case PJSIP_TSX_STATE_TERMINATED:
+ /* Change dialog state. */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_CONNECTING, event);
+ } else {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* If transaction is terminated in non-2xx situation,
+ * terminate dialog as well. This happens when something unexpected
+ * occurs, such as transport error.
+ */
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
+ !PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200))
+ {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ dlg_is_terminated = 1;
+ }
+ break;
+
+ default:
+ pj_assert(0);
+ break;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->method.id == PJSIP_CANCEL_METHOD)
+ {
+ pjsip_tx_data *tdata;
+
+ /* Check if sequence number matches the pending INVITE. */
+ if (dlg->invite_tsx==NULL ||
+ pj_strcmp(&tsx->branch, &dlg->invite_tsx->branch) != 0)
+ {
+ PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no matching INVITE"));
+
+ /* No matching INVITE transaction found. */
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST );
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ return 0;
+ }
+
+ /* Always respond the CANCEL with 200/CANCEL no matter what. */
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 200 );
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+
+ /* Respond the INVITE transaction with 487, only if transaction has
+ * not completed.
+ */
+ if (dlg->invite_tsx->last_tx) {
+ if (dlg->invite_tsx->status_code < 200) {
+ tdata = dlg->invite_tsx->last_tx;
+ tdata->msg->line.status.code = 487;
+ tdata->msg->line.status.reason = *pjsip_get_status_text(487);
+ /* Reset packet buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+ pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+ } else {
+ PJ_LOG(4, (dlg->obj_name, "Received CANCEL with no effect, "
+ "Transaction already terminated "
+ "with status %d",
+ dlg->invite_tsx->status_code));
+ }
+ } else {
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 487);
+ pjsip_tsx_on_tx_msg( dlg->invite_tsx, tdata );
+ }
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return dlg_is_terminated ? -1 : 0;
+}
+
+static int dlg_on_state_proceeding( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (dlg->role == PJSIP_ROLE_UAC) {
+ return dlg_on_state_proceeding_caller( dlg, tsx, event );
+ } else {
+ return dlg_on_state_proceeding_callee( dlg, tsx, event );
+ }
+}
+
+static int dlg_on_state_connecting( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ if (tsx == dlg->invite_tsx) {
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ (tsx->state == PJSIP_TSX_STATE_TERMINATED ||
+ tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+ tsx->state == PJSIP_TSX_STATE_CONFIRMED))
+ {
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_ESTABLISHED, event);
+ } else {
+ /* Probably because we never get the ACK, or transport error
+ * when sending ACK.
+ */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+ }
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+ } else {
+ /* Handle case when transaction is started when dialog is connecting
+ * (e.g. BYE requests cross wire.
+ */
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAS)
+ {
+ pjsip_tx_data *response;
+
+ if (tsx->status_code >= 200)
+ return 0;
+
+ if (tsx->method.id == PJSIP_BYE_METHOD) {
+ /* Set state to DISCONNECTED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ response = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 200);
+ } else {
+ response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ }
+
+ if (response)
+ pjsip_tsx_on_tx_msg(tsx, response);
+
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int dlg_on_state_established( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx)
+
+ if (tsx && tsx->method.id == PJSIP_BYE_METHOD) {
+ /* Set state to DISCONNECTED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_DISCONNECTED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ /* Answer with 200/BYE. */
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata,
+ 200 );
+ if (tdata)
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+ }
+ } else if (tsx && event->src_type == PJSIP_EVENT_RX_MSG) {
+ pjsip_method_e method = event->src.rdata->cseq->method.id;
+
+ PJ_TODO(PROPERLY_HANDLE_REINVITATION)
+
+ /* Reinvitation. The message may be INVITE or an ACK. */
+ if (method == PJSIP_INVITE_METHOD) {
+ if (dlg->invite_tsx && dlg->invite_tsx->status_code < 200) {
+ /* Section 14.2: A UAS that receives a second INVITE before it
+ * sends the final response to a first INVITE with a lower
+ * CSeq sequence number on the same dialog MUST return a 500
+ * (Server Internal Error) response to the second INVITE and
+ * MUST include a Retry-After header field with a randomly
+ * chosen value of between 0 and 10 seconds.
+ */
+ pjsip_retry_after_hdr *hdr;
+ pjsip_tx_data *tdata =
+ pjsip_endpt_create_response(dlg->ua->endpt,
+ event->src.rdata, 500);
+
+ if (!tdata)
+ return 0;
+
+ /* Add Retry-After. */
+ hdr = pjsip_retry_after_hdr_create(tdata->pool);
+ hdr->ivalue = 9;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
+
+ /* Send. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ return 0;
+ }
+
+ /* Keep this as our current INVITE transaction. */
+ dlg->invite_tsx = tsx;
+
+ /* Create response buffer. */
+ tsx->last_tx = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 100);
+ pjsip_tx_data_add_ref(tsx->last_tx);
+
+ }
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_MID_CALL_REQUEST, event);
+
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+static int dlg_on_state_disconnected( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx)
+
+ /* Handle case when transaction is started when dialog is disconnected
+ * (e.g. BYE requests cross wire.
+ */
+ if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAS)
+ {
+ pjsip_tx_data *response = NULL;
+
+ if (tsx->status_code >= 200)
+ return 0;
+
+ if (tsx->method.id == PJSIP_BYE_METHOD) {
+ response = pjsip_endpt_create_response( dlg->ua->endpt,
+ event->src.rdata, 200);
+ } else {
+ response = pjsip_endpt_create_response( dlg->ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ }
+ if (response)
+ pjsip_tsx_on_tx_msg(tsx, response);
+
+ return 0;
+ }
+ /* Handle case when outgoing BYE was rejected with 401/407 */
+ else if (event->type == PJSIP_EVENT_TSX_STATE_CHANGED &&
+ event->src_type == PJSIP_EVENT_RX_MSG &&
+ tsx->role == PJSIP_ROLE_UAC)
+ {
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( dlg->ua->endpt, dlg->pool,
+ &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+ }
+ }
+
+
+ if (dlg->pending_tsx_count == 0) {
+ /* Set state to TERMINATED. */
+ dlg_set_state(dlg, PJSIP_DIALOG_STATE_TERMINATED, event);
+
+ /* Notify application. */
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_STATE_CHANGED, event);
+
+ return -1;
+ } else {
+ dlg_call_dlg_callback(dlg, PJSIP_DIALOG_EVENT_OTHER, event);
+ }
+
+ return 0;
+}
+
+static int dlg_on_state_terminated( pjsip_dlg *dlg,
+ pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(dlg)
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(event)
+
+ return -1;
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index 1167975e..508b5d9a 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -1,496 +1,518 @@ -/* $Id$ - * - */ -#include <pjsip_mod_ua/sip_reg.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_parser.h> -#include <pjsip/sip_module.h> -#include <pjsip/sip_transaction.h> -#include <pjsip/sip_event.h> -#include <pjsip/sip_misc.h> -#include <pjsip/sip_auth_msg.h> -#include <pj/pool.h> -#include <pj/string.h> -#include <pj/guid.h> -#include <pj/log.h> - -#define REFRESH_TIMER 1 -#define DELAY_BEFORE_REFRESH 5 -#define THIS_FILE "sip_regc.c" - -/** - * SIP client registration structure. - */ -struct pjsip_regc -{ - pj_pool_t *pool; - pjsip_endpoint *endpt; - pj_bool_t _delete_flag; - int pending_tsx; - - void *token; - pjsip_regc_cb *cb; - - pj_str_t str_srv_url; - pjsip_uri *srv_url; - pjsip_cid_hdr *cid_hdr; - pjsip_cseq_hdr *cseq_hdr; - pjsip_from_hdr *from_hdr; - pjsip_to_hdr *to_hdr; - char *contact_buf; - pjsip_generic_string_hdr *contact_hdr; - pjsip_expires_hdr *expires_hdr; - pjsip_contact_hdr *unreg_contact_hdr; - pjsip_expires_hdr *unreg_expires_hdr; - pj_uint32_t expires; - - /* Credentials. */ - int cred_count; - pjsip_cred_info *cred_info; - - /* Authorization sessions. */ - pjsip_auth_session auth_sess_list; - - /* Auto refresh registration. */ - pj_bool_t auto_reg; - pj_timer_entry timer; -}; - - - -PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, - pjsip_regc_cb *cb) -{ - pj_pool_t *pool; - pjsip_regc *regc; - - if (cb == NULL) - return NULL; - - pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024); - regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc)); - - regc->pool = pool; - regc->endpt = endpt; - regc->token = token; - regc->cb = cb; - regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE); - regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; - - pj_list_init(®c->auth_sess_list); - - return regc; -} - - -PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc) -{ - if (regc->pending_tsx) { - regc->_delete_flag = 1; - regc->cb = NULL; - } else { - pjsip_endpt_destroy_pool(regc->endpt, regc->pool); - } -} - - -PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc) -{ - return regc->pool; -} - -static void set_expires( pjsip_regc *regc, pj_uint32_t expires) -{ - if (expires != regc->expires) { - regc->expires_hdr = pjsip_expires_hdr_create(regc->pool); - regc->expires_hdr->ivalue = expires; - } else { - regc->expires_hdr = NULL; - } -} - - -static pj_status_t set_contact( pjsip_regc *regc, - int contact_cnt, - const pj_str_t contact[] ) -{ - int i; - char *s; - const pj_str_t contact_STR = { "Contact", 7}; - - /* Concatenate contacts. */ - for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) { - if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) { - return -1; - } - pj_memcpy(s, contact[i].ptr, contact[i].slen); - s += contact[i].slen; - - if (i != contact_cnt - 1) { - *s++ = ','; - *s++ = ' '; - } - } - - /* Set "Contact" header. */ - regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR); - regc->contact_hdr->hvalue.ptr = regc->contact_buf; - regc->contact_hdr->hvalue.slen = (s - regc->contact_buf); - - return 0; -} - - -PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, - const pj_str_t *srv_url, - const pj_str_t *from_url, - const pj_str_t *to_url, - int contact_cnt, - const pj_str_t contact[], - pj_uint32_t expires) -{ - pj_str_t tmp; - - /* Copy server URL. */ - pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); - - /* Set server URL. */ - tmp = regc->str_srv_url; - regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0); - if (regc->srv_url == NULL) { - return -1; - } - - /* Set "From" header. */ - pj_strdup_with_null(regc->pool, &tmp, from_url); - regc->from_hdr = pjsip_from_hdr_create(regc->pool); - regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (!regc->from_hdr->uri) { - PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr)); - return -1; - } - - /* Set "To" header. */ - pj_strdup_with_null(regc->pool, &tmp, to_url); - regc->to_hdr = pjsip_to_hdr_create(regc->pool); - regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (!regc->to_hdr->uri) { - PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr)); - return -1; - } - - - /* Set "Contact" header. */ - if (set_contact( regc, contact_cnt, contact) != 0) - return -1; - - /* Set "Expires" header, if required. */ - set_expires( regc, expires); - - /* Set "Call-ID" header. */ - regc->cid_hdr = pjsip_cid_hdr_create(regc->pool); - pj_create_unique_string(regc->pool, ®c->cid_hdr->id); - - /* Set "CSeq" header. */ - regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool); - regc->cseq_hdr->cseq = 0; - pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD); - - /* Create "Contact" header used in unregistration. */ - regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool); - regc->unreg_contact_hdr->star = 1; - - /* Create "Expires" header used in unregistration. */ - regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool); - regc->unreg_expires_hdr->ivalue = 0; - - /* Done. */ - return 0; -} - -PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, - int count, - const pjsip_cred_info cred[] ) -{ - if (count > 0) { - regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info)); - pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info)); - } - regc->cred_count = count; - return 0; -} - -static pjsip_tx_data *create_request(pjsip_regc *regc) -{ - pjsip_tx_data *tdata; - pjsip_msg *msg; - - /* Create transmit data. */ - tdata = pjsip_endpt_create_tdata(regc->endpt); - if (!tdata) { - return NULL; - } - - /* Create request message. */ - msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); - tdata->msg = msg; - - /* Initialize request line. */ - pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD); - msg->line.req.uri = regc->srv_url; - - /* Add headers. */ - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr); - - /* Add cached authorization headers. */ - pjsip_auth_init_req( regc->pool, tdata, ®c->auth_sess_list, - regc->cred_count, regc->cred_info ); - - /* Add reference counter to transmit data. */ - pjsip_tx_data_add_ref(tdata); - - return tdata; -} - - -PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg) -{ - pjsip_msg *msg; - pjsip_tx_data *tdata; - - tdata = create_request(regc); - if (!tdata) - return NULL; - - msg = tdata->msg; - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr); - if (regc->expires_hdr) - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr); - - if (regc->timer.id != 0) { - pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); - regc->timer.id = 0; - } - - regc->auto_reg = autoreg; - - return tdata; -} - - -PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc) -{ - pjsip_tx_data *tdata; - pjsip_msg *msg; - - if (regc->timer.id != 0) { - pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); - regc->timer.id = 0; - } - - tdata = create_request(regc); - if (!tdata) - return NULL; - - msg = tdata->msg; - pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr); - pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); - - return tdata; -} - - -PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc, - int contact_cnt, - const pj_str_t contact[] ) -{ - return set_contact( regc, contact_cnt, contact ); -} - - -PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, - pj_uint32_t expires ) -{ - set_expires( regc, expires ); - return 0; -} - - -static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason, - pjsip_rx_data *rdata, pj_int32_t expiration, - int contact_cnt, pjsip_contact_hdr *contact[]) -{ - struct pjsip_regc_cbparam cbparam; - - - cbparam.regc = regc; - cbparam.token = regc->token; - cbparam.code = status; - cbparam.reason = *reason; - cbparam.rdata = rdata; - cbparam.contact_cnt = contact_cnt; - cbparam.expiration = expiration; - if (contact_cnt) { - pj_memcpy( cbparam.contact, contact, - contact_cnt*sizeof(pjsip_contact_hdr*)); - } - - (*regc->cb)(&cbparam); -} - -static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, - struct pj_timer_entry *entry) -{ - pjsip_regc *regc = entry->user_data; - pjsip_tx_data *tdata; - - PJ_UNUSED_ARG(timer_heap) - - entry->id = 0; - tdata = pjsip_regc_register(regc, 1); - if (tdata) { - pjsip_regc_send(regc, tdata); - } else { - pj_str_t reason = pj_str("Unable to create txdata"); - call_callback(regc, -1, &reason, NULL, -1, 0, NULL); - } -} - -static void tsx_callback(void *token, pjsip_event *event) -{ - pjsip_regc *regc = token; - pjsip_transaction *tsx = event->obj.tsx; - - /* If registration data has been deleted by user then remove registration - * data from transaction's callback, and don't call callback. - */ - if (regc->_delete_flag) { - --regc->pending_tsx; - - } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || - tsx->status_code == PJSIP_SC_UNAUTHORIZED) - { - pjsip_rx_data *rdata = event->src.rdata; - pjsip_tx_data *tdata; - - tdata = pjsip_auth_reinit_req( regc->endpt, - regc->pool, ®c->auth_sess_list, - regc->cred_count, regc->cred_info, - tsx->last_tx, event->src.rdata ); - - if (tdata) { - --regc->pending_tsx; - pjsip_regc_send(regc, tdata); - return; - } else { - call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason, - rdata, -1, 0, NULL); - --regc->pending_tsx; - } - } else { - int contact_cnt = 0; - pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT]; - pjsip_rx_data *rdata; - pj_int32_t expiration = 0xFFFF; - - if (tsx->status_code/100 == 2) { - int i; - pjsip_contact_hdr *hdr; - pjsip_msg *msg; - pjsip_expires_hdr *expires; - - rdata = event->src.rdata; - msg = rdata->msg; - hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); - while (hdr) { - contact[contact_cnt++] = hdr; - hdr = hdr->next; - if (hdr == (void*)&msg->hdr) - break; - hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr); - } - - expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); - - if (expires) - expiration = expires->ivalue; - - for (i=0; i<contact_cnt; ++i) { - hdr = contact[i]; - if (hdr->expires >= 0 && hdr->expires < expiration) - expiration = contact[i]->expires; - } - - if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) { - pj_time_val delay = { 0, 0}; - - delay.sec = expiration - DELAY_BEFORE_REFRESH; - if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && - delay.sec > (pj_int32_t)regc->expires) - { - delay.sec = regc->expires; - } - if (delay.sec < DELAY_BEFORE_REFRESH) - delay.sec = DELAY_BEFORE_REFRESH; - regc->timer.cb = ®c_refresh_timer_cb; - regc->timer.id = REFRESH_TIMER; - regc->timer.user_data = regc; - pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay); - } - - } else { - rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL; - } - - - /* Call callback. */ - if (expiration == 0xFFFF) expiration = -1; - call_callback(regc, tsx->status_code, - (rdata ? &rdata->msg->line.status.reason - : pjsip_get_status_text(tsx->status_code)), - rdata, expiration, - contact_cnt, contact); - - --regc->pending_tsx; - } - - /* Delete the record if user destroy regc during the callback. */ - if (regc->_delete_flag && regc->pending_tsx==0) { - pjsip_regc_destroy(regc); - } -} - -PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) -{ - int status; - - /* Make sure we don't have pending transaction. */ - if (regc->pending_tsx) { - pj_str_t reason = pj_str("Transaction in progress"); - call_callback(regc, -1, &reason, NULL, -1, 0, NULL); - pjsip_tx_data_dec_ref( tdata ); - return; - } - - /* Invalidate message buffer. */ - pjsip_tx_data_invalidate_msg(tdata); - - /* Increment CSeq */ - regc->cseq_hdr->cseq++; - - /* Send. */ - status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback); - if (status==0) - ++regc->pending_tsx; - else { - pj_str_t reason = pj_str("Unable to send request."); - call_callback(regc, status, &reason, NULL, -1, 0, NULL); - } -} - - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_mod_ua/sip_reg.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_auth_msg.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+
+#define REFRESH_TIMER 1
+#define DELAY_BEFORE_REFRESH 5
+#define THIS_FILE "sip_regc.c"
+
+/**
+ * SIP client registration structure.
+ */
+struct pjsip_regc
+{
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pj_bool_t _delete_flag;
+ int pending_tsx;
+
+ void *token;
+ pjsip_regc_cb *cb;
+
+ pj_str_t str_srv_url;
+ pjsip_uri *srv_url;
+ pjsip_cid_hdr *cid_hdr;
+ pjsip_cseq_hdr *cseq_hdr;
+ pjsip_from_hdr *from_hdr;
+ pjsip_to_hdr *to_hdr;
+ char *contact_buf;
+ pjsip_generic_string_hdr *contact_hdr;
+ pjsip_expires_hdr *expires_hdr;
+ pjsip_contact_hdr *unreg_contact_hdr;
+ pjsip_expires_hdr *unreg_expires_hdr;
+ pj_uint32_t expires;
+
+ /* Credentials. */
+ int cred_count;
+ pjsip_cred_info *cred_info;
+
+ /* Authorization sessions. */
+ pjsip_auth_session auth_sess_list;
+
+ /* Auto refresh registration. */
+ pj_bool_t auto_reg;
+ pj_timer_entry timer;
+};
+
+
+
+PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token,
+ pjsip_regc_cb *cb)
+{
+ pj_pool_t *pool;
+ pjsip_regc *regc;
+
+ if (cb == NULL)
+ return NULL;
+
+ pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024);
+ regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc));
+
+ regc->pool = pool;
+ regc->endpt = endpt;
+ regc->token = token;
+ regc->cb = cb;
+ regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE);
+ regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED;
+
+ pj_list_init(®c->auth_sess_list);
+
+ return regc;
+}
+
+
+PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc)
+{
+ if (regc->pending_tsx) {
+ regc->_delete_flag = 1;
+ regc->cb = NULL;
+ } else {
+ pjsip_endpt_destroy_pool(regc->endpt, regc->pool);
+ }
+}
+
+
+PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc)
+{
+ return regc->pool;
+}
+
+static void set_expires( pjsip_regc *regc, pj_uint32_t expires)
+{
+ if (expires != regc->expires) {
+ regc->expires_hdr = pjsip_expires_hdr_create(regc->pool);
+ regc->expires_hdr->ivalue = expires;
+ } else {
+ regc->expires_hdr = NULL;
+ }
+}
+
+
+static pj_status_t set_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ int i;
+ char *s;
+ const pj_str_t contact_STR = { "Contact", 7};
+
+ /* Concatenate contacts. */
+ for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) {
+ if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) {
+ return -1;
+ }
+ pj_memcpy(s, contact[i].ptr, contact[i].slen);
+ s += contact[i].slen;
+
+ if (i != contact_cnt - 1) {
+ *s++ = ',';
+ *s++ = ' ';
+ }
+ }
+
+ /* Set "Contact" header. */
+ regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR);
+ regc->contact_hdr->hvalue.ptr = regc->contact_buf;
+ regc->contact_hdr->hvalue.slen = (s - regc->contact_buf);
+
+ return 0;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc,
+ const pj_str_t *srv_url,
+ const pj_str_t *from_url,
+ const pj_str_t *to_url,
+ int contact_cnt,
+ const pj_str_t contact[],
+ pj_uint32_t expires)
+{
+ pj_str_t tmp;
+
+ /* Copy server URL. */
+ pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url);
+
+ /* Set server URL. */
+ tmp = regc->str_srv_url;
+ regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0);
+ if (regc->srv_url == NULL) {
+ return -1;
+ }
+
+ /* Set "From" header. */
+ pj_strdup_with_null(regc->pool, &tmp, from_url);
+ regc->from_hdr = pjsip_from_hdr_create(regc->pool);
+ regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->from_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr));
+ return -1;
+ }
+
+ /* Set "To" header. */
+ pj_strdup_with_null(regc->pool, &tmp, to_url);
+ regc->to_hdr = pjsip_to_hdr_create(regc->pool);
+ regc->to_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (!regc->to_hdr->uri) {
+ PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr));
+ return -1;
+ }
+
+
+ /* Set "Contact" header. */
+ if (set_contact( regc, contact_cnt, contact) != 0)
+ return -1;
+
+ /* Set "Expires" header, if required. */
+ set_expires( regc, expires);
+
+ /* Set "Call-ID" header. */
+ regc->cid_hdr = pjsip_cid_hdr_create(regc->pool);
+ pj_create_unique_string(regc->pool, ®c->cid_hdr->id);
+
+ /* Set "CSeq" header. */
+ regc->cseq_hdr = pjsip_cseq_hdr_create(regc->pool);
+ regc->cseq_hdr->cseq = 0;
+ pjsip_method_set( ®c->cseq_hdr->method, PJSIP_REGISTER_METHOD);
+
+ /* Create "Contact" header used in unregistration. */
+ regc->unreg_contact_hdr = pjsip_contact_hdr_create(regc->pool);
+ regc->unreg_contact_hdr->star = 1;
+
+ /* Create "Expires" header used in unregistration. */
+ regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool);
+ regc->unreg_expires_hdr->ivalue = 0;
+
+ /* Done. */
+ return 0;
+}
+
+PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc,
+ int count,
+ const pjsip_cred_info cred[] )
+{
+ if (count > 0) {
+ regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info));
+ pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info));
+ }
+ regc->cred_count = count;
+ return 0;
+}
+
+static pjsip_tx_data *create_request(pjsip_regc *regc)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg;
+
+ /* Create transmit data. */
+ tdata = pjsip_endpt_create_tdata(regc->endpt);
+ if (!tdata) {
+ return NULL;
+ }
+
+ /* Create request message. */
+ msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+ tdata->msg = msg;
+
+ /* Initialize request line. */
+ pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD);
+ msg->line.req.uri = regc->srv_url;
+
+ /* Add headers. */
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr);
+
+ /* Add cached authorization headers. */
+ pjsip_auth_init_req( regc->pool, tdata, ®c->auth_sess_list,
+ regc->cred_count, regc->cred_info );
+
+ /* Add reference counter to transmit data. */
+ pjsip_tx_data_add_ref(tdata);
+
+ return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg)
+{
+ pjsip_msg *msg;
+ pjsip_tx_data *tdata;
+
+ tdata = create_request(regc);
+ if (!tdata)
+ return NULL;
+
+ msg = tdata->msg;
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr);
+ if (regc->expires_hdr)
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->expires_hdr);
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
+ regc->timer.id = 0;
+ }
+
+ regc->auto_reg = autoreg;
+
+ return tdata;
+}
+
+
+PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg;
+
+ if (regc->timer.id != 0) {
+ pjsip_endpt_cancel_timer(regc->endpt, ®c->timer);
+ regc->timer.id = 0;
+ }
+
+ tdata = create_request(regc);
+ if (!tdata)
+ return NULL;
+
+ msg = tdata->msg;
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr);
+ pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr);
+
+ return tdata;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_contact( pjsip_regc *regc,
+ int contact_cnt,
+ const pj_str_t contact[] )
+{
+ return set_contact( regc, contact_cnt, contact );
+}
+
+
+PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc,
+ pj_uint32_t expires )
+{
+ set_expires( regc, expires );
+ return 0;
+}
+
+
+static void call_callback(pjsip_regc *regc, int status, const pj_str_t *reason,
+ pjsip_rx_data *rdata, pj_int32_t expiration,
+ int contact_cnt, pjsip_contact_hdr *contact[])
+{
+ struct pjsip_regc_cbparam cbparam;
+
+
+ cbparam.regc = regc;
+ cbparam.token = regc->token;
+ cbparam.code = status;
+ cbparam.reason = *reason;
+ cbparam.rdata = rdata;
+ cbparam.contact_cnt = contact_cnt;
+ cbparam.expiration = expiration;
+ if (contact_cnt) {
+ pj_memcpy( cbparam.contact, contact,
+ contact_cnt*sizeof(pjsip_contact_hdr*));
+ }
+
+ (*regc->cb)(&cbparam);
+}
+
+static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_regc *regc = entry->user_data;
+ pjsip_tx_data *tdata;
+
+ PJ_UNUSED_ARG(timer_heap)
+
+ entry->id = 0;
+ tdata = pjsip_regc_register(regc, 1);
+ if (tdata) {
+ pjsip_regc_send(regc, tdata);
+ } else {
+ pj_str_t reason = pj_str("Unable to create txdata");
+ call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+ }
+}
+
+static void tsx_callback(void *token, pjsip_event *event)
+{
+ pjsip_regc *regc = token;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ /* If registration data has been deleted by user then remove registration
+ * data from transaction's callback, and don't call callback.
+ */
+ if (regc->_delete_flag) {
+ --regc->pending_tsx;
+
+ } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
+ tsx->status_code == PJSIP_SC_UNAUTHORIZED)
+ {
+ pjsip_rx_data *rdata = event->src.rdata;
+ pjsip_tx_data *tdata;
+
+ tdata = pjsip_auth_reinit_req( regc->endpt,
+ regc->pool, ®c->auth_sess_list,
+ regc->cred_count, regc->cred_info,
+ tsx->last_tx, event->src.rdata );
+
+ if (tdata) {
+ --regc->pending_tsx;
+ pjsip_regc_send(regc, tdata);
+ return;
+ } else {
+ call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason,
+ rdata, -1, 0, NULL);
+ --regc->pending_tsx;
+ }
+ } else {
+ int contact_cnt = 0;
+ pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];
+ pjsip_rx_data *rdata;
+ pj_int32_t expiration = 0xFFFF;
+
+ if (tsx->status_code/100 == 2) {
+ int i;
+ pjsip_contact_hdr *hdr;
+ pjsip_msg *msg;
+ pjsip_expires_hdr *expires;
+
+ rdata = event->src.rdata;
+ msg = rdata->msg;
+ hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL);
+ while (hdr) {
+ contact[contact_cnt++] = hdr;
+ hdr = hdr->next;
+ if (hdr == (void*)&msg->hdr)
+ break;
+ hdr = pjsip_msg_find_hdr(msg, PJSIP_H_CONTACT, hdr);
+ }
+
+ expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+
+ if (expires)
+ expiration = expires->ivalue;
+
+ for (i=0; i<contact_cnt; ++i) {
+ hdr = contact[i];
+ if (hdr->expires >= 0 && hdr->expires < expiration)
+ expiration = contact[i]->expires;
+ }
+
+ if (regc->auto_reg && expiration != 0 && expiration != 0xFFFF) {
+ pj_time_val delay = { 0, 0};
+
+ delay.sec = expiration - DELAY_BEFORE_REFRESH;
+ if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED &&
+ delay.sec > (pj_int32_t)regc->expires)
+ {
+ delay.sec = regc->expires;
+ }
+ if (delay.sec < DELAY_BEFORE_REFRESH)
+ delay.sec = DELAY_BEFORE_REFRESH;
+ regc->timer.cb = ®c_refresh_timer_cb;
+ regc->timer.id = REFRESH_TIMER;
+ regc->timer.user_data = regc;
+ pjsip_endpt_schedule_timer( regc->endpt, ®c->timer, &delay);
+ }
+
+ } else {
+ rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL;
+ }
+
+
+ /* Call callback. */
+ if (expiration == 0xFFFF) expiration = -1;
+ call_callback(regc, tsx->status_code,
+ (rdata ? &rdata->msg->line.status.reason
+ : pjsip_get_status_text(tsx->status_code)),
+ rdata, expiration,
+ contact_cnt, contact);
+
+ --regc->pending_tsx;
+ }
+
+ /* Delete the record if user destroy regc during the callback. */
+ if (regc->_delete_flag && regc->pending_tsx==0) {
+ pjsip_regc_destroy(regc);
+ }
+}
+
+PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata)
+{
+ int status;
+
+ /* Make sure we don't have pending transaction. */
+ if (regc->pending_tsx) {
+ pj_str_t reason = pj_str("Transaction in progress");
+ call_callback(regc, -1, &reason, NULL, -1, 0, NULL);
+ pjsip_tx_data_dec_ref( tdata );
+ return;
+ }
+
+ /* Invalidate message buffer. */
+ pjsip_tx_data_invalidate_msg(tdata);
+
+ /* Increment CSeq */
+ regc->cseq_hdr->cseq++;
+
+ /* Send. */
+ status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback);
+ if (status==0)
+ ++regc->pending_tsx;
+ else {
+ pj_str_t reason = pj_str("Unable to send request.");
+ call_callback(regc, status, &reason, NULL, -1, 0, NULL);
+ }
+}
+
+
diff --git a/pjsip/src/pjsip-ua/sip_ua.c b/pjsip/src/pjsip-ua/sip_ua.c index 6502a5e2..fa779349 100644 --- a/pjsip/src/pjsip-ua/sip_ua.c +++ b/pjsip/src/pjsip-ua/sip_ua.c @@ -1,491 +1,513 @@ -/* $Id$ - * - */ -#include <pjsip_mod_ua/sip_ua.h> -#include <pjsip_mod_ua/sip_dialog.h> -#include <pjsip_mod_ua/sip_ua_private.h> -#include <pjsip/sip_module.h> -#include <pjsip/sip_event.h> -#include <pjsip/sip_misc.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_transaction.h> -#include <pj/list.h> -#include <pj/log.h> -#include <pj/string.h> -#include <pj/guid.h> -#include <pj/os.h> -#include <pj/hash.h> -#include <pj/pool.h> - -#define PJSIP_POOL_LEN_USER_AGENT 1024 -#define PJSIP_POOL_INC_USER_AGENT 0 - - -#define LOG_THIS "useragent.." - -/* - * Static prototypes. - */ -static pj_status_t ua_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ); -static pj_status_t ua_start( struct pjsip_module *mod ); -static pj_status_t ua_deinit( struct pjsip_module *mod ); -static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt ); -static pjsip_dlg *find_dialog( pjsip_user_agent *ua, - pjsip_rx_data *rdata ); -static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, - pj_str_t *key ); -PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg ); - -/* - * Default UA instance. - */ -static pjsip_user_agent ua_instance; - -/* - * Module interface. - */ -static struct pjsip_module mod_ua = -{ - { "User-Agent", 10 }, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - NULL, /* User agent instance, initialized by APP. */ - 0, /* Number of methods supported (will be initialized later). */ - { 0 }, /* Array of methods (will be initialized later) */ - &ua_init, /* init_module() */ - &ua_start, /* start_module() */ - &ua_deinit, /* deinit_module() */ - &ua_tsx_handler, /* tsx_handler() */ -}; - -/* - * Initialize user agent instance. - */ -static pj_status_t ua_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - static pjsip_method m_invite, m_ack, m_cancel, m_bye; - pjsip_user_agent *ua = mod->mod_data; - extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */ - - pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD ); - pjsip_method_set( &m_ack, PJSIP_ACK_METHOD ); - pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD ); - pjsip_method_set( &m_bye, PJSIP_BYE_METHOD ); - - mod->method_cnt = 4; - mod->methods[0] = &m_invite; - mod->methods[1] = &m_ack; - mod->methods[2] = &m_cancel; - mod->methods[3] = &m_bye; - - /* Initialize the user agent. */ - ua->endpt = endpt; - ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA, - PJSIP_POOL_INC_UA); - if (!ua->pool) { - return -1; - } - ua->mod_id = id; - ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0); - if (!ua->mutex) { - return -1; - } - ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT); - if (ua->dlg_table == NULL) { - return -1; - } - pj_list_init(&ua->dlg_list); - - /* Initialize dialog lock. */ - pjsip_dlg_lock_tls_id = pj_thread_local_alloc(); - if (pjsip_dlg_lock_tls_id == -1) { - return -1; - } - pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL); - - return 0; -} - -/* - * Start user agent instance. - */ -static pj_status_t ua_start( struct pjsip_module *mod ) -{ - PJ_UNUSED_ARG(mod) - return 0; -} - -/* - * Destroy user agent. - */ -static pj_status_t ua_deinit( struct pjsip_module *mod ) -{ - pjsip_user_agent *ua = mod->mod_data; - - pj_mutex_unlock(ua->mutex); - - /* Release pool */ - if (ua->pool) { - pjsip_endpt_destroy_pool( ua->endpt, ua->pool ); - } - return 0; -} - -/* - * Get the module interface for the UA module. - */ -PJ_DEF(pjsip_module*) pjsip_ua_get_module(void) -{ - mod_ua.mod_data = &ua_instance; - return &mod_ua; -} - -/* - * Register callback to receive dialog notifications. - */ -PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua, - pjsip_dlg_callback *cb ) -{ - ua->dlg_cb = cb; -} - -/* - * Find dialog. - * This function is called for a new transactions, which a dialog hasn't been - * 'attached' to the transaction. - */ -static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata ) -{ - pjsip_dlg *dlg; - pj_str_t *tag; - - /* Non-CANCEL requests/response can be found by looking at the tag in the - * hash table. CANCEL requests don't have tags, so instead we'll try to - * find the UAS INVITE transaction in endpoint's hash table - */ - if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) { - - /* Create key for the rdata, but this time, use INVITE as the - * method. - */ - pj_str_t key; - pjsip_role_e role; - pjsip_method invite_method; - pjsip_transaction *invite_tsx; - - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - role = PJSIP_ROLE_UAS; - } else { - role = PJSIP_ROLE_UAC; - } - pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD); - pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata); - - /* Lookup the INVITE transaction */ - invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key); - - /* We should find the dialog attached to the INVITE transaction */ - return invite_tsx ? - (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL; - - } else { - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - tag = &rdata->to_tag; - } else { - tag = &rdata->from_tag; - } - /* Find the dialog in UA hash table */ - pj_mutex_lock(ua->mutex); - dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen ); - pj_mutex_unlock(ua->mutex); - } - - return dlg; -} - -/* - * This function receives event notification from transactions. It is called by - * endpoint. - */ -static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_user_agent *ua = mod->mod_data; - pjsip_dlg *dlg = NULL; - pjsip_transaction *tsx = event->obj.tsx; - - PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)", - (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type), - pjsip_event_str(event->src_type), event->src.data)); - - /* Special case to handle ACK which doesn't match any INVITE transactions. */ - if (event->type == PJSIP_EVENT_RX_ACK_MSG) { - /* Find the dialog based on the "tag". */ - dlg = find_dialog( ua, event->src.rdata ); - - /* We should be able to find it. */ - if (!dlg) { - PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK")); - return; - } - - /* Match CSeq with pending INVITE in dialog. */ - if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) { - /* A match found. */ - tsx = dlg->invite_tsx; - - /* Pass the event to transaction if transaction handles ACK. */ - if (tsx->handle_ack) { - PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction")); - pjsip_tsx_on_rx_msg(tsx, event->src.rdata); - return; - } - } else { - tsx = NULL; - PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK")); - return; - } - } - - /* For discard event, transaction is NULL. */ - if (tsx == NULL) { - return; - } - - /* Try to pickup the dlg from the transaction. */ - dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id]; - - if (dlg != NULL) { - - /* Nothing to do now. */ - - } else if (event->src_type == PJSIP_EVENT_RX_MSG) { - - /* This must be a new UAS transaction. */ - - /* Finds dlg that can handle this transaction. */ - dlg = find_dialog( ua, event->src.rdata); - - /* Create a new dlg if there's no existing dlg that can handle - the request, ONLY if the incoming message is an INVITE request. - */ - if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) { - - if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) { - /* Create new dialog. */ - dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS ); - - if (dlg == NULL || - pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0) - { - pjsip_tx_data *tdata; - - /* Dialog initialization has failed. Respond request with 500 */ - if (dlg) { - pjsip_ua_destroy_dialog(dlg); - } - tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, - PJSIP_SC_INTERNAL_SERVER_ERROR); - if (tdata) { - pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); - } - return; - } - - } else { - pjsip_tx_data *tdata; - - /* Check the method */ - switch (tsx->method.id) { - case PJSIP_INVITE_METHOD: - case PJSIP_ACK_METHOD: - case PJSIP_BYE_METHOD: - case PJSIP_CANCEL_METHOD: - /* Stale non-INVITE request. - * For now, respond all stale requests with 481 (?). - */ - tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata, - PJSIP_SC_CALL_TSX_DOES_NOT_EXIST); - if (tdata) { - pjsip_tsx_on_tx_msg( event->obj.tsx, tdata ); - } - break; - } - - return; - } - } else { - /* Check the method */ - switch (tsx->method.id) { - case PJSIP_INVITE_METHOD: - case PJSIP_ACK_METHOD: - case PJSIP_BYE_METHOD: - case PJSIP_CANCEL_METHOD: - /* These methods belongs to dialog. - * If we receive these methods while no dialog is found, - * then it must be a stale responses. - */ - break; - default: - return; - } - - } - - if (dlg == NULL) { - PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d", - event->src.rdata, - pj_sockaddr_get_str_addr(&event->src.rdata->addr), - pj_sockaddr_get_port(&event->src.rdata->addr))); - } - - /* Set the dlg in the transaction (dlg can be NULL). */ - tsx->module_data[ua->mod_id] = dlg; - - } else { - /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG - * if UAS is responding to a transaction which does not exist. - * Just ignore. - */ - return; - } - - /* Pass the event to the dlg. */ - if (dlg) { - pjsip_dlg_on_tsx_event(dlg, tsx, event); - } -} - -/* - * Register dialog to UA. - */ -static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg, - pj_str_t *key ) -{ - /* Assure that no entry with similar key exists in the hash table. */ - pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0); - - /* Insert entry to hash table. */ - pj_hash_set( dlg->pool, ua->dlg_table, - key->ptr, key->slen, dlg); - - /* Insert to the list. */ - pj_list_insert_before(&ua->dlg_list, dlg); - return PJ_SUCCESS; -} - -/* - * Create a new dialog. - */ -PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua, - pjsip_role_e role ) -{ - pj_pool_t *pool; - pjsip_dlg *dlg; - - PJ_UNUSED_ARG(ua) - - /* Create pool for the dialog. */ - pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p", - PJSIP_POOL_LEN_DIALOG, - PJSIP_POOL_INC_DIALOG); - - /* Create the dialog. */ - dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg)); - dlg->pool = pool; - dlg->ua = ua; - dlg->role = role; - sprintf(dlg->obj_name, "dlg%p", dlg); - - /* Create mutex for the dialog. */ - dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0); - if (!dlg->mutex) { - pjsip_endpt_destroy_pool(ua->endpt, pool); - return NULL; - } - - /* Create unique tag for the dialog. */ - pj_create_unique_string( pool, &dlg->local.tag ); - - /* Register dialog. */ - pj_mutex_lock(ua->mutex); - if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) { - pj_mutex_unlock(ua->mutex); - pj_mutex_destroy(dlg->mutex); - pjsip_endpt_destroy_pool( ua->endpt, pool ); - return NULL; - } - pj_mutex_unlock(ua->mutex); - - PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role))); - return dlg; -} - -/* - * Destroy dialog. - */ -PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg ) -{ - PJ_LOG(5, (dlg->obj_name, "destroying..")); - - /* Lock dialog's mutex. - * Check the mutex validity first since this function can be called - * on dialog initialization failure (which might be because mutex could not - * be allocated in the first place). - */ - if (dlg->mutex) { - pj_mutex_lock(dlg->mutex); - } - - /* This must be called while holding dialog's mutex, if any. */ - pjsip_on_dialog_destroyed(dlg); - - /* Lock UA. */ - pj_mutex_lock(dlg->ua->mutex); - - /* Erase from hash table. */ - pj_hash_set( dlg->pool, dlg->ua->dlg_table, - dlg->local.tag.ptr, dlg->local.tag.slen, NULL); - - /* Erase from the list. */ - pj_list_erase(dlg); - - /* Unlock UA. */ - pj_mutex_unlock(dlg->ua->mutex); - - /* Unlock mutex. */ - if (dlg->mutex) { - pj_mutex_unlock(dlg->mutex); - } - - /* Destroy the pool. */ - pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool); -} - -/* - * Dump user agent state to log file. - */ -PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua) -{ -#if PJ_LOG_MAX_LEVEL >= 3 - PJ_LOG(3,(LOG_THIS, "Dumping user agent")); - PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u", - pj_pool_get_capacity(ua->pool), - pj_pool_get_used_size(ua->pool))); - PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table))); - - if (pj_hash_count(ua->dlg_table)) { - pjsip_dlg *dlg; - - PJ_LOG(3,(LOG_THIS, " Dumping dialog list:")); - dlg = ua->dlg_list.next; - while (dlg != (pjsip_dlg*) &ua->dlg_list) { - PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name, - pjsip_dlg_state_str(dlg->state))); - dlg = dlg->next; - } - } -#endif -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip_mod_ua/sip_ua.h>
+#include <pjsip_mod_ua/sip_dialog.h>
+#include <pjsip_mod_ua/sip_ua_private.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pj/list.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/os.h>
+#include <pj/hash.h>
+#include <pj/pool.h>
+
+#define PJSIP_POOL_LEN_USER_AGENT 1024
+#define PJSIP_POOL_INC_USER_AGENT 0
+
+
+#define LOG_THIS "useragent.."
+
+/*
+ * Static prototypes.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id );
+static pj_status_t ua_start( struct pjsip_module *mod );
+static pj_status_t ua_deinit( struct pjsip_module *mod );
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *evt );
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua,
+ pjsip_rx_data *rdata );
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+ pj_str_t *key );
+PJ_DECL(void) pjsip_on_dialog_destroyed( pjsip_dlg *dlg );
+
+/*
+ * Default UA instance.
+ */
+static pjsip_user_agent ua_instance;
+
+/*
+ * Module interface.
+ */
+static struct pjsip_module mod_ua =
+{
+ { "User-Agent", 10 }, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ NULL, /* User agent instance, initialized by APP. */
+ 0, /* Number of methods supported (will be initialized later). */
+ { 0 }, /* Array of methods (will be initialized later) */
+ &ua_init, /* init_module() */
+ &ua_start, /* start_module() */
+ &ua_deinit, /* deinit_module() */
+ &ua_tsx_handler, /* tsx_handler() */
+};
+
+/*
+ * Initialize user agent instance.
+ */
+static pj_status_t ua_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ static pjsip_method m_invite, m_ack, m_cancel, m_bye;
+ pjsip_user_agent *ua = mod->mod_data;
+ extern int pjsip_dlg_lock_tls_id; /* defined in sip_dialog.c */
+
+ pjsip_method_set( &m_invite, PJSIP_INVITE_METHOD );
+ pjsip_method_set( &m_ack, PJSIP_ACK_METHOD );
+ pjsip_method_set( &m_cancel, PJSIP_CANCEL_METHOD );
+ pjsip_method_set( &m_bye, PJSIP_BYE_METHOD );
+
+ mod->method_cnt = 4;
+ mod->methods[0] = &m_invite;
+ mod->methods[1] = &m_ack;
+ mod->methods[2] = &m_cancel;
+ mod->methods[3] = &m_bye;
+
+ /* Initialize the user agent. */
+ ua->endpt = endpt;
+ ua->pool = pjsip_endpt_create_pool(endpt, "pua%p", PJSIP_POOL_LEN_UA,
+ PJSIP_POOL_INC_UA);
+ if (!ua->pool) {
+ return -1;
+ }
+ ua->mod_id = id;
+ ua->mutex = pj_mutex_create(ua->pool, " ua%p", 0);
+ if (!ua->mutex) {
+ return -1;
+ }
+ ua->dlg_table = pj_hash_create(ua->pool, PJSIP_MAX_DIALOG_COUNT);
+ if (ua->dlg_table == NULL) {
+ return -1;
+ }
+ pj_list_init(&ua->dlg_list);
+
+ /* Initialize dialog lock. */
+ pjsip_dlg_lock_tls_id = pj_thread_local_alloc();
+ if (pjsip_dlg_lock_tls_id == -1) {
+ return -1;
+ }
+ pj_thread_local_set(pjsip_dlg_lock_tls_id, NULL);
+
+ return 0;
+}
+
+/*
+ * Start user agent instance.
+ */
+static pj_status_t ua_start( struct pjsip_module *mod )
+{
+ PJ_UNUSED_ARG(mod)
+ return 0;
+}
+
+/*
+ * Destroy user agent.
+ */
+static pj_status_t ua_deinit( struct pjsip_module *mod )
+{
+ pjsip_user_agent *ua = mod->mod_data;
+
+ pj_mutex_unlock(ua->mutex);
+
+ /* Release pool */
+ if (ua->pool) {
+ pjsip_endpt_destroy_pool( ua->endpt, ua->pool );
+ }
+ return 0;
+}
+
+/*
+ * Get the module interface for the UA module.
+ */
+PJ_DEF(pjsip_module*) pjsip_ua_get_module(void)
+{
+ mod_ua.mod_data = &ua_instance;
+ return &mod_ua;
+}
+
+/*
+ * Register callback to receive dialog notifications.
+ */
+PJ_DEF(void) pjsip_ua_set_dialog_callback( pjsip_user_agent *ua,
+ pjsip_dlg_callback *cb )
+{
+ ua->dlg_cb = cb;
+}
+
+/*
+ * Find dialog.
+ * This function is called for a new transactions, which a dialog hasn't been
+ * 'attached' to the transaction.
+ */
+static pjsip_dlg *find_dialog( pjsip_user_agent *ua, pjsip_rx_data *rdata )
+{
+ pjsip_dlg *dlg;
+ pj_str_t *tag;
+
+ /* Non-CANCEL requests/response can be found by looking at the tag in the
+ * hash table. CANCEL requests don't have tags, so instead we'll try to
+ * find the UAS INVITE transaction in endpoint's hash table
+ */
+ if (rdata->cseq->method.id == PJSIP_CANCEL_METHOD) {
+
+ /* Create key for the rdata, but this time, use INVITE as the
+ * method.
+ */
+ pj_str_t key;
+ pjsip_role_e role;
+ pjsip_method invite_method;
+ pjsip_transaction *invite_tsx;
+
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ role = PJSIP_ROLE_UAS;
+ } else {
+ role = PJSIP_ROLE_UAC;
+ }
+ pjsip_method_set(&invite_method, PJSIP_INVITE_METHOD);
+ pjsip_tsx_create_key(rdata->pool, &key, role, &invite_method, rdata);
+
+ /* Lookup the INVITE transaction */
+ invite_tsx = pjsip_endpt_find_tsx(ua->endpt, &key);
+
+ /* We should find the dialog attached to the INVITE transaction */
+ return invite_tsx ?
+ (pjsip_dlg*) invite_tsx->module_data[ua->mod_id] : NULL;
+
+ } else {
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ tag = &rdata->to_tag;
+ } else {
+ tag = &rdata->from_tag;
+ }
+ /* Find the dialog in UA hash table */
+ pj_mutex_lock(ua->mutex);
+ dlg = pj_hash_get( ua->dlg_table, tag->ptr, tag->slen );
+ pj_mutex_unlock(ua->mutex);
+ }
+
+ return dlg;
+}
+
+/*
+ * This function receives event notification from transactions. It is called by
+ * endpoint.
+ */
+static void ua_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_user_agent *ua = mod->mod_data;
+ pjsip_dlg *dlg = NULL;
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ PJ_LOG(5, (LOG_THIS, "ua_tsx_handler(tsx=%s, evt=%s, src=%s, data=%p)",
+ (tsx ? tsx->obj_name : "NULL"), pjsip_event_str(event->type),
+ pjsip_event_str(event->src_type), event->src.data));
+
+ /* Special case to handle ACK which doesn't match any INVITE transactions. */
+ if (event->type == PJSIP_EVENT_RX_ACK_MSG) {
+ /* Find the dialog based on the "tag". */
+ dlg = find_dialog( ua, event->src.rdata );
+
+ /* We should be able to find it. */
+ if (!dlg) {
+ PJ_LOG(4,(LOG_THIS, "Unable to find dialog for incoming ACK"));
+ return;
+ }
+
+ /* Match CSeq with pending INVITE in dialog. */
+ if (dlg->invite_tsx && dlg->invite_tsx->cseq==event->src.rdata->cseq->cseq) {
+ /* A match found. */
+ tsx = dlg->invite_tsx;
+
+ /* Pass the event to transaction if transaction handles ACK. */
+ if (tsx->handle_ack) {
+ PJ_LOG(4,(LOG_THIS, "Re-routing strandled ACK to transaction"));
+ pjsip_tsx_on_rx_msg(tsx, event->src.rdata);
+ return;
+ }
+ } else {
+ tsx = NULL;
+ PJ_LOG(4,(LOG_THIS, "Unable to find INVITE tsx for incoming ACK"));
+ return;
+ }
+ }
+
+ /* For discard event, transaction is NULL. */
+ if (tsx == NULL) {
+ return;
+ }
+
+ /* Try to pickup the dlg from the transaction. */
+ dlg = (pjsip_dlg*) tsx->module_data[ua->mod_id];
+
+ if (dlg != NULL) {
+
+ /* Nothing to do now. */
+
+ } else if (event->src_type == PJSIP_EVENT_RX_MSG) {
+
+ /* This must be a new UAS transaction. */
+
+ /* Finds dlg that can handle this transaction. */
+ dlg = find_dialog( ua, event->src.rdata);
+
+ /* Create a new dlg if there's no existing dlg that can handle
+ the request, ONLY if the incoming message is an INVITE request.
+ */
+ if (dlg==NULL && event->src.rdata->msg->type == PJSIP_REQUEST_MSG) {
+
+ if (event->src.rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD) {
+ /* Create new dialog. */
+ dlg = pjsip_ua_create_dialog( ua, PJSIP_ROLE_UAS );
+
+ if (dlg == NULL ||
+ pjsip_dlg_init_from_rdata( dlg, event->src.rdata) != 0)
+ {
+ pjsip_tx_data *tdata;
+
+ /* Dialog initialization has failed. Respond request with 500 */
+ if (dlg) {
+ pjsip_ua_destroy_dialog(dlg);
+ }
+ tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
+ PJSIP_SC_INTERNAL_SERVER_ERROR);
+ if (tdata) {
+ pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+ }
+ return;
+ }
+
+ } else {
+ pjsip_tx_data *tdata;
+
+ /* Check the method */
+ switch (tsx->method.id) {
+ case PJSIP_INVITE_METHOD:
+ case PJSIP_ACK_METHOD:
+ case PJSIP_BYE_METHOD:
+ case PJSIP_CANCEL_METHOD:
+ /* Stale non-INVITE request.
+ * For now, respond all stale requests with 481 (?).
+ */
+ tdata = pjsip_endpt_create_response(ua->endpt, event->src.rdata,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST);
+ if (tdata) {
+ pjsip_tsx_on_tx_msg( event->obj.tsx, tdata );
+ }
+ break;
+ }
+
+ return;
+ }
+ } else {
+ /* Check the method */
+ switch (tsx->method.id) {
+ case PJSIP_INVITE_METHOD:
+ case PJSIP_ACK_METHOD:
+ case PJSIP_BYE_METHOD:
+ case PJSIP_CANCEL_METHOD:
+ /* These methods belongs to dialog.
+ * If we receive these methods while no dialog is found,
+ * then it must be a stale responses.
+ */
+ break;
+ default:
+ return;
+ }
+
+ }
+
+ if (dlg == NULL) {
+ PJ_LOG(3, (LOG_THIS, "Receives spurious rdata %p from %s:%d",
+ event->src.rdata,
+ pj_sockaddr_get_str_addr(&event->src.rdata->addr),
+ pj_sockaddr_get_port(&event->src.rdata->addr)));
+ }
+
+ /* Set the dlg in the transaction (dlg can be NULL). */
+ tsx->module_data[ua->mod_id] = dlg;
+
+ } else {
+ /* This CAN happen with event->src_type == PJSIP_EVENT_TX_MSG
+ * if UAS is responding to a transaction which does not exist.
+ * Just ignore.
+ */
+ return;
+ }
+
+ /* Pass the event to the dlg. */
+ if (dlg) {
+ pjsip_dlg_on_tsx_event(dlg, tsx, event);
+ }
+}
+
+/*
+ * Register dialog to UA.
+ */
+static pj_status_t ua_register_dialog( pjsip_user_agent *ua, pjsip_dlg *dlg,
+ pj_str_t *key )
+{
+ /* Assure that no entry with similar key exists in the hash table. */
+ pj_assert( pj_hash_get( ua->dlg_table, key->ptr, key->slen) == 0);
+
+ /* Insert entry to hash table. */
+ pj_hash_set( dlg->pool, ua->dlg_table,
+ key->ptr, key->slen, dlg);
+
+ /* Insert to the list. */
+ pj_list_insert_before(&ua->dlg_list, dlg);
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create a new dialog.
+ */
+PJ_DEF(pjsip_dlg*) pjsip_ua_create_dialog( pjsip_user_agent *ua,
+ pjsip_role_e role )
+{
+ pj_pool_t *pool;
+ pjsip_dlg *dlg;
+
+ PJ_UNUSED_ARG(ua)
+
+ /* Create pool for the dialog. */
+ pool = pjsip_endpt_create_pool( ua->endpt, "pdlg%p",
+ PJSIP_POOL_LEN_DIALOG,
+ PJSIP_POOL_INC_DIALOG);
+
+ /* Create the dialog. */
+ dlg = pj_pool_calloc(pool, 1, sizeof(pjsip_dlg));
+ dlg->pool = pool;
+ dlg->ua = ua;
+ dlg->role = role;
+ sprintf(dlg->obj_name, "dlg%p", dlg);
+
+ /* Create mutex for the dialog. */
+ dlg->mutex = pj_mutex_create(dlg->pool, "mdlg%p", 0);
+ if (!dlg->mutex) {
+ pjsip_endpt_destroy_pool(ua->endpt, pool);
+ return NULL;
+ }
+
+ /* Create unique tag for the dialog. */
+ pj_create_unique_string( pool, &dlg->local.tag );
+
+ /* Register dialog. */
+ pj_mutex_lock(ua->mutex);
+ if (ua_register_dialog(ua, dlg, &dlg->local.tag) != PJ_SUCCESS) {
+ pj_mutex_unlock(ua->mutex);
+ pj_mutex_destroy(dlg->mutex);
+ pjsip_endpt_destroy_pool( ua->endpt, pool );
+ return NULL;
+ }
+ pj_mutex_unlock(ua->mutex);
+
+ PJ_LOG(4, (dlg->obj_name, "new %s dialog created", pjsip_role_name(role)));
+ return dlg;
+}
+
+/*
+ * Destroy dialog.
+ */
+PJ_DEF(void) pjsip_ua_destroy_dialog( pjsip_dlg *dlg )
+{
+ PJ_LOG(5, (dlg->obj_name, "destroying.."));
+
+ /* Lock dialog's mutex.
+ * Check the mutex validity first since this function can be called
+ * on dialog initialization failure (which might be because mutex could not
+ * be allocated in the first place).
+ */
+ if (dlg->mutex) {
+ pj_mutex_lock(dlg->mutex);
+ }
+
+ /* This must be called while holding dialog's mutex, if any. */
+ pjsip_on_dialog_destroyed(dlg);
+
+ /* Lock UA. */
+ pj_mutex_lock(dlg->ua->mutex);
+
+ /* Erase from hash table. */
+ pj_hash_set( dlg->pool, dlg->ua->dlg_table,
+ dlg->local.tag.ptr, dlg->local.tag.slen, NULL);
+
+ /* Erase from the list. */
+ pj_list_erase(dlg);
+
+ /* Unlock UA. */
+ pj_mutex_unlock(dlg->ua->mutex);
+
+ /* Unlock mutex. */
+ if (dlg->mutex) {
+ pj_mutex_unlock(dlg->mutex);
+ }
+
+ /* Destroy the pool. */
+ pjsip_endpt_destroy_pool( dlg->ua->endpt, dlg->pool);
+}
+
+/*
+ * Dump user agent state to log file.
+ */
+PJ_DEF(void) pjsip_ua_dump(pjsip_user_agent *ua)
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ PJ_LOG(3,(LOG_THIS, "Dumping user agent"));
+ PJ_LOG(3,(LOG_THIS, " Pool capacity=%u, used=%u",
+ pj_pool_get_capacity(ua->pool),
+ pj_pool_get_used_size(ua->pool)));
+ PJ_LOG(3,(LOG_THIS, " Number of dialogs=%u", pj_hash_count(ua->dlg_table)));
+
+ if (pj_hash_count(ua->dlg_table)) {
+ pjsip_dlg *dlg;
+
+ PJ_LOG(3,(LOG_THIS, " Dumping dialog list:"));
+ dlg = ua->dlg_list.next;
+ while (dlg != (pjsip_dlg*) &ua->dlg_list) {
+ PJ_LOG(3, (LOG_THIS, " %s %s", dlg->obj_name,
+ pjsip_dlg_state_str(dlg->state)));
+ dlg = dlg->next;
+ }
+ }
+#endif
+}
+
diff --git a/pjsip/src/pjsip-ua/sip_ua_private.h b/pjsip/src/pjsip-ua/sip_ua_private.h index 471c4db0..cc6dfcf8 100644 --- a/pjsip/src/pjsip-ua/sip_ua_private.h +++ b/pjsip/src/pjsip-ua/sip_ua_private.h @@ -1,22 +1,44 @@ -/* $Id$ - * - */ - -#ifndef __PJSIP_UA_PRIVATE_H__ -#define __PJSIP_UA_PRIVATE_H__ - - -/* - * Internal dialog functions. - */ -pj_status_t pjsip_dlg_init_from_rdata( pjsip_dlg *dlg, - pjsip_rx_data *rdata ); - - -void pjsip_dlg_on_tsx_event( pjsip_dlg *dlg, - pjsip_transaction *tsx, - pjsip_event *event); - - -#endif /* __PJSIP_UA_PRIVATE_H__ */ - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#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.c b/pjsip/src/pjsip/sip_auth.c index 030b4a40..bf161a1a 100644 --- a/pjsip/src/pjsip/sip_auth.c +++ b/pjsip/src/pjsip/sip_auth.c @@ -1,788 +1,810 @@ -/* $Id$ - */ -#include <pjsip/sip_auth.h> -#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */ -#include <pjsip/sip_transport.h> -#include <pjsip/sip_endpoint.h> -#include <pjlib-util/md5.h> -#include <pj/log.h> -#include <pj/string.h> -#include <pj/pool.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_auth.h>
+#include <pjsip/sip_auth_parser.h> /* just to get pjsip_DIGEST_STR */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjlib-util/md5.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/pool.h>
#include <pj/guid.h>
-#include <pj/assert.h> +#include <pj/assert.h>
#include <pj/ctype.h>
- -/* Length of digest string. */ -#define MD5STRLEN 32 - -/* Maximum stack size we use for storing username+realm+password etc. */ -#define MAX_TEMP 128 - -/* A macro just to get rid of type mismatch between char and unsigned char */ -#define MD5_APPEND(pms,buf,len) md5_append(pms, (const unsigned char*)buf, len) - -/* Logging. */ -#define THIS_FILE "sip_auth.c" -#if 0 -# define AUTH_TRACE_(expr) PJ_LOG(3, expr) -#else -# define AUTH_TRACE_(expr) -#endif - -static const char hex[] = "0123456789abcdef"; - -/* Transform digest to string. - * output must be at least MD5STRLEN+1 bytes. - * - * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED! - */ -static void digest2str(const unsigned char digest[], char *output) -{ - char *p = output; - int i; - - for (i = 0; i<16; ++i) { - int val = digest[i]; - *p++ = hex[val >> 4]; - *p++ = hex[val & 0x0F]; - } -} - -/* - * Create response digest based on the parameters and store the - * digest ASCII in 'result'. - */ -static void create_digest( pj_str_t *result, - const pj_str_t *nonce, - const pj_str_t *nc, - const pj_str_t *cnonce, - const pj_str_t *qop, - const pj_str_t *uri, - const pjsip_cred_info *cred_info, - const pj_str_t *method) -{ - char ha1[MD5STRLEN]; - char ha2[MD5STRLEN]; - unsigned char digest[16]; - md5_state_t pms; - - pj_assert(result->slen >= MD5STRLEN); - - AUTH_TRACE_((THIS_FILE, "Begin creating digest")); - - if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) { - /*** - *** ha1 = MD5(username ":" realm ":" password) - ***/ - md5_init(&pms); - MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen); - md5_finish(&pms, digest); - - digest2str(digest, ha1); - - } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) { - pj_assert(cred_info->data.slen == 32); - pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen ); - } - - AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1)); - - /*** - *** ha2 = MD5(method ":" req_uri) - ***/ - md5_init(&pms); - MD5_APPEND( &pms, method->ptr, method->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, uri->ptr, uri->slen); - md5_finish(&pms, digest); - digest2str(digest, ha2); - - AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2)); - - /*** - *** When qop is not used: - *** response = MD5(ha1 ":" nonce ":" ha2) - *** - *** When qop=auth is used: - *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2) - ***/ - md5_init(&pms); - MD5_APPEND( &pms, ha1, MD5STRLEN); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, nonce->ptr, nonce->slen); - if (qop && qop->slen != 0) { - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, nc->ptr, nc->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, cnonce->ptr, cnonce->slen); - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, qop->ptr, qop->slen); - } - MD5_APPEND( &pms, ":", 1); - MD5_APPEND( &pms, ha2, MD5STRLEN); - - /* This is the final response digest. */ - md5_finish(&pms, digest); - - /* Convert digest to string and store in chal->response. */ - result->slen = MD5STRLEN; - digest2str(digest, result->ptr); - - AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr)); - AUTH_TRACE_((THIS_FILE, "Digest created")); -} - -/* - * Finds out if qop offer contains "auth" token. - */ -static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer) -{ - pj_str_t qop; - char *p; - - pj_strdup_with_null( pool, &qop, qop_offer); - p = qop.ptr; - while (*p) { - *p = (char)pj_tolower(*p); - ++p; - } - - p = qop.ptr; - while (*p) { - if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') { - int e = *(p+4); - if (e=='"' || e==',' || e==0) - return PJ_TRUE; - else - p += 4; - } else { - ++p; - } - } - - return PJ_FALSE; -} - -/* - * Generate response digest. - * Most of the parameters to generate the digest (i.e. username, realm, uri, - * and nonce) are expected to be in the credential. Additional parameters (i.e. - * password and method param) should be supplied in the argument. - * - * The resulting digest will be stored in cred->response. - * The pool is used to allocate 32 bytes to store the digest in cred->response. - */ -static pj_status_t respond_digest( pj_pool_t *pool, - pjsip_digest_credential *cred, - const pjsip_digest_challenge *chal, - const pj_str_t *uri, - const pjsip_cred_info *cred_info, - const pj_str_t *cnonce, - pj_uint32_t nc, - const pj_str_t *method) -{ - /* Check algorithm is supported. We only support MD5. */ - if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR)) - { - PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"", - chal->algorithm.slen, chal->algorithm.ptr)); - return -1; - } - - /* Build digest credential from arguments. */ - pj_strdup(pool, &cred->username, &cred_info->username); - pj_strdup(pool, &cred->realm, &chal->realm); - pj_strdup(pool, &cred->nonce, &chal->nonce); - pj_strdup(pool, &cred->uri, uri); - cred->algorithm = pjsip_MD5_STR; - pj_strdup(pool, &cred->opaque, &chal->opaque); - - /* Allocate memory. */ - cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN); - cred->response.slen = MD5STRLEN; - - if (chal->qop.slen == 0) { - /* Server doesn't require quality of protection. */ - - /* Convert digest to string and store in chal->response. */ - create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL, - uri, cred_info, method); - - } else if (has_auth_qop(pool, &chal->qop)) { - /* Server requires quality of protection. - * We respond with selecting "qop=auth" protection. - */ - cred->qop = pjsip_AUTH_STR; - cred->nc.ptr = pj_pool_alloc(pool, 16); - pj_snprintf(cred->nc.ptr, 16, "%06u", nc); - - if (cnonce && cnonce->slen) { - pj_strdup(pool, &cred->cnonce, cnonce); - } else { - pj_str_t dummy_cnonce = { "b39971", 6}; - pj_strdup(pool, &cred->cnonce, &dummy_cnonce); - } - - create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce, - &pjsip_AUTH_STR, uri, cred_info, method ); - - } else { - /* Server requires quality protection that we don't support. */ - PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s", - chal->qop.slen, chal->qop.ptr)); - return -1; - } - - return 0; -} - -#if PJSIP_AUTH_QOP_SUPPORT -/* - * Update authentication session with a challenge. - */ -static void update_digest_session( pj_pool_t *ses_pool, - pjsip_auth_session *auth_sess, - const pjsip_www_authenticate_hdr *hdr ) -{ - if (hdr->challenge.digest.qop.slen == 0) - return; - - /* Initialize cnonce and qop if not present. */ - if (auth_sess->cnonce.slen == 0) { - /* Save the whole challenge */ - auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr); - - /* Create cnonce */ - pj_create_unique_string( ses_pool, &auth_sess->cnonce ); - - /* Initialize nonce-count */ - auth_sess->nc = 1; - - /* Save realm. */ - pj_assert(auth_sess->realm.slen != 0); - if (auth_sess->realm.slen == 0) { - pj_strdup(ses_pool, &auth_sess->realm, - &hdr->challenge.digest.realm); - } - - } else { - /* Update last_nonce and nonce-count */ - if (!pj_strcmp(&hdr->challenge.digest.nonce, - &auth_sess->last_chal->challenge.digest.nonce)) - { - /* Same nonce, increment nonce-count */ - ++auth_sess->nc; - } else { - /* Server gives new nonce. */ - pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce, - &hdr->challenge.digest.nonce); - /* Has the opaque changed? */ - if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque, - &hdr->challenge.digest.opaque)) - { - pj_strdup(ses_pool, - &auth_sess->last_chal->challenge.digest.opaque, - &hdr->challenge.digest.opaque); - } - auth_sess->nc = 1; - } - } -} -#endif /* PJSIP_AUTH_QOP_SUPPORT */ - - -/* Find authentication session in the list. */ -static pjsip_auth_session *find_session( pjsip_auth_session *sess_list, - const pj_str_t *realm ) -{ - pjsip_auth_session *sess = sess_list->next; - while (sess != sess_list) { - if (pj_stricmp(&sess->realm, realm) == 0) - return sess; - sess = sess->next; - } - - return NULL; -} - -/* - * Create Authorization/Proxy-Authorization response header based on the challege - * in WWW-Authenticate/Proxy-Authenticate header. - */ -PJ_DEF(pjsip_authorization_hdr*) -pjsip_auth_respond( pj_pool_t *req_pool, - const pjsip_www_authenticate_hdr *hdr, - const pjsip_uri *uri, - const pjsip_cred_info *cred_info, - const pjsip_method *method, - pj_pool_t *sess_pool, - pjsip_auth_session *auth_sess) -{ - pjsip_authorization_hdr *auth; - char tmp[PJSIP_MAX_URL_SIZE]; - pj_str_t uri_str; - pj_pool_t *pool; - - pj_assert(hdr != NULL); - pj_assert(uri != NULL); - pj_assert(cred_info != NULL); - pj_assert(method != NULL); - - /* Print URL in the original request. */ - uri_str.ptr = tmp; - uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp)); - if (uri_str.slen < 1) { - pj_assert(!"URL is too long!"); - PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!")); - return NULL; - } - -# if (PJSIP_AUTH_HEADER_CACHING) - { - pool = sess_pool; - PJ_UNUSED_ARG(req_pool); - } -# else - { - pool = req_pool; - PJ_UNUSED_ARG(sess_pool); - } -# endif - - if (hdr->type == PJSIP_H_WWW_AUTHENTICATE) - auth = pjsip_authorization_hdr_create(pool); - else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE) - auth = pjsip_proxy_authorization_hdr_create(pool); - else { - pj_assert(0); - return NULL; - } - - /* Only support digest scheme at the moment. */ - if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { - pj_status_t rc; - pj_str_t *cnonce = NULL; - pj_uint32_t nc = 1; - - /* Update the session (nonce-count etc) if required. */ -# if PJSIP_AUTH_QOP_SUPPORT - { - if (auth_sess) { - update_digest_session( sess_pool, auth_sess, hdr ); - - cnonce = &auth_sess->cnonce; - nc = auth_sess->nc; - } - } -# endif /* PJSIP_AUTH_QOP_SUPPORT */ - - auth->scheme = pjsip_DIGEST_STR; - rc = respond_digest( pool, &auth->credential.digest, - &hdr->challenge.digest, &uri_str, cred_info, - cnonce, nc, &method->name); - if (rc != 0) - return NULL; - - /* Set qop type in auth session the first time only. */ - if (hdr->challenge.digest.qop.slen != 0 && auth_sess) { - if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { - pj_str_t *qop_val = &auth->credential.digest.qop; - if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) { - auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH; - } else { - auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN; - } - } - } - } else { - auth = NULL; - } - - /* Keep the new authorization header in the cache, only - * if no qop is not present. - */ -# if PJSIP_AUTH_HEADER_CACHING - { - if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) { - pjsip_cached_auth_hdr *cached_hdr; - - /* Delete old header with the same method. */ - cached_hdr = auth_sess->cached_hdr.next; - while (cached_hdr != &auth_sess->cached_hdr) { - if (pjsip_method_cmp(method, &cached_hdr->method)==0) - break; - cached_hdr = cached_hdr->next; - } - - /* Save the header to the list. */ - if (cached_hdr != &auth_sess->cached_hdr) { - cached_hdr->hdr = auth; - } else { - cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr)); - pjsip_method_copy( pool, &cached_hdr->method, method); - cached_hdr->hdr = auth; - pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr ); - } - } - } -# endif - - return auth; - -} - -/* Verify incoming Authorization/Proxy-Authorization header against existing - * credentials. Will return TRUE if the authorization request matches any of - * the credential. - */ -PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr, - const pj_str_t *method, - const pjsip_cred_info *cred_info ) -{ - if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) { - char digest_buf[MD5STRLEN]; - pj_str_t digest; - const pjsip_digest_credential *dig = &hdr->credential.digest; - - /* Check that username match. */ - if (pj_strcmp(&dig->username, &cred_info->username) != 0) - return PJ_FALSE; - - /* Check that realm match. */ - if (pj_strcmp(&dig->realm, &cred_info->realm) != 0) - return PJ_FALSE; - - /* Prepare for our digest calculation. */ - digest.ptr = digest_buf; - digest.slen = MD5STRLEN; - - /* Create digest for comparison. */ - create_digest( &digest, - &hdr->credential.digest.nonce, - &hdr->credential.digest.nc, - &hdr->credential.digest.cnonce, - &hdr->credential.digest.qop, - &hdr->credential.digest.uri, - cred_info, - method ); - - return pj_stricmp(&digest, &hdr->credential.digest.response) == 0; - - } else { - pj_assert(0); - return PJ_FALSE; - } -} - -/* Find credential to use for the specified realm and scheme. */ -PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count, - const pjsip_cred_info cred[], - const pj_str_t *realm, - const pj_str_t *scheme) -{ - unsigned i; - PJ_UNUSED_ARG(scheme); - for (i=0; i<count; ++i) { - if (pj_stricmp(&cred[i].realm, realm) == 0) - return &cred[i]; - } - return NULL; -} - -#if PJSIP_AUTH_AUTO_SEND_NEXT -static void new_auth_for_req( pjsip_tx_data *tdata, - pj_pool_t *sess_pool, - pjsip_auth_session *sess, - int cred_count, - const pjsip_cred_info cred_info[]) -{ - const pjsip_cred_info *cred; - pjsip_authorization_hdr *hauth; - - pj_assert(sess->last_chal != NULL); - - cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm, - &sess->last_chal->scheme ); - if (!cred) - return; - - - hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, - tdata->msg->line.req.uri, - cred, &tdata->msg->line.req.method, - sess_pool, sess); - if (hauth) { - pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth); - } -} -#endif - -/* - * Initialize new request message with authorization headers. - * This function will put Authorization/Proxy-Authorization headers to the - * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING) - * and the session has previously sent Authorization/Proxy-Authorization header - * with the same method, then the same Authorization/Proxy-Authorization header - * will be resent from the cache only if qop is not present. If the stack is - * configured to automatically generate next Authorization/Proxy-Authorization - * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy- - * Authorization headers are calculated and generated when they are not present - * in the case or if authorization session has qop. - * - * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag - * are not set, this function will do nothing. The stack then will only send - * Authorization/Proxy-Authorization to respond 401/407 response. - */ -PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool, - pjsip_tx_data *tdata, - pjsip_auth_session *sess_list, - int cred_count, - const pjsip_cred_info cred_info[]) -{ - pjsip_auth_session *sess; - pjsip_method *method = &tdata->msg->line.req.method; - - pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); - - if (!sess_list) - return 0; - - sess = sess_list->next; - while (sess != sess_list) { - if (sess->qop_value == PJSIP_AUTH_QOP_NONE) { -# if (PJSIP_AUTH_HEADER_CACHING) - { - pjsip_cached_auth_hdr *entry = sess->cached_hdr.next; - while (entry != &sess->cached_hdr) { - if (pjsip_method_cmp(&entry->method, method)==0) { - pjsip_authorization_hdr *hauth; - hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr); - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); - } else { -# if (PJSIP_AUTH_AUTO_SEND_NEXT) - { - new_auth_for_req( tdata, sess_pool, sess, - cred_count, cred_info); - } -# else - { - PJ_UNUSED_ARG(sess_pool); - PJ_UNUSED_ARG(cred_count); - PJ_UNUSED_ARG(cred_info); - } -# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */ - } - entry = entry->next; - } - } -# elif (PJSIP_AUTH_AUTO_SEND_NEXT) - { - new_auth_for_req( tdata, sess_pool, sess, - cred_count, cred_info); - } -# else - { - PJ_UNUSED_ARG(sess_pool); - PJ_UNUSED_ARG(cred_count); - PJ_UNUSED_ARG(cred_info); - } -# endif /* PJSIP_AUTH_HEADER_CACHING */ - - } -# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT) - else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) { - /* For qop="auth", we have to re-create the authorization header. - */ - const pjsip_cred_info *cred; - pjsip_authorization_hdr *hauth; - - cred = pjsip_auth_find_cred( cred_count, cred_info, - &sess->realm, - &sess->last_chal->scheme); - if (!cred) { - sess = sess->next; - continue; - } - - hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, - tdata->msg->line.req.uri, - cred, - &tdata->msg->line.req.method, - sess_pool, sess ); - if (hauth) { - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); - } - } -# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */ - - sess = sess->next; - } - return 0; -} - -/* Process authorization challenge */ -static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool, - const pjsip_www_authenticate_hdr *hchal, - const pjsip_uri *uri, - pjsip_tx_data *tdata, - int cred_count, - const pjsip_cred_info cred_info[], - pj_pool_t *ses_pool, - pjsip_auth_session *auth_sess) -{ - const pjsip_cred_info *cred; - pjsip_authorization_hdr *sent_auth = NULL, *hauth; - pjsip_hdr *hdr; - - /* See if we have sent authorization header for this realm */ - hdr = tdata->msg->hdr.next; - while (hdr != &tdata->msg->hdr) { - if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE && - hdr->type == PJSIP_H_AUTHORIZATION) || - (hchal->type == PJSIP_H_PROXY_AUTHENTICATE && - hdr->type == PJSIP_H_PROXY_AUTHORIZATION)) - { - sent_auth = (pjsip_authorization_hdr*) hdr; - if (pj_stricmp(&hchal->challenge.common.realm, - &sent_auth->credential.common.realm )==0) - { - break; - } - } - hdr = hdr->next; - } - - /* If we have sent, see if server rejected because of stale nonce or - * other causes. - */ - if (hdr != &tdata->msg->hdr) { - if (hchal->challenge.digest.stale == 0) { - /* Our credential is rejected. No point in trying to re-supply - * the same credential. - */ - PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s", - sent_auth->credential.digest.username.slen, - sent_auth->credential.digest.username.ptr, - sent_auth->credential.digest.realm.slen, - sent_auth->credential.digest.realm.ptr)); - return NULL; - } - - /* Otherwise remove old, stale authorization header from the mesasge. - * We will supply a new one. - */ - pj_list_erase(sent_auth); - } - - /* Find credential to be used for the challenge. */ - cred = pjsip_auth_find_cred( cred_count, cred_info, - &hchal->challenge.common.realm, &hchal->scheme); - if (!cred) { - const pj_str_t *realm = &hchal->challenge.common.realm; - PJ_LOG(4,(THIS_FILE, - "Unable to set auth for %s: can not find credential for %.*s/%.*s", - tdata->obj_name, - realm->slen, realm->ptr, - hchal->scheme.slen, hchal->scheme.ptr)); - return NULL; - } - - /* Respond to authorization challenge. */ - hauth = pjsip_auth_respond( req_pool, hchal, uri, cred, - &tdata->msg->line.req.method, - ses_pool, auth_sess); - return hauth; -} - - -/* Reinitialize outgoing request after 401/407 response is received. - * The purpose of this function is: - * - to add a Authorization/Proxy-Authorization header. - * - to put the newly created Authorization/Proxy-Authorization header - * in cached_list. - */ -PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt, - pj_pool_t *ses_pool, - pjsip_auth_session *sess_list, - int cred_count, - const pjsip_cred_info cred_info[], - pjsip_tx_data *tdata, - const pjsip_rx_data *rdata) -{ - const pjsip_hdr *hdr; - pjsip_via_hdr *via; - - PJ_UNUSED_ARG(endpt); - - pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG); - pj_assert(rdata->msg->line.status.code == 401 || - rdata->msg->line.status.code == 407 ); - - /* - * Respond to each authentication challenge. - */ - hdr = rdata->msg->hdr.next; - while (hdr != &rdata->msg->hdr) { - pjsip_auth_session *sess; - const pjsip_www_authenticate_hdr *hchal; - pjsip_authorization_hdr *hauth; - - /* Find WWW-Authenticate or Proxy-Authenticate header. */ - while (hdr->type != PJSIP_H_WWW_AUTHENTICATE && - hdr->type != PJSIP_H_PROXY_AUTHENTICATE && - hdr != &rdata->msg->hdr) - { - hdr = hdr->next; - } - if (hdr == &rdata->msg->hdr) - break; - - hchal = (const pjsip_www_authenticate_hdr*) hdr; - - /* Find authentication session for this realm, create a new one - * if not present. - */ - sess = find_session(sess_list, &hchal->challenge.common.realm ); - if (!sess) { - sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess)); - pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm); - sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE); -# if (PJSIP_AUTH_HEADER_CACHING) - { - pj_list_init(&sess->cached_hdr); - } -# endif - pj_list_insert_before( sess_list, sess ); - } - - /* Create authorization header for this challenge, and update - * authorization session. - */ - hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri, - tdata, cred_count, cred_info, ses_pool, sess ); - if (!hauth) - return NULL; - - /* Add to the message. */ - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth); - - /* Process next header. */ - hdr = hdr->next; - } - - - /* Remove branch param in Via header. */ - via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - via->branch_param.slen = 0; - - /* Increment reference counter. */ - pjsip_tx_data_add_ref(tdata); - - /* Done. */ - return tdata; -} - +
+/* Length of digest string. */
+#define MD5STRLEN 32
+
+/* Maximum stack size we use for storing username+realm+password etc. */
+#define MAX_TEMP 128
+
+/* A macro just to get rid of type mismatch between char and unsigned char */
+#define MD5_APPEND(pms,buf,len) md5_append(pms, (const unsigned char*)buf, len)
+
+/* Logging. */
+#define THIS_FILE "sip_auth.c"
+#if 0
+# define AUTH_TRACE_(expr) PJ_LOG(3, expr)
+#else
+# define AUTH_TRACE_(expr)
+#endif
+
+static const char hex[] = "0123456789abcdef";
+
+/* Transform digest to string.
+ * output must be at least MD5STRLEN+1 bytes.
+ *
+ * NOTE: THE OUTPUT STRING IS NOT NULL TERMINATED!
+ */
+static void digest2str(const unsigned char digest[], char *output)
+{
+ char *p = output;
+ int i;
+
+ for (i = 0; i<16; ++i) {
+ int val = digest[i];
+ *p++ = hex[val >> 4];
+ *p++ = hex[val & 0x0F];
+ }
+}
+
+/*
+ * Create response digest based on the parameters and store the
+ * digest ASCII in 'result'.
+ */
+static void create_digest( pj_str_t *result,
+ const pj_str_t *nonce,
+ const pj_str_t *nc,
+ const pj_str_t *cnonce,
+ const pj_str_t *qop,
+ const pj_str_t *uri,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *method)
+{
+ char ha1[MD5STRLEN];
+ char ha2[MD5STRLEN];
+ unsigned char digest[16];
+ md5_state_t pms;
+
+ pj_assert(result->slen >= MD5STRLEN);
+
+ AUTH_TRACE_((THIS_FILE, "Begin creating digest"));
+
+ if (cred_info->data_type == PJSIP_CRED_DATA_PLAIN_PASSWD) {
+ /***
+ *** ha1 = MD5(username ":" realm ":" password)
+ ***/
+ md5_init(&pms);
+ MD5_APPEND( &pms, cred_info->username.ptr, cred_info->username.slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, cred_info->realm.ptr, cred_info->realm.slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, cred_info->data.ptr, cred_info->data.slen);
+ md5_finish(&pms, digest);
+
+ digest2str(digest, ha1);
+
+ } else if (cred_info->data_type == PJSIP_CRED_DATA_DIGEST) {
+ pj_assert(cred_info->data.slen == 32);
+ pj_memcpy( ha1, cred_info->data.ptr, cred_info->data.slen );
+ }
+
+ AUTH_TRACE_((THIS_FILE, " ha1=%.32s", ha1));
+
+ /***
+ *** ha2 = MD5(method ":" req_uri)
+ ***/
+ md5_init(&pms);
+ MD5_APPEND( &pms, method->ptr, method->slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, uri->ptr, uri->slen);
+ md5_finish(&pms, digest);
+ digest2str(digest, ha2);
+
+ AUTH_TRACE_((THIS_FILE, " ha2=%.32s", ha2));
+
+ /***
+ *** When qop is not used:
+ *** response = MD5(ha1 ":" nonce ":" ha2)
+ ***
+ *** When qop=auth is used:
+ *** response = MD5(ha1 ":" nonce ":" nc ":" cnonce ":" qop ":" ha2)
+ ***/
+ md5_init(&pms);
+ MD5_APPEND( &pms, ha1, MD5STRLEN);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, nonce->ptr, nonce->slen);
+ if (qop && qop->slen != 0) {
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, nc->ptr, nc->slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, cnonce->ptr, cnonce->slen);
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, qop->ptr, qop->slen);
+ }
+ MD5_APPEND( &pms, ":", 1);
+ MD5_APPEND( &pms, ha2, MD5STRLEN);
+
+ /* This is the final response digest. */
+ md5_finish(&pms, digest);
+
+ /* Convert digest to string and store in chal->response. */
+ result->slen = MD5STRLEN;
+ digest2str(digest, result->ptr);
+
+ AUTH_TRACE_((THIS_FILE, " digest=%.32s", result->ptr));
+ AUTH_TRACE_((THIS_FILE, "Digest created"));
+}
+
+/*
+ * Finds out if qop offer contains "auth" token.
+ */
+static pj_bool_t has_auth_qop( pj_pool_t *pool, const pj_str_t *qop_offer)
+{
+ pj_str_t qop;
+ char *p;
+
+ pj_strdup_with_null( pool, &qop, qop_offer);
+ p = qop.ptr;
+ while (*p) {
+ *p = (char)pj_tolower(*p);
+ ++p;
+ }
+
+ p = qop.ptr;
+ while (*p) {
+ if (*p=='a' && *(p+1)=='u' && *(p+2)=='t' && *(p+3)=='h') {
+ int e = *(p+4);
+ if (e=='"' || e==',' || e==0)
+ return PJ_TRUE;
+ else
+ p += 4;
+ } else {
+ ++p;
+ }
+ }
+
+ return PJ_FALSE;
+}
+
+/*
+ * Generate response digest.
+ * Most of the parameters to generate the digest (i.e. username, realm, uri,
+ * and nonce) are expected to be in the credential. Additional parameters (i.e.
+ * password and method param) should be supplied in the argument.
+ *
+ * The resulting digest will be stored in cred->response.
+ * The pool is used to allocate 32 bytes to store the digest in cred->response.
+ */
+static pj_status_t respond_digest( pj_pool_t *pool,
+ pjsip_digest_credential *cred,
+ const pjsip_digest_challenge *chal,
+ const pj_str_t *uri,
+ const pjsip_cred_info *cred_info,
+ const pj_str_t *cnonce,
+ pj_uint32_t nc,
+ const pj_str_t *method)
+{
+ /* Check algorithm is supported. We only support MD5. */
+ if (chal->algorithm.slen && pj_stricmp(&chal->algorithm, &pjsip_MD5_STR))
+ {
+ PJ_LOG(4,(THIS_FILE, "Unsupported digest algorithm \"%.*s\"",
+ chal->algorithm.slen, chal->algorithm.ptr));
+ return -1;
+ }
+
+ /* Build digest credential from arguments. */
+ pj_strdup(pool, &cred->username, &cred_info->username);
+ pj_strdup(pool, &cred->realm, &chal->realm);
+ pj_strdup(pool, &cred->nonce, &chal->nonce);
+ pj_strdup(pool, &cred->uri, uri);
+ cred->algorithm = pjsip_MD5_STR;
+ pj_strdup(pool, &cred->opaque, &chal->opaque);
+
+ /* Allocate memory. */
+ cred->response.ptr = pj_pool_alloc(pool, MD5STRLEN);
+ cred->response.slen = MD5STRLEN;
+
+ if (chal->qop.slen == 0) {
+ /* Server doesn't require quality of protection. */
+
+ /* Convert digest to string and store in chal->response. */
+ create_digest( &cred->response, &cred->nonce, NULL, NULL, NULL,
+ uri, cred_info, method);
+
+ } else if (has_auth_qop(pool, &chal->qop)) {
+ /* Server requires quality of protection.
+ * We respond with selecting "qop=auth" protection.
+ */
+ cred->qop = pjsip_AUTH_STR;
+ cred->nc.ptr = pj_pool_alloc(pool, 16);
+ pj_snprintf(cred->nc.ptr, 16, "%06u", nc);
+
+ if (cnonce && cnonce->slen) {
+ pj_strdup(pool, &cred->cnonce, cnonce);
+ } else {
+ pj_str_t dummy_cnonce = { "b39971", 6};
+ pj_strdup(pool, &cred->cnonce, &dummy_cnonce);
+ }
+
+ create_digest( &cred->response, &cred->nonce, &cred->nc, cnonce,
+ &pjsip_AUTH_STR, uri, cred_info, method );
+
+ } else {
+ /* Server requires quality protection that we don't support. */
+ PJ_LOG(4,(THIS_FILE, "Unsupported qop offer %.*s",
+ chal->qop.slen, chal->qop.ptr));
+ return -1;
+ }
+
+ return 0;
+}
+
+#if PJSIP_AUTH_QOP_SUPPORT
+/*
+ * Update authentication session with a challenge.
+ */
+static void update_digest_session( pj_pool_t *ses_pool,
+ pjsip_auth_session *auth_sess,
+ const pjsip_www_authenticate_hdr *hdr )
+{
+ if (hdr->challenge.digest.qop.slen == 0)
+ return;
+
+ /* Initialize cnonce and qop if not present. */
+ if (auth_sess->cnonce.slen == 0) {
+ /* Save the whole challenge */
+ auth_sess->last_chal = pjsip_hdr_clone(ses_pool, hdr);
+
+ /* Create cnonce */
+ pj_create_unique_string( ses_pool, &auth_sess->cnonce );
+
+ /* Initialize nonce-count */
+ auth_sess->nc = 1;
+
+ /* Save realm. */
+ pj_assert(auth_sess->realm.slen != 0);
+ if (auth_sess->realm.slen == 0) {
+ pj_strdup(ses_pool, &auth_sess->realm,
+ &hdr->challenge.digest.realm);
+ }
+
+ } else {
+ /* Update last_nonce and nonce-count */
+ if (!pj_strcmp(&hdr->challenge.digest.nonce,
+ &auth_sess->last_chal->challenge.digest.nonce))
+ {
+ /* Same nonce, increment nonce-count */
+ ++auth_sess->nc;
+ } else {
+ /* Server gives new nonce. */
+ pj_strdup(ses_pool, &auth_sess->last_chal->challenge.digest.nonce,
+ &hdr->challenge.digest.nonce);
+ /* Has the opaque changed? */
+ if (pj_strcmp(&auth_sess->last_chal->challenge.digest.opaque,
+ &hdr->challenge.digest.opaque))
+ {
+ pj_strdup(ses_pool,
+ &auth_sess->last_chal->challenge.digest.opaque,
+ &hdr->challenge.digest.opaque);
+ }
+ auth_sess->nc = 1;
+ }
+ }
+}
+#endif /* PJSIP_AUTH_QOP_SUPPORT */
+
+
+/* Find authentication session in the list. */
+static pjsip_auth_session *find_session( pjsip_auth_session *sess_list,
+ const pj_str_t *realm )
+{
+ pjsip_auth_session *sess = sess_list->next;
+ while (sess != sess_list) {
+ if (pj_stricmp(&sess->realm, realm) == 0)
+ return sess;
+ sess = sess->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Create Authorization/Proxy-Authorization response header based on the challege
+ * in WWW-Authenticate/Proxy-Authenticate header.
+ */
+PJ_DEF(pjsip_authorization_hdr*)
+pjsip_auth_respond( pj_pool_t *req_pool,
+ const pjsip_www_authenticate_hdr *hdr,
+ const pjsip_uri *uri,
+ const pjsip_cred_info *cred_info,
+ const pjsip_method *method,
+ pj_pool_t *sess_pool,
+ pjsip_auth_session *auth_sess)
+{
+ pjsip_authorization_hdr *auth;
+ char tmp[PJSIP_MAX_URL_SIZE];
+ pj_str_t uri_str;
+ pj_pool_t *pool;
+
+ pj_assert(hdr != NULL);
+ pj_assert(uri != NULL);
+ pj_assert(cred_info != NULL);
+ pj_assert(method != NULL);
+
+ /* Print URL in the original request. */
+ uri_str.ptr = tmp;
+ uri_str.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, uri, tmp, sizeof(tmp));
+ if (uri_str.slen < 1) {
+ pj_assert(!"URL is too long!");
+ PJ_LOG(4,(THIS_FILE, "Unable to authorize: URI is too long!"));
+ return NULL;
+ }
+
+# if (PJSIP_AUTH_HEADER_CACHING)
+ {
+ pool = sess_pool;
+ PJ_UNUSED_ARG(req_pool);
+ }
+# else
+ {
+ pool = req_pool;
+ PJ_UNUSED_ARG(sess_pool);
+ }
+# endif
+
+ if (hdr->type == PJSIP_H_WWW_AUTHENTICATE)
+ auth = pjsip_authorization_hdr_create(pool);
+ else if (hdr->type == PJSIP_H_PROXY_AUTHENTICATE)
+ auth = pjsip_proxy_authorization_hdr_create(pool);
+ else {
+ pj_assert(0);
+ return NULL;
+ }
+
+ /* Only support digest scheme at the moment. */
+ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+ pj_status_t rc;
+ pj_str_t *cnonce = NULL;
+ pj_uint32_t nc = 1;
+
+ /* Update the session (nonce-count etc) if required. */
+# if PJSIP_AUTH_QOP_SUPPORT
+ {
+ if (auth_sess) {
+ update_digest_session( sess_pool, auth_sess, hdr );
+
+ cnonce = &auth_sess->cnonce;
+ nc = auth_sess->nc;
+ }
+ }
+# endif /* PJSIP_AUTH_QOP_SUPPORT */
+
+ auth->scheme = pjsip_DIGEST_STR;
+ rc = respond_digest( pool, &auth->credential.digest,
+ &hdr->challenge.digest, &uri_str, cred_info,
+ cnonce, nc, &method->name);
+ if (rc != 0)
+ return NULL;
+
+ /* Set qop type in auth session the first time only. */
+ if (hdr->challenge.digest.qop.slen != 0 && auth_sess) {
+ if (auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+ pj_str_t *qop_val = &auth->credential.digest.qop;
+ if (!pj_strcmp(qop_val, &pjsip_AUTH_STR)) {
+ auth_sess->qop_value = PJSIP_AUTH_QOP_AUTH;
+ } else {
+ auth_sess->qop_value = PJSIP_AUTH_QOP_UNKNOWN;
+ }
+ }
+ }
+ } else {
+ auth = NULL;
+ }
+
+ /* Keep the new authorization header in the cache, only
+ * if no qop is not present.
+ */
+# if PJSIP_AUTH_HEADER_CACHING
+ {
+ if (auth && auth_sess && auth_sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+ pjsip_cached_auth_hdr *cached_hdr;
+
+ /* Delete old header with the same method. */
+ cached_hdr = auth_sess->cached_hdr.next;
+ while (cached_hdr != &auth_sess->cached_hdr) {
+ if (pjsip_method_cmp(method, &cached_hdr->method)==0)
+ break;
+ cached_hdr = cached_hdr->next;
+ }
+
+ /* Save the header to the list. */
+ if (cached_hdr != &auth_sess->cached_hdr) {
+ cached_hdr->hdr = auth;
+ } else {
+ cached_hdr = pj_pool_alloc(pool, sizeof(*cached_hdr));
+ pjsip_method_copy( pool, &cached_hdr->method, method);
+ cached_hdr->hdr = auth;
+ pj_list_insert_before( &auth_sess->cached_hdr, cached_hdr );
+ }
+ }
+ }
+# endif
+
+ return auth;
+
+}
+
+/* Verify incoming Authorization/Proxy-Authorization header against existing
+ * credentials. Will return TRUE if the authorization request matches any of
+ * the credential.
+ */
+PJ_DEF(pj_bool_t) pjsip_auth_verify(const pjsip_authorization_hdr *hdr,
+ const pj_str_t *method,
+ const pjsip_cred_info *cred_info )
+{
+ if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) {
+ char digest_buf[MD5STRLEN];
+ pj_str_t digest;
+ const pjsip_digest_credential *dig = &hdr->credential.digest;
+
+ /* Check that username match. */
+ if (pj_strcmp(&dig->username, &cred_info->username) != 0)
+ return PJ_FALSE;
+
+ /* Check that realm match. */
+ if (pj_strcmp(&dig->realm, &cred_info->realm) != 0)
+ return PJ_FALSE;
+
+ /* Prepare for our digest calculation. */
+ digest.ptr = digest_buf;
+ digest.slen = MD5STRLEN;
+
+ /* Create digest for comparison. */
+ create_digest( &digest,
+ &hdr->credential.digest.nonce,
+ &hdr->credential.digest.nc,
+ &hdr->credential.digest.cnonce,
+ &hdr->credential.digest.qop,
+ &hdr->credential.digest.uri,
+ cred_info,
+ method );
+
+ return pj_stricmp(&digest, &hdr->credential.digest.response) == 0;
+
+ } else {
+ pj_assert(0);
+ return PJ_FALSE;
+ }
+}
+
+/* Find credential to use for the specified realm and scheme. */
+PJ_DEF(const pjsip_cred_info*) pjsip_auth_find_cred( unsigned count,
+ const pjsip_cred_info cred[],
+ const pj_str_t *realm,
+ const pj_str_t *scheme)
+{
+ unsigned i;
+ PJ_UNUSED_ARG(scheme);
+ for (i=0; i<count; ++i) {
+ if (pj_stricmp(&cred[i].realm, realm) == 0)
+ return &cred[i];
+ }
+ return NULL;
+}
+
+#if PJSIP_AUTH_AUTO_SEND_NEXT
+static void new_auth_for_req( pjsip_tx_data *tdata,
+ pj_pool_t *sess_pool,
+ pjsip_auth_session *sess,
+ int cred_count,
+ const pjsip_cred_info cred_info[])
+{
+ const pjsip_cred_info *cred;
+ pjsip_authorization_hdr *hauth;
+
+ pj_assert(sess->last_chal != NULL);
+
+ cred = pjsip_auth_find_cred( cred_count, cred_info, &sess->realm,
+ &sess->last_chal->scheme );
+ if (!cred)
+ return;
+
+
+ hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
+ tdata->msg->line.req.uri,
+ cred, &tdata->msg->line.req.method,
+ sess_pool, sess);
+ if (hauth) {
+ pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hauth);
+ }
+}
+#endif
+
+/*
+ * Initialize new request message with authorization headers.
+ * This function will put Authorization/Proxy-Authorization headers to the
+ * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
+ * and the session has previously sent Authorization/Proxy-Authorization header
+ * with the same method, then the same Authorization/Proxy-Authorization header
+ * will be resent from the cache only if qop is not present. If the stack is
+ * configured to automatically generate next Authorization/Proxy-Authorization
+ * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
+ * Authorization headers are calculated and generated when they are not present
+ * in the case or if authorization session has qop.
+ *
+ * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
+ * are not set, this function will do nothing. The stack then will only send
+ * Authorization/Proxy-Authorization to respond 401/407 response.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
+ pjsip_tx_data *tdata,
+ pjsip_auth_session *sess_list,
+ int cred_count,
+ const pjsip_cred_info cred_info[])
+{
+ pjsip_auth_session *sess;
+ pjsip_method *method = &tdata->msg->line.req.method;
+
+ pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+
+ if (!sess_list)
+ return 0;
+
+ sess = sess_list->next;
+ while (sess != sess_list) {
+ if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
+# if (PJSIP_AUTH_HEADER_CACHING)
+ {
+ pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
+ while (entry != &sess->cached_hdr) {
+ if (pjsip_method_cmp(&entry->method, method)==0) {
+ pjsip_authorization_hdr *hauth;
+ hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+ } else {
+# if (PJSIP_AUTH_AUTO_SEND_NEXT)
+ {
+ new_auth_for_req( tdata, sess_pool, sess,
+ cred_count, cred_info);
+ }
+# else
+ {
+ PJ_UNUSED_ARG(sess_pool);
+ PJ_UNUSED_ARG(cred_count);
+ PJ_UNUSED_ARG(cred_info);
+ }
+# endif /* PJSIP_AUTH_AUTO_SEND_NEXT */
+ }
+ entry = entry->next;
+ }
+ }
+# elif (PJSIP_AUTH_AUTO_SEND_NEXT)
+ {
+ new_auth_for_req( tdata, sess_pool, sess,
+ cred_count, cred_info);
+ }
+# else
+ {
+ PJ_UNUSED_ARG(sess_pool);
+ PJ_UNUSED_ARG(cred_count);
+ PJ_UNUSED_ARG(cred_info);
+ }
+# endif /* PJSIP_AUTH_HEADER_CACHING */
+
+ }
+# if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
+ else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
+ /* For qop="auth", we have to re-create the authorization header.
+ */
+ const pjsip_cred_info *cred;
+ pjsip_authorization_hdr *hauth;
+
+ cred = pjsip_auth_find_cred( cred_count, cred_info,
+ &sess->realm,
+ &sess->last_chal->scheme);
+ if (!cred) {
+ sess = sess->next;
+ continue;
+ }
+
+ hauth = pjsip_auth_respond( tdata->pool, sess->last_chal,
+ tdata->msg->line.req.uri,
+ cred,
+ &tdata->msg->line.req.method,
+ sess_pool, sess );
+ if (hauth) {
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+ }
+ }
+# endif /* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
+
+ sess = sess->next;
+ }
+ return 0;
+}
+
+/* Process authorization challenge */
+static pjsip_authorization_hdr *process_auth( pj_pool_t *req_pool,
+ const pjsip_www_authenticate_hdr *hchal,
+ const pjsip_uri *uri,
+ pjsip_tx_data *tdata,
+ int cred_count,
+ const pjsip_cred_info cred_info[],
+ pj_pool_t *ses_pool,
+ pjsip_auth_session *auth_sess)
+{
+ const pjsip_cred_info *cred;
+ pjsip_authorization_hdr *sent_auth = NULL, *hauth;
+ pjsip_hdr *hdr;
+
+ /* See if we have sent authorization header for this realm */
+ hdr = tdata->msg->hdr.next;
+ while (hdr != &tdata->msg->hdr) {
+ if ((hchal->type == PJSIP_H_WWW_AUTHENTICATE &&
+ hdr->type == PJSIP_H_AUTHORIZATION) ||
+ (hchal->type == PJSIP_H_PROXY_AUTHENTICATE &&
+ hdr->type == PJSIP_H_PROXY_AUTHORIZATION))
+ {
+ sent_auth = (pjsip_authorization_hdr*) hdr;
+ if (pj_stricmp(&hchal->challenge.common.realm,
+ &sent_auth->credential.common.realm )==0)
+ {
+ break;
+ }
+ }
+ hdr = hdr->next;
+ }
+
+ /* If we have sent, see if server rejected because of stale nonce or
+ * other causes.
+ */
+ if (hdr != &tdata->msg->hdr) {
+ if (hchal->challenge.digest.stale == 0) {
+ /* Our credential is rejected. No point in trying to re-supply
+ * the same credential.
+ */
+ PJ_LOG(4, (THIS_FILE, "Authorization failed for %.*s@%.*s",
+ sent_auth->credential.digest.username.slen,
+ sent_auth->credential.digest.username.ptr,
+ sent_auth->credential.digest.realm.slen,
+ sent_auth->credential.digest.realm.ptr));
+ return NULL;
+ }
+
+ /* Otherwise remove old, stale authorization header from the mesasge.
+ * We will supply a new one.
+ */
+ pj_list_erase(sent_auth);
+ }
+
+ /* Find credential to be used for the challenge. */
+ cred = pjsip_auth_find_cred( cred_count, cred_info,
+ &hchal->challenge.common.realm, &hchal->scheme);
+ if (!cred) {
+ const pj_str_t *realm = &hchal->challenge.common.realm;
+ PJ_LOG(4,(THIS_FILE,
+ "Unable to set auth for %s: can not find credential for %.*s/%.*s",
+ tdata->obj_name,
+ realm->slen, realm->ptr,
+ hchal->scheme.slen, hchal->scheme.ptr));
+ return NULL;
+ }
+
+ /* Respond to authorization challenge. */
+ hauth = pjsip_auth_respond( req_pool, hchal, uri, cred,
+ &tdata->msg->line.req.method,
+ ses_pool, auth_sess);
+ return hauth;
+}
+
+
+/* Reinitialize outgoing request after 401/407 response is received.
+ * The purpose of this function is:
+ * - to add a Authorization/Proxy-Authorization header.
+ * - to put the newly created Authorization/Proxy-Authorization header
+ * in cached_list.
+ */
+PJ_DEF(pjsip_tx_data*) pjsip_auth_reinit_req( pjsip_endpoint *endpt,
+ pj_pool_t *ses_pool,
+ pjsip_auth_session *sess_list,
+ int cred_count,
+ const pjsip_cred_info cred_info[],
+ pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata)
+{
+ const pjsip_hdr *hdr;
+ pjsip_via_hdr *via;
+
+ PJ_UNUSED_ARG(endpt);
+
+ pj_assert(rdata->msg->type == PJSIP_RESPONSE_MSG);
+ pj_assert(rdata->msg->line.status.code == 401 ||
+ rdata->msg->line.status.code == 407 );
+
+ /*
+ * Respond to each authentication challenge.
+ */
+ hdr = rdata->msg->hdr.next;
+ while (hdr != &rdata->msg->hdr) {
+ pjsip_auth_session *sess;
+ const pjsip_www_authenticate_hdr *hchal;
+ pjsip_authorization_hdr *hauth;
+
+ /* Find WWW-Authenticate or Proxy-Authenticate header. */
+ while (hdr->type != PJSIP_H_WWW_AUTHENTICATE &&
+ hdr->type != PJSIP_H_PROXY_AUTHENTICATE &&
+ hdr != &rdata->msg->hdr)
+ {
+ hdr = hdr->next;
+ }
+ if (hdr == &rdata->msg->hdr)
+ break;
+
+ hchal = (const pjsip_www_authenticate_hdr*) hdr;
+
+ /* Find authentication session for this realm, create a new one
+ * if not present.
+ */
+ sess = find_session(sess_list, &hchal->challenge.common.realm );
+ if (!sess) {
+ sess = pj_pool_calloc( ses_pool, 1, sizeof(*sess));
+ pj_strdup( ses_pool, &sess->realm, &hchal->challenge.common.realm);
+ sess->is_proxy = (hchal->type == PJSIP_H_PROXY_AUTHENTICATE);
+# if (PJSIP_AUTH_HEADER_CACHING)
+ {
+ pj_list_init(&sess->cached_hdr);
+ }
+# endif
+ pj_list_insert_before( sess_list, sess );
+ }
+
+ /* Create authorization header for this challenge, and update
+ * authorization session.
+ */
+ hauth = process_auth( tdata->pool, hchal, tdata->msg->line.req.uri,
+ tdata, cred_count, cred_info, ses_pool, sess );
+ if (!hauth)
+ return NULL;
+
+ /* Add to the message. */
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+
+ /* Process next header. */
+ hdr = hdr->next;
+ }
+
+
+ /* Remove branch param in Via header. */
+ via = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+ via->branch_param.slen = 0;
+
+ /* Increment reference counter. */
+ pjsip_tx_data_add_ref(tdata);
+
+ /* Done. */
+ return tdata;
+}
+
diff --git a/pjsip/src/pjsip/sip_auth_msg.c b/pjsip/src/pjsip/sip_auth_msg.c index 9e88ef4a..b9fe2962 100644 --- a/pjsip/src/pjsip/sip_auth_msg.c +++ b/pjsip/src/pjsip/sip_auth_msg.c @@ -1,294 +1,316 @@ -/* $Id$ - */ -#include <pjsip/sip_auth_msg.h> -#include <pjsip/sip_auth_parser.h> -#include <pj/pool.h> -#include <pj/list.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_auth_msg.h>
+#include <pjsip/sip_auth_parser.h>
+#include <pj/pool.h>
+#include <pj/list.h>
#include <pj/string.h>
-#include <pj/assert.h> -#include <pjsip/print_util.h> - -/////////////////////////////////////////////////////////////////////////////// -/* - * Authorization and Proxy-Authorization header. - */ -static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *hdr); -static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *hdr); -static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, - char *buf, pj_size_t size); - -static pjsip_hdr_vptr authorization_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print, -}; - - -PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool) -{ - pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr); - return hdr; -} - -PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool) -{ - pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr); - return hdr; -} - -static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"'); - copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"'); - copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"'); - copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"'); - copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"'); - copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm); - copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"'); - copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"'); - //Note: there's no dbl-quote in qop in Authorization header - // (unlike WWW-Authenticate) - //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"'); - copy_advance_pair(buf, ", qop=", 6, cred->qop); - copy_advance_pair(buf, ", nc=", 5, cred->nc); - copy_advance(buf, cred->other_param); - - return (int) (buf-startbuf); -} - -static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size) -{ - PJ_UNUSED_ARG(cred); - PJ_UNUSED_ARG(buf); - PJ_UNUSED_ARG(size); - return -1; -} - -static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - copy_advance(buf, hdr->scheme); - *buf++ = ' '; - - if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0) - { - printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf); - } - else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0) - { - printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf); - } - else { - pj_assert(0); - return -1; - } - - if (printed == -1) - return -1; - - buf += printed; - *buf = '\0'; - return (int)(buf-startbuf); -} - -static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *rhs) -{ - /* This function also serves Proxy-Authorization header. */ - pjsip_authorization_hdr *hdr; - if (rhs->type == PJSIP_H_AUTHORIZATION) - hdr = pjsip_authorization_hdr_create(pool); - else - hdr = pjsip_proxy_authorization_hdr_create(pool); - - pj_strdup(pool, &hdr->scheme, &rhs->scheme); - - if (pj_stricmp2(&hdr->scheme, "digest") == 0) { - pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username); - pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm); - pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce); - pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri); - pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response); - pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm); - pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce); - pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque); - pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop); - pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc); - pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param); - } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { - pj_assert(0); - return NULL; - } else { - pj_assert(0); - return NULL; - } - - return hdr; -} - -static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_authorization_hdr *rhs) -{ - /* This function also serves Proxy-Authorization header. */ - pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Proxy-Authenticate and WWW-Authenticate header. - */ -static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *hdr); -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *hdr); - -static pjsip_hdr_vptr www_authenticate_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print, -}; - - -PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool) -{ - pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr); - return hdr; -} - - -PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool) -{ - pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr); - return hdr; -} - -static int print_digest_challenge( pjsip_digest_challenge *chal, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"'); - copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"'); - copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"'); - copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"'); - if (chal->stale) { - pj_str_t true_str = { "true", 4 }; - copy_advance_pair(buf, ",stale=", 7, true_str); - } - copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm); - copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"'); - copy_advance(buf, chal->other_param); - - return (int)(buf-startbuf); -} - -static int print_pgp_challenge( pjsip_pgp_challenge *chal, - char *buf, pj_size_t size) -{ - PJ_UNUSED_ARG(chal); - PJ_UNUSED_ARG(buf); - PJ_UNUSED_ARG(size); - return -1; -} - -static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - copy_advance(buf, hdr->scheme); - *buf++ = ' '; - - if (pj_stricmp2(&hdr->scheme, "digest") == 0) - printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf); - else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) - printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf); - else { - pj_assert(0); - return -1; - } - - if (printed == -1) - return -1; - - buf += printed; - *buf = '\0'; - return (int)(buf-startbuf); -} - -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *rhs) -{ - /* This function also serves Proxy-Authenticate header. */ - pjsip_www_authenticate_hdr *hdr; - if (rhs->type == PJSIP_H_WWW_AUTHENTICATE) - hdr = pjsip_www_authenticate_hdr_create(pool); - else - hdr = pjsip_proxy_authenticate_hdr_create(pool); - - pj_strdup(pool, &hdr->scheme, &rhs->scheme); - - if (pj_stricmp2(&hdr->scheme, "digest") == 0) { - pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm); - pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain); - pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce); - pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque); - hdr->challenge.digest.stale = rhs->challenge.digest.stale; - pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm); - pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop); - pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param); - } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) { - pj_assert(0); - return NULL; - } else { - pj_assert(0); - return NULL; - } - - return hdr; - -} - -static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_www_authenticate_hdr *rhs) -{ - /* This function also serves Proxy-Authenticate header. */ - pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - +#include <pj/assert.h>
+#include <pjsip/print_util.h>
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Authorization and Proxy-Authorization header.
+ */
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *hdr);
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *hdr);
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+ char *buf, pj_size_t size);
+
+static pjsip_hdr_vptr authorization_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_authorization_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_authorization_hdr_print,
+};
+
+
+PJ_DEF(pjsip_authorization_hdr*) pjsip_authorization_hdr_create(pj_pool_t *pool)
+{
+ pjsip_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_AUTHORIZATION, &authorization_hdr_vptr);
+ return hdr;
+}
+
+PJ_DEF(pjsip_proxy_authorization_hdr*) pjsip_proxy_authorization_hdr_create(pj_pool_t *pool)
+{
+ pjsip_proxy_authorization_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_PROXY_AUTHORIZATION, &authorization_hdr_vptr);
+ return hdr;
+}
+
+static int print_digest_credential(pjsip_digest_credential *cred, char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance_pair_quote_cond(buf, "username=", 9, cred->username, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", realm=", 8, cred->realm, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", nonce=", 8, cred->nonce, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", uri=", 6, cred->uri, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", response=", 11, cred->response, '"', '"');
+ copy_advance_pair(buf, ", algorithm=", 12, cred->algorithm);
+ copy_advance_pair_quote_cond(buf, ", cnonce=", 9, cred->cnonce, '"', '"');
+ copy_advance_pair_quote_cond(buf, ", opaque=", 9, cred->opaque, '"', '"');
+ //Note: there's no dbl-quote in qop in Authorization header
+ // (unlike WWW-Authenticate)
+ //copy_advance_pair_quote_cond(buf, ", qop=", 6, cred->qop, '"', '"');
+ copy_advance_pair(buf, ", qop=", 6, cred->qop);
+ copy_advance_pair(buf, ", nc=", 5, cred->nc);
+ copy_advance(buf, cred->other_param);
+
+ return (int) (buf-startbuf);
+}
+
+static int print_pgp_credential(pjsip_pgp_credential *cred, char *buf, pj_size_t size)
+{
+ PJ_UNUSED_ARG(cred);
+ PJ_UNUSED_ARG(buf);
+ PJ_UNUSED_ARG(size);
+ return -1;
+}
+
+static int pjsip_authorization_hdr_print( pjsip_authorization_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ copy_advance(buf, hdr->scheme);
+ *buf++ = ' ';
+
+ if (pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR) == 0)
+ {
+ printed = print_digest_credential(&hdr->credential.digest, buf, endbuf - buf);
+ }
+ else if (pj_stricmp(&hdr->scheme, &pjsip_PGP_STR) == 0)
+ {
+ printed = print_pgp_credential(&hdr->credential.pgp, buf, endbuf - buf);
+ }
+ else {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (printed == -1)
+ return -1;
+
+ buf += printed;
+ *buf = '\0';
+ return (int)(buf-startbuf);
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *rhs)
+{
+ /* This function also serves Proxy-Authorization header. */
+ pjsip_authorization_hdr *hdr;
+ if (rhs->type == PJSIP_H_AUTHORIZATION)
+ hdr = pjsip_authorization_hdr_create(pool);
+ else
+ hdr = pjsip_proxy_authorization_hdr_create(pool);
+
+ pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+ if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+ pj_strdup(pool, &hdr->credential.digest.username, &rhs->credential.digest.username);
+ pj_strdup(pool, &hdr->credential.digest.realm, &rhs->credential.digest.realm);
+ pj_strdup(pool, &hdr->credential.digest.nonce, &rhs->credential.digest.nonce);
+ pj_strdup(pool, &hdr->credential.digest.uri, &rhs->credential.digest.uri);
+ pj_strdup(pool, &hdr->credential.digest.response, &rhs->credential.digest.response);
+ pj_strdup(pool, &hdr->credential.digest.algorithm, &rhs->credential.digest.algorithm);
+ pj_strdup(pool, &hdr->credential.digest.cnonce, &rhs->credential.digest.cnonce);
+ pj_strdup(pool, &hdr->credential.digest.opaque, &rhs->credential.digest.opaque);
+ pj_strdup(pool, &hdr->credential.digest.qop, &rhs->credential.digest.qop);
+ pj_strdup(pool, &hdr->credential.digest.nc, &rhs->credential.digest.nc);
+ pj_strdup(pool, &hdr->credential.digest.other_param, &rhs->credential.digest.other_param);
+ } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+ pj_assert(0);
+ return NULL;
+ } else {
+ pj_assert(0);
+ return NULL;
+ }
+
+ return hdr;
+}
+
+static pjsip_authorization_hdr* pjsip_authorization_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_authorization_hdr *rhs)
+{
+ /* This function also serves Proxy-Authorization header. */
+ pjsip_authorization_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Proxy-Authenticate and WWW-Authenticate header.
+ */
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *hdr);
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *hdr);
+
+static pjsip_hdr_vptr www_authenticate_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_www_authenticate_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_www_authenticate_hdr_print,
+};
+
+
+PJ_DEF(pjsip_www_authenticate_hdr*) pjsip_www_authenticate_hdr_create(pj_pool_t *pool)
+{
+ pjsip_www_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_WWW_AUTHENTICATE, &www_authenticate_hdr_vptr);
+ return hdr;
+}
+
+
+PJ_DEF(pjsip_proxy_authenticate_hdr*) pjsip_proxy_authenticate_hdr_create(pj_pool_t *pool)
+{
+ pjsip_proxy_authenticate_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_PROXY_AUTHENTICATE, &www_authenticate_hdr_vptr);
+ return hdr;
+}
+
+static int print_digest_challenge( pjsip_digest_challenge *chal,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance_pair_quote_cond(buf, " realm=", 7, chal->realm, '"', '"');
+ copy_advance_pair_quote_cond(buf, ",domain=", 8, chal->domain, '"', '"');
+ copy_advance_pair_quote_cond(buf, ",nonce=", 7, chal->nonce, '"', '"');
+ copy_advance_pair_quote_cond(buf, ",opaque=", 8, chal->opaque, '"', '"');
+ if (chal->stale) {
+ pj_str_t true_str = { "true", 4 };
+ copy_advance_pair(buf, ",stale=", 7, true_str);
+ }
+ copy_advance_pair(buf, ",algorithm=", 11, chal->algorithm);
+ copy_advance_pair_quote_cond(buf, ",qop=", 5, chal->qop, '"', '"');
+ copy_advance(buf, chal->other_param);
+
+ return (int)(buf-startbuf);
+}
+
+static int print_pgp_challenge( pjsip_pgp_challenge *chal,
+ char *buf, pj_size_t size)
+{
+ PJ_UNUSED_ARG(chal);
+ PJ_UNUSED_ARG(buf);
+ PJ_UNUSED_ARG(size);
+ return -1;
+}
+
+static int pjsip_www_authenticate_hdr_print( pjsip_www_authenticate_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ copy_advance(buf, hdr->scheme);
+ *buf++ = ' ';
+
+ if (pj_stricmp2(&hdr->scheme, "digest") == 0)
+ printed = print_digest_challenge(&hdr->challenge.digest, buf, endbuf - buf);
+ else if (pj_stricmp2(&hdr->scheme, "pgp") == 0)
+ printed = print_pgp_challenge(&hdr->challenge.pgp, buf, endbuf - buf);
+ else {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (printed == -1)
+ return -1;
+
+ buf += printed;
+ *buf = '\0';
+ return (int)(buf-startbuf);
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *rhs)
+{
+ /* This function also serves Proxy-Authenticate header. */
+ pjsip_www_authenticate_hdr *hdr;
+ if (rhs->type == PJSIP_H_WWW_AUTHENTICATE)
+ hdr = pjsip_www_authenticate_hdr_create(pool);
+ else
+ hdr = pjsip_proxy_authenticate_hdr_create(pool);
+
+ pj_strdup(pool, &hdr->scheme, &rhs->scheme);
+
+ if (pj_stricmp2(&hdr->scheme, "digest") == 0) {
+ pj_strdup(pool, &hdr->challenge.digest.realm, &rhs->challenge.digest.realm);
+ pj_strdup(pool, &hdr->challenge.digest.domain, &rhs->challenge.digest.domain);
+ pj_strdup(pool, &hdr->challenge.digest.nonce, &rhs->challenge.digest.nonce);
+ pj_strdup(pool, &hdr->challenge.digest.opaque, &rhs->challenge.digest.opaque);
+ hdr->challenge.digest.stale = rhs->challenge.digest.stale;
+ pj_strdup(pool, &hdr->challenge.digest.algorithm, &rhs->challenge.digest.algorithm);
+ pj_strdup(pool, &hdr->challenge.digest.qop, &rhs->challenge.digest.qop);
+ pj_strdup(pool, &hdr->challenge.digest.other_param, &rhs->challenge.digest.other_param);
+ } else if (pj_stricmp2(&hdr->scheme, "pgp") == 0) {
+ pj_assert(0);
+ return NULL;
+ } else {
+ pj_assert(0);
+ return NULL;
+ }
+
+ return hdr;
+
+}
+
+static pjsip_www_authenticate_hdr* pjsip_www_authenticate_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_www_authenticate_hdr *rhs)
+{
+ /* This function also serves Proxy-Authenticate header. */
+ pjsip_www_authenticate_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
diff --git a/pjsip/src/pjsip/sip_auth_parser.c b/pjsip/src/pjsip/sip_auth_parser.c index 5e121b50..b6c8172c 100644 --- a/pjsip/src/pjsip/sip_auth_parser.c +++ b/pjsip/src/pjsip/sip_auth_parser.c @@ -1,275 +1,297 @@ -/* $Id$ - */ -#include <pjsip/sip_auth_parser.h> -#include <pjsip/sip_auth_msg.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_auth_parser.h>
+#include <pjsip/sip_auth_msg.h>
#include <pjsip/sip_parser.h>
-#include <pj/assert.h> -#include <pj/string.h> -#include <pj/except.h> - -static pjsip_hdr* parse_hdr_authorization ( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_proxy_authorization ( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_www_authenticate ( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_proxy_authenticate ( pjsip_parse_ctx *ctx ); - +#include <pj/assert.h>
+#include <pj/string.h>
+#include <pj/except.h>
+
+static pjsip_hdr* parse_hdr_authorization ( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_proxy_authorization ( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_www_authenticate ( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_proxy_authenticate ( pjsip_parse_ctx *ctx );
+
static void parse_digest_credential ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_credential *cred); + pjsip_digest_credential *cred);
static void parse_pgp_credential ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_credential *cred); + pjsip_pgp_credential *cred);
static void parse_digest_challenge ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_challenge *chal); + pjsip_digest_challenge *chal);
static void parse_pgp_challenge ( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_challenge *chal); - -const pj_str_t pjsip_USERNAME_STR = { "username", 8 }, - pjsip_REALM_STR = { "realm", 5}, - pjsip_NONCE_STR = { "nonce", 5}, - pjsip_URI_STR = { "uri", 3 }, - pjsip_RESPONSE_STR = { "response", 8 }, - pjsip_ALGORITHM_STR = { "algorithm", 9 }, - pjsip_DOMAIN_STR = { "domain", 6 }, - pjsip_STALE_STR = { "stale", 5}, - pjsip_QOP_STR = { "qop", 3}, - pjsip_CNONCE_STR = { "cnonce", 6}, - pjsip_OPAQUE_STR = { "opaque", 6}, - pjsip_NC_STR = { "nc", 2}, - pjsip_TRUE_STR = { "true", 4}, - pjsip_QUOTED_TRUE_STR = { "\"true\"", 6}, - pjsip_FALSE_STR = { "false", 5}, - pjsip_QUOTED_FALSE_STR = { "\"false\"", 7}, - pjsip_DIGEST_STR = { "Digest", 6}, - pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8}, - pjsip_PGP_STR = { "PGP", 3 }, - pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 }, - pjsip_MD5_STR = { "md5", 3 }, - pjsip_QUOTED_MD5_STR = { "\"md5\"", 5}, - pjsip_AUTH_STR = { "auth", 4}, - pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 }; - - + pjsip_pgp_challenge *chal);
+
+const pj_str_t pjsip_USERNAME_STR = { "username", 8 },
+ pjsip_REALM_STR = { "realm", 5},
+ pjsip_NONCE_STR = { "nonce", 5},
+ pjsip_URI_STR = { "uri", 3 },
+ pjsip_RESPONSE_STR = { "response", 8 },
+ pjsip_ALGORITHM_STR = { "algorithm", 9 },
+ pjsip_DOMAIN_STR = { "domain", 6 },
+ pjsip_STALE_STR = { "stale", 5},
+ pjsip_QOP_STR = { "qop", 3},
+ pjsip_CNONCE_STR = { "cnonce", 6},
+ pjsip_OPAQUE_STR = { "opaque", 6},
+ pjsip_NC_STR = { "nc", 2},
+ pjsip_TRUE_STR = { "true", 4},
+ pjsip_QUOTED_TRUE_STR = { "\"true\"", 6},
+ pjsip_FALSE_STR = { "false", 5},
+ pjsip_QUOTED_FALSE_STR = { "\"false\"", 7},
+ pjsip_DIGEST_STR = { "Digest", 6},
+ pjsip_QUOTED_DIGEST_STR = { "\"Digest\"", 8},
+ pjsip_PGP_STR = { "PGP", 3 },
+ pjsip_QUOTED_PGP_STR = { "\"PGP\"", 5 },
+ pjsip_MD5_STR = { "md5", 3 },
+ pjsip_QUOTED_MD5_STR = { "\"md5\"", 5},
+ pjsip_AUTH_STR = { "auth", 4},
+ pjsip_QUOTED_AUTH_STR = { "\"auth\"", 6 };
+
+
static void parse_digest_credential( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_credential *cred) -{ - for (;;) { - pj_str_t name, value; - - pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE); - - if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) { - cred->username = value; - - } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) { - cred->realm = value; - - } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { - cred->nonce = value; - - } else if (!pj_stricmp(&name, &pjsip_URI_STR)) { - cred->uri = value; - - } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) { - cred->response = value; - - } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { - cred->algorithm = value; - - } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) { - cred->cnonce = value; - - } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { - cred->opaque = value; - - } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { - cred->qop = value; - - } else if (!pj_stricmp(&name, &pjsip_NC_STR)) { - cred->nc = value; - - } else { - pjsip_concat_param_imp(&cred->other_param,pool,&name,&value, ','); - } - - /* Eat comma */ - if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',') - pj_scan_get_char(scanner); - else - break; - } -} - + pjsip_digest_credential *cred)
+{
+ for (;;) {
+ pj_str_t name, value;
+
+ pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE);
+
+ if (!pj_stricmp(&name, &pjsip_USERNAME_STR)) {
+ cred->username = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+ cred->realm = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+ cred->nonce = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_URI_STR)) {
+ cred->uri = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_RESPONSE_STR)) {
+ cred->response = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+ cred->algorithm = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_CNONCE_STR)) {
+ cred->cnonce = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+ cred->opaque = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+ cred->qop = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_NC_STR)) {
+ cred->nc = value;
+
+ } else {
+ pjsip_concat_param_imp(&cred->other_param,pool,&name,&value, ',');
+ }
+
+ /* Eat comma */
+ if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
+ pj_scan_get_char(scanner);
+ else
+ break;
+ }
+}
+
static void parse_pgp_credential( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_credential *cred) -{ - PJ_UNUSED_ARG(scanner); - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(cred); - - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); -} - + pjsip_pgp_credential *cred)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(cred);
+
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
static void parse_digest_challenge( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_digest_challenge *chal) -{ - for (;;) { - pj_str_t name, value; - - pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE); - - if (!pj_stricmp(&name, &pjsip_REALM_STR)) { - chal->realm = value; - - } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) { - chal->domain = value; - - } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) { - chal->nonce = value; - - } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) { - chal->opaque = value; - - } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) { + pjsip_digest_challenge *chal)
+{
+ for (;;) {
+ pj_str_t name, value;
+
+ pjsip_parse_param_imp(scanner, &name, &value,PJSIP_PARSE_REMOVE_QUOTE);
+
+ if (!pj_stricmp(&name, &pjsip_REALM_STR)) {
+ chal->realm = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_DOMAIN_STR)) {
+ chal->domain = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_NONCE_STR)) {
+ chal->nonce = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_OPAQUE_STR)) {
+ chal->opaque = value;
+
+ } else if (!pj_stricmp(&name, &pjsip_STALE_STR)) {
if (!pj_stricmp(&value, &pjsip_TRUE_STR) ||
!pj_stricmp(&value, &pjsip_QUOTED_TRUE_STR))
- { + {
chal->stale = 1;
- } - - } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) { - chal->algorithm = value; - - - } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) { - chal->qop = value; - - } else { + }
+
+ } else if (!pj_stricmp(&name, &pjsip_ALGORITHM_STR)) {
+ chal->algorithm = value;
+
+
+ } else if (!pj_stricmp(&name, &pjsip_QOP_STR)) {
+ chal->qop = value;
+
+ } else {
pjsip_concat_param_imp(&chal->other_param, pool,
- &name, &value, ','); - } - - /* Eat comma */ - if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',') - pj_scan_get_char(scanner); - else - break; - } -} - + &name, &value, ',');
+ }
+
+ /* Eat comma */
+ if (!pj_scan_is_eof(scanner) && *scanner->curptr == ',')
+ pj_scan_get_char(scanner);
+ else
+ break;
+ }
+}
+
static void parse_pgp_challenge( pj_scanner *scanner, pj_pool_t *pool,
- pjsip_pgp_challenge *chal) -{ - PJ_UNUSED_ARG(scanner); - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(chal); - - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); -} - -static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool, - pjsip_authorization_hdr *hdr) -{ - if (*scanner->curptr == '"') { - pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); - hdr->scheme.ptr++; - hdr->scheme.slen -= 2; - } else { - pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme); - } - - if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { - - parse_digest_credential(scanner, pool, &hdr->credential.digest); - - } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { - - parse_pgp_credential( scanner, pool, &hdr->credential.pgp); - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - pjsip_parse_end_hdr_imp( scanner ); -} - -static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool, - pjsip_www_authenticate_hdr *hdr) -{ - if (*scanner->curptr == '"') { - pj_scan_get_quote(scanner, '"', '"', &hdr->scheme); - hdr->scheme.ptr++; - hdr->scheme.slen -= 2; - } else { - pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme); - } - - if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) { - - parse_digest_challenge(scanner, pool, &hdr->challenge.digest); - - } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) { - - parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp); - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - pjsip_parse_end_hdr_imp( scanner ); -} - - -static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx ) -{ - pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool); - int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr); - return (pjsip_hdr*)hdr; -} - -static pjsip_hdr* parse_hdr_proxy_authorization( pjsip_parse_ctx *ctx ) -{ + pjsip_pgp_challenge *chal)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(chal);
+
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+static void int_parse_hdr_authorization( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_authorization_hdr *hdr)
+{
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+ hdr->scheme.ptr++;
+ hdr->scheme.slen -= 2;
+ } else {
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
+ }
+
+ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+ parse_digest_credential(scanner, pool, &hdr->credential.digest);
+
+ } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+ parse_pgp_credential( scanner, pool, &hdr->credential.pgp);
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+}
+
+static void int_parse_hdr_authenticate( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_www_authenticate_hdr *hdr)
+{
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote(scanner, '"', '"', &hdr->scheme);
+ hdr->scheme.ptr++;
+ hdr->scheme.slen -= 2;
+ } else {
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->scheme);
+ }
+
+ if (!pj_stricmp(&hdr->scheme, &pjsip_DIGEST_STR)) {
+
+ parse_digest_challenge(scanner, pool, &hdr->challenge.digest);
+
+ } else if (!pj_stricmp(&hdr->scheme, &pjsip_PGP_STR)) {
+
+ parse_pgp_challenge(scanner, pool, &hdr->challenge.pgp);
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ pjsip_parse_end_hdr_imp( scanner );
+}
+
+
+static pjsip_hdr* parse_hdr_authorization( pjsip_parse_ctx *ctx )
+{
+ pjsip_authorization_hdr *hdr = pjsip_authorization_hdr_create(ctx->pool);
+ int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+static pjsip_hdr* parse_hdr_proxy_authorization( pjsip_parse_ctx *ctx )
+{
pjsip_proxy_authorization_hdr *hdr =
- pjsip_proxy_authorization_hdr_create(ctx->pool); - int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr); - return (pjsip_hdr*)hdr; -} - -static pjsip_hdr* parse_hdr_www_authenticate( pjsip_parse_ctx *ctx ) -{ + pjsip_proxy_authorization_hdr_create(ctx->pool);
+ int_parse_hdr_authorization(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+static pjsip_hdr* parse_hdr_www_authenticate( pjsip_parse_ctx *ctx )
+{
pjsip_www_authenticate_hdr *hdr =
- pjsip_www_authenticate_hdr_create(ctx->pool); - int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr); - return (pjsip_hdr*)hdr; -} - -static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx ) -{ + pjsip_www_authenticate_hdr_create(ctx->pool);
+ int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+static pjsip_hdr* parse_hdr_proxy_authenticate( pjsip_parse_ctx *ctx )
+{
pjsip_proxy_authenticate_hdr *hdr =
- pjsip_proxy_authenticate_hdr_create(ctx->pool); - int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr); - return (pjsip_hdr*)hdr; -} - - -PJ_DEF(pj_status_t) pjsip_auth_init_parser() + pjsip_proxy_authenticate_hdr_create(ctx->pool);
+ int_parse_hdr_authenticate(ctx->scanner, ctx->pool, hdr);
+ return (pjsip_hdr*)hdr;
+}
+
+
+PJ_DEF(pj_status_t) pjsip_auth_init_parser()
{
pj_status_t status;
- +
status = pjsip_register_hdr_parser( "Authorization", NULL,
&parse_hdr_authorization);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
status = pjsip_register_hdr_parser( "Proxy-Authorization", NULL,
&parse_hdr_proxy_authorization);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
status = pjsip_register_hdr_parser( "WWW-Authenticate", NULL,
&parse_hdr_www_authenticate);
- PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); + PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
status = pjsip_register_hdr_parser( "Proxy-Authenticate", NULL,
&parse_hdr_proxy_authenticate);
PJ_ASSERT_RETURN(status==PJ_SUCCESS, status);
- return PJ_SUCCESS; -} - -PJ_DEF(void) pjsip_auth_deinit_parser() -{ -} - + return PJ_SUCCESS;
+}
+
+PJ_DEF(void) pjsip_auth_deinit_parser()
+{
+}
+
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index 49c70b1b..08d68278 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -1,841 +1,863 @@ -/* $Id$ - */ -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_transaction.h> -#include <pjsip/sip_private.h> -#include <pjsip/sip_event.h> -#include <pjsip/sip_resolve.h> -#include <pjsip/sip_module.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_private.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_module.h>
#include <pjsip/sip_misc.h>
-#include <pjsip/sip_errno.h> -#include <pj/except.h> -#include <pj/log.h> -#include <pj/string.h> -#include <pj/os.h> -#include <pj/pool.h> -#include <pj/hash.h> +#include <pjsip/sip_errno.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/hash.h>
#include <pj/assert.h>
#include <pj/errno.h>
- - -#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION -#define THIS_FILE "endpoint" - -#define MAX_METHODS 32 - -/** - * The SIP endpoint. - */ -struct pjsip_endpoint -{ - /** Pool to allocate memory for the endpoint. */ - pj_pool_t *pool; - - /** Mutex for the pool, hash table, and event list/queue. */ - pj_mutex_t *mutex; - - /** Pool factory. */ - pj_pool_factory *pf; - - /** Transaction table. */ - pj_hash_table_t *tsx_table; - - /** Mutex for transaction table. */ - pj_mutex_t *tsx_table_mutex; - - /** Timer heap. */ - pj_timer_heap_t *timer_heap; - - /** Transport manager. */ - pjsip_transport_mgr *transport_mgr; - - /** DNS Resolver. */ - pjsip_resolver_t *resolver; - - /** Number of modules registered. */ - pj_uint32_t mod_count; - - /** Modules. */ - pjsip_module *modules[PJSIP_MAX_MODULE]; - - /** 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; - - /** Additional request headers. */ - pjsip_hdr req_hdr; -}; - - - -/* - * Prototypes. - */ -static void endpt_transport_callback( pjsip_endpoint *, pjsip_rx_data *rdata ); - - -/* - * Create transaction. - * Defined in sip_transaction.c - */ +
+
+#define PJSIP_EX_NO_MEMORY PJ_NO_MEMORY_EXCEPTION
+#define THIS_FILE "endpoint"
+
+#define MAX_METHODS 32
+
+/**
+ * The SIP endpoint.
+ */
+struct pjsip_endpoint
+{
+ /** Pool to allocate memory for the endpoint. */
+ pj_pool_t *pool;
+
+ /** Mutex for the pool, hash table, and event list/queue. */
+ pj_mutex_t *mutex;
+
+ /** Pool factory. */
+ pj_pool_factory *pf;
+
+ /** Transaction table. */
+ pj_hash_table_t *tsx_table;
+
+ /** Mutex for transaction table. */
+ pj_mutex_t *tsx_table_mutex;
+
+ /** Timer heap. */
+ pj_timer_heap_t *timer_heap;
+
+ /** Transport manager. */
+ pjsip_transport_mgr *transport_mgr;
+
+ /** DNS Resolver. */
+ pjsip_resolver_t *resolver;
+
+ /** Number of modules registered. */
+ pj_uint32_t mod_count;
+
+ /** Modules. */
+ pjsip_module *modules[PJSIP_MAX_MODULE];
+
+ /** 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;
+
+ /** Additional request headers. */
+ pjsip_hdr req_hdr;
+};
+
+
+
+/*
+ * Prototypes.
+ */
+static void endpt_transport_callback( pjsip_endpoint *, pjsip_rx_data *rdata );
+
+
+/*
+ * Create transaction.
+ * Defined in sip_transaction.c
+ */
pj_status_t pjsip_tsx_create( pj_pool_t *pool, pjsip_endpoint *endpt,
- pjsip_transaction **tsx ); - -/* - * This is the global handler for memory allocation failure, for pools that - * are created by the endpoint (by default, all pools ARE allocated by - * endpoint). The error is handled by throwing exception, and hopefully, - * the exception will be handled by the application (or this library). - */ -static void pool_callback( pj_pool_t *pool, pj_size_t size ) -{ - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(size); - - PJ_THROW(PJSIP_EX_NO_MEMORY); -} - - -/* - * Initialize modules. - */ -static pj_status_t init_modules( pjsip_endpoint *endpt ) -{ - pj_status_t status; - unsigned i; - //pj_str_t str_COMMA = { ", ", 2 }; - extern pjsip_module aux_tsx_module; - - PJ_LOG(5, (THIS_FILE, "init_modules()")); - - /* Load static modules. */ - endpt->mod_count = PJSIP_MAX_MODULE; - status = register_static_modules( &endpt->mod_count, endpt->modules ); - if (status != 0) { - return status; - } - - /* Add mini aux module. */ - endpt->modules[endpt->mod_count++] = &aux_tsx_module; - - /* Load dynamic modules. */ - // Not supported yet! - - /* Sort modules on the priority. */ - for (i=endpt->mod_count-1; i>0; --i) { - pj_uint32_t max = 0; - unsigned j; - for (j=1; j<=i; ++j) { - if (endpt->modules[j]->priority > endpt->modules[max]->priority) - max = j; - } - if (max != i) { - pjsip_module *temp = endpt->modules[max]; - endpt->modules[max] = endpt->modules[i]; - endpt->modules[i] = temp; - } - } - - /* Initialize each module. */ - for (i=0; i < endpt->mod_count; ++i) { - int j; - - pjsip_module *mod = endpt->modules[i]; - if (mod->init_module) { - status = mod->init_module(endpt, mod, i); - if (status != 0) { - return status; - } - } - - /* Collect all supported methods from modules. */ - for (j=0; j<mod->method_cnt; ++j) { - unsigned k; - for (k=0; k<endpt->method_cnt; ++k) { - if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0) - break; - } - if (k == endpt->method_cnt) { - if (endpt->method_cnt < MAX_METHODS) { - endpt->methods[endpt->method_cnt++] = mod->methods[j]; - } else { - PJ_LOG(1,(THIS_FILE, "Too many methods")); - return -1; - } - } - } - } - - /* Create Allow header. */ - endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool ); - endpt->allow_hdr->count = endpt->method_cnt; - for (i=0; i<endpt->method_cnt; ++i) { - endpt->allow_hdr->values[i] = endpt->methods[i]->name; - } - - /* Start each module. */ - for (i=0; i < endpt->mod_count; ++i) { - pjsip_module *mod = endpt->modules[i]; - if (mod->start_module) { - status = mod->start_module(mod); - if (status != 0) { - return status; - } - } - } - - /* Done. */ - return 0; -} - -/* - * Unregister the transaction from the hash table, and destroy the resources - * from the transaction. - */ -PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name)); - - pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED); - - /* No need to lock transaction. - * This function typically is called from the transaction callback, which - * means that transaction mutex is being held. - */ - pj_assert( pj_mutex_is_locked(tsx->mutex) ); - - /* Lock endpoint. */ - pj_mutex_lock( endpt->tsx_table_mutex ); - - /* Unregister from the hash table. */ - pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr, - tsx->transaction_key.slen, NULL); - - /* Unlock endpoint mutex. */ - pj_mutex_unlock( endpt->tsx_table_mutex ); - - /* Destroy transaction mutex. */ - pj_mutex_destroy( tsx->mutex ); - - /* Release the pool for the transaction. */ - pj_pool_release(tsx->pool); - - PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx)); -} - - -/* - * Receive transaction events from transactions and dispatch them to the - * modules. - */ -static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt) -{ - unsigned i; - - /* Dispatch event to modules. */ - for (i=0; i<endpt->mod_count; ++i) { - pjsip_module *mod = endpt->modules[i]; - if (mod && mod->tsx_handler) { - mod->tsx_handler( mod, evt ); - } - } - - /* Destroy transaction if it is terminated. */ - if (evt->type == PJSIP_EVENT_TSX_STATE && - evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED) - { - /* No need to lock mutex. Mutex is locked inside the destroy function */ - pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx ); - } -} - -/* - * Receive transaction events from transactions and put in the event queue - * to be processed later. - */ -void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt ) + pjsip_transaction **tsx );
+
+/*
+ * This is the global handler for memory allocation failure, for pools that
+ * are created by the endpoint (by default, all pools ARE allocated by
+ * endpoint). The error is handled by throwing exception, and hopefully,
+ * the exception will be handled by the application (or this library).
+ */
+static void pool_callback( pj_pool_t *pool, pj_size_t size )
{
- // Need to protect this with try/catch? - endpt_do_event(endpt, evt); -} - -/* - * Get "Allow" header. - */ -PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt ) -{ - return endpt->allow_hdr; -} - -/* - * Get additional headers to be put in outgoing request message. - */ -PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt) -{ - return &endpt->req_hdr; -} - -PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt, - int url_cnt, const pj_str_t url[]) -{ - int i; - pjsip_route_hdr *hdr; - pj_str_t str_ROUTE = { "Route", 5 }; - - /* Lock endpoint mutex. */ - pj_mutex_lock(endpt->mutex); - - pj_list_init(&endpt->route_hdr_list); - - for (i=0; i<url_cnt; ++i) { - int len = url[i].slen; - char *dup = pj_pool_alloc(endpt->pool, len + 1); - pj_memcpy(dup, url[i].ptr, len); - dup[len] = '\0'; - - hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL); - if (!hdr) { - pj_mutex_unlock(endpt->mutex); - PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup)); - return -1; - } - - pj_assert(hdr->type == PJSIP_H_ROUTE); - pj_list_insert_before(&endpt->route_hdr_list, hdr); - } - - /* Unlock endpoint mutex. */ - pj_mutex_unlock(endpt->mutex); - - return 0; -} - -/* - * Get "Route" header list. - */ -PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt ) -{ - return &endpt->route_hdr_list; -} - - -/* - * Initialize endpoint. - */ + PJ_UNUSED_ARG(pool);
+ PJ_UNUSED_ARG(size);
+
+ PJ_THROW(PJSIP_EX_NO_MEMORY);
+}
+
+
+/*
+ * Initialize modules.
+ */
+static pj_status_t init_modules( pjsip_endpoint *endpt )
+{
+ pj_status_t status;
+ unsigned i;
+ //pj_str_t str_COMMA = { ", ", 2 };
+ extern pjsip_module aux_tsx_module;
+
+ PJ_LOG(5, (THIS_FILE, "init_modules()"));
+
+ /* Load static modules. */
+ endpt->mod_count = PJSIP_MAX_MODULE;
+ status = register_static_modules( &endpt->mod_count, endpt->modules );
+ if (status != 0) {
+ return status;
+ }
+
+ /* Add mini aux module. */
+ endpt->modules[endpt->mod_count++] = &aux_tsx_module;
+
+ /* Load dynamic modules. */
+ // Not supported yet!
+
+ /* Sort modules on the priority. */
+ for (i=endpt->mod_count-1; i>0; --i) {
+ pj_uint32_t max = 0;
+ unsigned j;
+ for (j=1; j<=i; ++j) {
+ if (endpt->modules[j]->priority > endpt->modules[max]->priority)
+ max = j;
+ }
+ if (max != i) {
+ pjsip_module *temp = endpt->modules[max];
+ endpt->modules[max] = endpt->modules[i];
+ endpt->modules[i] = temp;
+ }
+ }
+
+ /* Initialize each module. */
+ for (i=0; i < endpt->mod_count; ++i) {
+ int j;
+
+ pjsip_module *mod = endpt->modules[i];
+ if (mod->init_module) {
+ status = mod->init_module(endpt, mod, i);
+ if (status != 0) {
+ return status;
+ }
+ }
+
+ /* Collect all supported methods from modules. */
+ for (j=0; j<mod->method_cnt; ++j) {
+ unsigned k;
+ for (k=0; k<endpt->method_cnt; ++k) {
+ if (pjsip_method_cmp(mod->methods[j], endpt->methods[k]) == 0)
+ break;
+ }
+ if (k == endpt->method_cnt) {
+ if (endpt->method_cnt < MAX_METHODS) {
+ endpt->methods[endpt->method_cnt++] = mod->methods[j];
+ } else {
+ PJ_LOG(1,(THIS_FILE, "Too many methods"));
+ return -1;
+ }
+ }
+ }
+ }
+
+ /* Create Allow header. */
+ endpt->allow_hdr = pjsip_allow_hdr_create( endpt->pool );
+ endpt->allow_hdr->count = endpt->method_cnt;
+ for (i=0; i<endpt->method_cnt; ++i) {
+ endpt->allow_hdr->values[i] = endpt->methods[i]->name;
+ }
+
+ /* Start each module. */
+ for (i=0; i < endpt->mod_count; ++i) {
+ pjsip_module *mod = endpt->modules[i];
+ if (mod->start_module) {
+ status = mod->start_module(mod);
+ if (status != 0) {
+ return status;
+ }
+ }
+ }
+
+ /* Done. */
+ return 0;
+}
+
+/*
+ * Unregister the transaction from the hash table, and destroy the resources
+ * from the transaction.
+ */
+PJ_DEF(void) pjsip_endpt_destroy_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_tsx(%s)", tsx->obj_name));
+
+ pj_assert(tsx->state == PJSIP_TSX_STATE_DESTROYED);
+
+ /* No need to lock transaction.
+ * This function typically is called from the transaction callback, which
+ * means that transaction mutex is being held.
+ */
+ pj_assert( pj_mutex_is_locked(tsx->mutex) );
+
+ /* Lock endpoint. */
+ pj_mutex_lock( endpt->tsx_table_mutex );
+
+ /* Unregister from the hash table. */
+ pj_hash_set( NULL, endpt->tsx_table, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, NULL);
+
+ /* Unlock endpoint mutex. */
+ pj_mutex_unlock( endpt->tsx_table_mutex );
+
+ /* Destroy transaction mutex. */
+ pj_mutex_destroy( tsx->mutex );
+
+ /* Release the pool for the transaction. */
+ pj_pool_release(tsx->pool);
+
+ PJ_LOG(4, (THIS_FILE, "tsx%p destroyed", tsx));
+}
+
+
+/*
+ * Receive transaction events from transactions and dispatch them to the
+ * modules.
+ */
+static void endpt_do_event( pjsip_endpoint *endpt, pjsip_event *evt)
+{
+ unsigned i;
+
+ /* Dispatch event to modules. */
+ for (i=0; i<endpt->mod_count; ++i) {
+ pjsip_module *mod = endpt->modules[i];
+ if (mod && mod->tsx_handler) {
+ mod->tsx_handler( mod, evt );
+ }
+ }
+
+ /* Destroy transaction if it is terminated. */
+ if (evt->type == PJSIP_EVENT_TSX_STATE &&
+ evt->body.tsx_state.tsx->state == PJSIP_TSX_STATE_DESTROYED)
+ {
+ /* No need to lock mutex. Mutex is locked inside the destroy function */
+ pjsip_endpt_destroy_tsx( endpt, evt->body.tsx_state.tsx );
+ }
+}
+
+/*
+ * Receive transaction events from transactions and put in the event queue
+ * to be processed later.
+ */
+void pjsip_endpt_send_tsx_event( pjsip_endpoint *endpt, pjsip_event *evt )
+{
+ // Need to protect this with try/catch?
+ endpt_do_event(endpt, evt);
+}
+
+/*
+ * Get "Allow" header.
+ */
+PJ_DECL(const pjsip_allow_hdr*) pjsip_endpt_get_allow_hdr( pjsip_endpoint *endpt )
+{
+ return endpt->allow_hdr;
+}
+
+/*
+ * Get additional headers to be put in outgoing request message.
+ */
+PJ_DEF(const pjsip_hdr*) pjsip_endpt_get_request_headers(pjsip_endpoint *endpt)
+{
+ return &endpt->req_hdr;
+}
+
+PJ_DEF(pj_status_t) pjsip_endpt_set_proxies( pjsip_endpoint *endpt,
+ int url_cnt, const pj_str_t url[])
+{
+ int i;
+ pjsip_route_hdr *hdr;
+ pj_str_t str_ROUTE = { "Route", 5 };
+
+ /* Lock endpoint mutex. */
+ pj_mutex_lock(endpt->mutex);
+
+ pj_list_init(&endpt->route_hdr_list);
+
+ for (i=0; i<url_cnt; ++i) {
+ int len = url[i].slen;
+ char *dup = pj_pool_alloc(endpt->pool, len + 1);
+ pj_memcpy(dup, url[i].ptr, len);
+ dup[len] = '\0';
+
+ hdr = pjsip_parse_hdr(endpt->pool, &str_ROUTE, dup, len, NULL);
+ if (!hdr) {
+ pj_mutex_unlock(endpt->mutex);
+ PJ_LOG(4,(THIS_FILE, "Invalid URL %s in proxy URL", dup));
+ return -1;
+ }
+
+ pj_assert(hdr->type == PJSIP_H_ROUTE);
+ pj_list_insert_before(&endpt->route_hdr_list, hdr);
+ }
+
+ /* Unlock endpoint mutex. */
+ pj_mutex_unlock(endpt->mutex);
+
+ return 0;
+}
+
+/*
+ * Get "Route" header list.
+ */
+PJ_DEF(const pjsip_route_hdr*) pjsip_endpt_get_routing( pjsip_endpoint *endpt )
+{
+ return &endpt->route_hdr_list;
+}
+
+
+/*
+ * Initialize endpoint.
+ */
PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf,
- pjsip_endpoint **p_endpt) -{ - pj_status_t status; - pj_pool_t *pool; - pjsip_endpoint *endpt; - pjsip_max_forwards_hdr *mf_hdr; - - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()")); + pjsip_endpoint **p_endpt)
+{
+ pj_status_t status;
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ pjsip_max_forwards_hdr *mf_hdr;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create()"));
*p_endpt = NULL;
- - /* Create pool */ - pool = pj_pool_create(pf, "pept%p", - PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT, - &pool_callback); - if (!pool) - return PJ_ENOMEM; - - /* Create endpoint. */ - endpt = pj_pool_calloc(pool, 1, sizeof(*endpt)); - endpt->pool = pool; - endpt->pf = pf; - - /* Create mutex for the events, etc. */ - status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex ); - if (status != PJ_SUCCESS) { - goto on_error; - } - - /* Create mutex for the transaction table. */ +
+ /* Create pool */
+ pool = pj_pool_create(pf, "pept%p",
+ PJSIP_POOL_LEN_ENDPT, PJSIP_POOL_INC_ENDPT,
+ &pool_callback);
+ if (!pool)
+ return PJ_ENOMEM;
+
+ /* Create endpoint. */
+ endpt = pj_pool_calloc(pool, 1, sizeof(*endpt));
+ endpt->pool = pool;
+ endpt->pf = pf;
+
+ /* Create mutex for the events, etc. */
+ status = pj_mutex_create_recursive( endpt->pool, "ept%p", &endpt->mutex );
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create mutex for the transaction table. */
status = pj_mutex_create_recursive( endpt->pool, "mtbl%p",
- &endpt->tsx_table_mutex); - if (status != PJ_SUCCESS) { - goto on_error; - } - - /* Create hash table for transaction. */ - endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT ); - if (!endpt->tsx_table) { - status = PJ_ENOMEM; - goto on_error; - } - - /* Create timer heap to manage all timers within this endpoint. */ + &endpt->tsx_table_mutex);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create hash table for transaction. */
+ endpt->tsx_table = pj_hash_create( endpt->pool, PJSIP_MAX_TSX_COUNT );
+ if (!endpt->tsx_table) {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ /* Create timer heap to manage all timers within this endpoint. */
status = pj_timer_heap_create( endpt->pool, PJSIP_MAX_TIMER_COUNT,
- &endpt->timer_heap); - if (status != PJ_SUCCESS) { - goto on_error; - } - - /* Create transport manager. */ - status = pjsip_transport_mgr_create( endpt->pool, - endpt, + &endpt->timer_heap);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create transport manager. */
+ status = pjsip_transport_mgr_create( endpt->pool,
+ endpt,
&endpt_transport_callback,
- &endpt->transport_mgr); - if (status != PJ_SUCCESS) { - goto on_error; - } - - /* Create asynchronous DNS resolver. */ - endpt->resolver = pjsip_resolver_create(endpt->pool); - if (!endpt->resolver) { - PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver")); - goto on_error; - } - - /* Initialize TLS ID for transaction lock. */ - status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id); - if (status != PJ_SUCCESS) { - goto on_error; - } - pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL); - - /* Initialize request headers. */ - pj_list_init(&endpt->req_hdr); - - /* Initialist "Route" header list. */ - 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; - pj_list_insert_before( &endpt->req_hdr, mf_hdr); - - /* Load and init modules. */ - status = init_modules(endpt); - if (status != PJ_SUCCESS) { - PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()")); - return status; - } - + &endpt->transport_mgr);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+
+ /* Create asynchronous DNS resolver. */
+ endpt->resolver = pjsip_resolver_create(endpt->pool);
+ if (!endpt->resolver) {
+ PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error creating resolver"));
+ goto on_error;
+ }
+
+ /* Initialize TLS ID for transaction lock. */
+ status = pj_thread_local_alloc(&pjsip_tsx_lock_tls_id);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ pj_thread_local_set(pjsip_tsx_lock_tls_id, NULL);
+
+ /* Initialize request headers. */
+ pj_list_init(&endpt->req_hdr);
+
+ /* Initialist "Route" header list. */
+ 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;
+ pj_list_insert_before( &endpt->req_hdr, mf_hdr);
+
+ /* Load and init modules. */
+ status = init_modules(endpt);
+ if (status != PJ_SUCCESS) {
+ PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init(): error in init_modules()"));
+ return status;
+ }
+
/* Done. */
- *p_endpt = endpt; - return status; - -on_error: - if (endpt->transport_mgr) { - pjsip_transport_mgr_destroy(endpt->transport_mgr); - endpt->transport_mgr = NULL; - } - if (endpt->mutex) { - pj_mutex_destroy(endpt->mutex); - endpt->mutex = NULL; - } - if (endpt->tsx_table_mutex) { - pj_mutex_destroy(endpt->tsx_table_mutex); - endpt->tsx_table_mutex = NULL; - } - pj_pool_release( endpt->pool ); - - PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed")); - return status; -} - -/* - * Destroy endpoint. - */ -PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()")); - - /* Shutdown and destroy all transports. */ - pjsip_transport_mgr_destroy(endpt->transport_mgr); - - /* Delete endpoint mutex. */ - pj_mutex_destroy(endpt->mutex); - - /* Delete transaction table mutex. */ - pj_mutex_destroy(endpt->tsx_table_mutex); - - /* Finally destroy pool. */ - pj_pool_release(endpt->pool); -} - -/* - * Create new pool. - */ -PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt, - const char *pool_name, - pj_size_t initial, - pj_size_t increment ) -{ - pj_pool_t *pool; - - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()")); - - /* Lock endpoint mutex. */ - pj_mutex_lock(endpt->mutex); - - /* Create pool */ - pool = pj_pool_create( endpt->pf, pool_name, - initial, increment, &pool_callback); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->mutex); - - if (pool) { - PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool))); - } else { - PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name)); - } - - return pool; -} - -/* - * Return back pool to endpoint's pool manager to be either destroyed or - * recycled. - */ -PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool ) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool))); - - pj_mutex_lock(endpt->mutex); - pj_pool_release( pool ); - pj_mutex_unlock(endpt->mutex); -} - -/* - * Handle events. - */ -PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt, - const pj_time_val *max_timeout) -{ - pj_time_val timeout; - int i; - - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_handle_events()")); - - /* Poll the timer. The timer heap has its own mutex for better - * granularity, so we don't need to lock end endpoint. We also keep - * polling the timer while we have events. - */ - timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */ - for (i=0; i<10; ++i) { - if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1) - break; - } - - /* If caller specifies maximum time to wait, then compare the value with - * the timeout to wait from timer, and use the minimum value. - */ - if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) { - timeout = *max_timeout; - } - - /* Poll events in the transport manager. */ - pjsip_transport_mgr_handle_events( endpt->transport_mgr, &timeout); -} - -/* - * Schedule timer. - */ -PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt, - pj_timer_entry *entry, - const pj_time_val *delay ) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)", - entry, delay->sec, delay->msec)); - return pj_timer_heap_schedule( endpt->timer_heap, entry, delay ); -} - -/* - * Cancel the previously registered timer. - */ -PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt, - pj_timer_entry *entry ) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry)); - pj_timer_heap_cancel( endpt->timer_heap, entry ); -} - -/* - * Create a new transaction. - * Endpoint must then initialize the new transaction as either UAS or UAC, and - * register it to the hash table. - */ + *p_endpt = endpt;
+ return status;
+
+on_error:
+ if (endpt->transport_mgr) {
+ pjsip_transport_mgr_destroy(endpt->transport_mgr);
+ endpt->transport_mgr = NULL;
+ }
+ if (endpt->mutex) {
+ pj_mutex_destroy(endpt->mutex);
+ endpt->mutex = NULL;
+ }
+ if (endpt->tsx_table_mutex) {
+ pj_mutex_destroy(endpt->tsx_table_mutex);
+ endpt->tsx_table_mutex = NULL;
+ }
+ pj_pool_release( endpt->pool );
+
+ PJ_LOG(4, (THIS_FILE, "pjsip_endpt_init() failed"));
+ return status;
+}
+
+/*
+ * Destroy endpoint.
+ */
+PJ_DEF(void) pjsip_endpt_destroy(pjsip_endpoint *endpt)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy()"));
+
+ /* Shutdown and destroy all transports. */
+ pjsip_transport_mgr_destroy(endpt->transport_mgr);
+
+ /* Delete endpoint mutex. */
+ pj_mutex_destroy(endpt->mutex);
+
+ /* Delete transaction table mutex. */
+ pj_mutex_destroy(endpt->tsx_table_mutex);
+
+ /* Finally destroy pool. */
+ pj_pool_release(endpt->pool);
+}
+
+/*
+ * Create new pool.
+ */
+PJ_DEF(pj_pool_t*) pjsip_endpt_create_pool( pjsip_endpoint *endpt,
+ const char *pool_name,
+ pj_size_t initial,
+ pj_size_t increment )
+{
+ pj_pool_t *pool;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_pool()"));
+
+ /* Lock endpoint mutex. */
+ pj_mutex_lock(endpt->mutex);
+
+ /* Create pool */
+ pool = pj_pool_create( endpt->pf, pool_name,
+ initial, increment, &pool_callback);
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->mutex);
+
+ if (pool) {
+ PJ_LOG(5, (THIS_FILE, " pool %s created", pj_pool_getobjname(pool)));
+ } else {
+ PJ_LOG(4, (THIS_FILE, "Unable to create pool %s!", pool_name));
+ }
+
+ return pool;
+}
+
+/*
+ * Return back pool to endpoint's pool manager to be either destroyed or
+ * recycled.
+ */
+PJ_DEF(void) pjsip_endpt_destroy_pool( pjsip_endpoint *endpt, pj_pool_t *pool )
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_destroy_pool(%s)", pj_pool_getobjname(pool)));
+
+ pj_mutex_lock(endpt->mutex);
+ pj_pool_release( pool );
+ pj_mutex_unlock(endpt->mutex);
+}
+
+/*
+ * Handle events.
+ */
+PJ_DEF(void) pjsip_endpt_handle_events( pjsip_endpoint *endpt,
+ const pj_time_val *max_timeout)
+{
+ pj_time_val timeout;
+ int i;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_handle_events()"));
+
+ /* Poll the timer. The timer heap has its own mutex for better
+ * granularity, so we don't need to lock end endpoint. We also keep
+ * polling the timer while we have events.
+ */
+ timeout.sec = timeout.msec = 0; /* timeout is 'out' var. This just to make compiler happy. */
+ for (i=0; i<10; ++i) {
+ if (pj_timer_heap_poll( endpt->timer_heap, &timeout ) < 1)
+ break;
+ }
+
+ /* If caller specifies maximum time to wait, then compare the value with
+ * the timeout to wait from timer, and use the minimum value.
+ */
+ if (max_timeout && PJ_TIME_VAL_GT(timeout, *max_timeout)) {
+ timeout = *max_timeout;
+ }
+
+ /* Poll events in the transport manager. */
+ pjsip_transport_mgr_handle_events( endpt->transport_mgr, &timeout);
+}
+
+/*
+ * Schedule timer.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_schedule_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry,
+ const pj_time_val *delay )
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_schedule_timer(entry=%p, delay=%u.%u)",
+ entry, delay->sec, delay->msec));
+ return pj_timer_heap_schedule( endpt->timer_heap, entry, delay );
+}
+
+/*
+ * Cancel the previously registered timer.
+ */
+PJ_DEF(void) pjsip_endpt_cancel_timer( pjsip_endpoint *endpt,
+ pj_timer_entry *entry )
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_cancel_timer(entry=%p)", entry));
+ pj_timer_heap_cancel( endpt->timer_heap, entry );
+}
+
+/*
+ * Create a new transaction.
+ * Endpoint must then initialize the new transaction as either UAS or UAC, and
+ * register it to the hash table.
+ */
PJ_DEF(pj_status_t) pjsip_endpt_create_tsx(pjsip_endpoint *endpt,
- pjsip_transaction **p_tsx) -{ - pj_pool_t *pool; + pjsip_transaction **p_tsx)
+{
+ pj_pool_t *pool;
PJ_ASSERT_RETURN(endpt && p_tsx, PJ_EINVAL);
- - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()")); - - /* Request one pool for the transaction. Mutex is locked there. */ - pool = pjsip_endpt_create_pool(endpt, "ptsx%p", - PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX); - if (pool == NULL) { - return PJ_ENOMEM; - } - - /* Create the transaction. */ - return pjsip_tsx_create(pool, endpt, p_tsx); -} - -/* - * Register the transaction to the endpoint. - * This will put the transaction to the transaction hash table. Before calling - * this function, the transaction must be INITIALIZED as either UAS or UAC, so - * that the transaction key is built. - */ -PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt, - pjsip_transaction *tsx) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name)); - - pj_assert(tsx->transaction_key.slen != 0); - //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL); - - /* Lock hash table mutex. */ - pj_mutex_lock(endpt->tsx_table_mutex); - - /* Register the transaction to the hash table. */ - pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr, - tsx->transaction_key.slen, tsx); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->tsx_table_mutex); -} - -/* - * Find transaction by the key. - */ -PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt, - const pj_str_t *key ) -{ - pjsip_transaction *tsx; - - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()")); - - /* Start lock mutex in the endpoint. */ - pj_mutex_lock(endpt->tsx_table_mutex); - - /* Find the transaction in the hash table. */ - tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen ); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->tsx_table_mutex); - - return tsx; -} - -/* - * Create key. - */ -static void rdata_create_key( pjsip_rx_data *rdata) -{ - pjsip_role_e role; - if (rdata->msg->type == PJSIP_REQUEST_MSG) { - role = PJSIP_ROLE_UAS; - } else { - role = PJSIP_ROLE_UAC; - } - pjsip_tsx_create_key(rdata->pool, &rdata->key, role, - &rdata->cseq->method, rdata); -} - -/* - * This is the callback that is called by the transport manager when it - * receives a message from the network. - */ -static void endpt_transport_callback( pjsip_endpoint *endpt, - pjsip_rx_data *rdata ) -{ - pjsip_msg *msg = rdata->msg; - pjsip_transaction *tsx; - pj_bool_t a_new_transaction_just_been_created = PJ_FALSE; - - PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata)); - - /* For response, check that the value in Via sent-by match the transport. - * If not matched, silently drop the response. - * Ref: RFC3261 Section 18.1.2 Receiving Response - */ - if (msg->type == PJSIP_RESPONSE_MSG) { - const pj_sockaddr_in *addr; - const char *addr_addr; - int port = rdata->via->sent_by.port; - pj_bool_t mismatch = PJ_FALSE; - if (port == 0) { - int type; - type = pjsip_transport_get_type(rdata->transport); - port = pjsip_transport_get_default_port_for_type(type); - } - addr = pjsip_transport_get_addr_name(rdata->transport); - addr_addr = pj_inet_ntoa(addr->sin_addr); - if (pj_strcmp2(&rdata->via->sent_by.host, addr_addr) != 0) - mismatch = PJ_TRUE; - else if (port != pj_ntohs(addr->sin_port)) { - /* Port or address mismatch, we should discard response */ - /* But we saw one implementation (we don't want to name it to - * protect the innocence) which put wrong sent-by port although - * the "rport" parameter is correct. - * So we discard the response only if the port doesn't match - * both the port in sent-by and rport. We try to be lenient here! - */ - if (rdata->via->rport_param != pj_sockaddr_in_get_port(addr)) - mismatch = PJ_TRUE; - else { - PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by" - " but the rport parameter is correct", - rdata)); - } - } - - if (mismatch) { - pjsip_event e; - - PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA); - endpt_do_event( endpt, &e ); - return; - } - } - - /* Create key for transaction lookup. */ - rdata_create_key( rdata); - - /* Find the transaction for the received message. */ - PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s", - rdata->key.slen, rdata->key.ptr)); - - /* Start lock mutex in the endpoint. */ - pj_mutex_lock(endpt->tsx_table_mutex); - - /* Find the transaction in the hash table. */ - tsx = pj_hash_get( endpt->tsx_table, rdata->key.ptr, rdata->key.slen ); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->tsx_table_mutex); - - /* If the transaction is not found... */ - if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { - - /* - * For response message, discard the message, except if the response is - * an 2xx class response to INVITE, which in this case it must be - * passed to TU to be acked. - */ - if (msg->type == PJSIP_RESPONSE_MSG) { - - /* Inform TU about the 200 message, only if it's INVITE. */ - if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) && - rdata->cseq->method.id == PJSIP_INVITE_METHOD) - { - pjsip_event e; - - /* Should not happen for UA. Tsx theoritically lives until - * all responses are absorbed. - */ - pj_assert(0); - - PJSIP_EVENT_INIT_RX_200_MSG(e, rdata); - endpt_do_event( endpt, &e ); - - } else { - /* Just discard the response, inform TU. */ - pjsip_event e; - +
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tsx()"));
+
+ /* Request one pool for the transaction. Mutex is locked there. */
+ pool = pjsip_endpt_create_pool(endpt, "ptsx%p",
+ PJSIP_POOL_LEN_TSX, PJSIP_POOL_INC_TSX);
+ if (pool == NULL) {
+ return PJ_ENOMEM;
+ }
+
+ /* Create the transaction. */
+ return pjsip_tsx_create(pool, endpt, p_tsx);
+}
+
+/*
+ * Register the transaction to the endpoint.
+ * This will put the transaction to the transaction hash table. Before calling
+ * this function, the transaction must be INITIALIZED as either UAS or UAC, so
+ * that the transaction key is built.
+ */
+PJ_DEF(void) pjsip_endpt_register_tsx( pjsip_endpoint *endpt,
+ pjsip_transaction *tsx)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_register_tsx(%s)", tsx->obj_name));
+
+ pj_assert(tsx->transaction_key.slen != 0);
+ //pj_assert(tsx->state != PJSIP_TSX_STATE_NULL);
+
+ /* Lock hash table mutex. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Register the transaction to the hash table. */
+ pj_hash_set( tsx->pool, endpt->tsx_table, tsx->transaction_key.ptr,
+ tsx->transaction_key.slen, tsx);
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+}
+
+/*
+ * Find transaction by the key.
+ */
+PJ_DECL(pjsip_transaction*) pjsip_endpt_find_tsx( pjsip_endpoint *endpt,
+ const pj_str_t *key )
+{
+ pjsip_transaction *tsx;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_find_tsx()"));
+
+ /* Start lock mutex in the endpoint. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Find the transaction in the hash table. */
+ tsx = pj_hash_get( endpt->tsx_table, key->ptr, key->slen );
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+
+ return tsx;
+}
+
+/*
+ * Create key.
+ */
+static void rdata_create_key( pjsip_rx_data *rdata)
+{
+ pjsip_role_e role;
+ if (rdata->msg->type == PJSIP_REQUEST_MSG) {
+ role = PJSIP_ROLE_UAS;
+ } else {
+ role = PJSIP_ROLE_UAC;
+ }
+ pjsip_tsx_create_key(rdata->pool, &rdata->key, role,
+ &rdata->cseq->method, rdata);
+}
+
+/*
+ * This is the callback that is called by the transport manager when it
+ * receives a message from the network.
+ */
+static void endpt_transport_callback( pjsip_endpoint *endpt,
+ pjsip_rx_data *rdata )
+{
+ pjsip_msg *msg = rdata->msg;
+ pjsip_transaction *tsx;
+ pj_bool_t a_new_transaction_just_been_created = PJ_FALSE;
+
+ PJ_LOG(5, (THIS_FILE, "endpt_transport_callback(rdata=%p)", rdata));
+
+ /* For response, check that the value in Via sent-by match the transport.
+ * If not matched, silently drop the response.
+ * Ref: RFC3261 Section 18.1.2 Receiving Response
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+ const pj_sockaddr_in *addr;
+ const char *addr_addr;
+ int port = rdata->via->sent_by.port;
+ pj_bool_t mismatch = PJ_FALSE;
+ if (port == 0) {
+ int type;
+ type = pjsip_transport_get_type(rdata->transport);
+ port = pjsip_transport_get_default_port_for_type(type);
+ }
+ addr = pjsip_transport_get_addr_name(rdata->transport);
+ addr_addr = pj_inet_ntoa(addr->sin_addr);
+ if (pj_strcmp2(&rdata->via->sent_by.host, addr_addr) != 0)
+ mismatch = PJ_TRUE;
+ else if (port != pj_ntohs(addr->sin_port)) {
+ /* Port or address mismatch, we should discard response */
+ /* But we saw one implementation (we don't want to name it to
+ * protect the innocence) which put wrong sent-by port although
+ * the "rport" parameter is correct.
+ * So we discard the response only if the port doesn't match
+ * both the port in sent-by and rport. We try to be lenient here!
+ */
+ if (rdata->via->rport_param != pj_sockaddr_in_get_port(addr))
+ mismatch = PJ_TRUE;
+ else {
+ PJ_LOG(4,(THIS_FILE, "Response %p has mismatch port in sent-by"
+ " but the rport parameter is correct",
+ rdata));
+ }
+ }
+
+ if (mismatch) {
+ pjsip_event e;
+
+ PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata, PJSIP_EINVALIDVIA);
+ endpt_do_event( endpt, &e );
+ return;
+ }
+ }
+
+ /* Create key for transaction lookup. */
+ rdata_create_key( rdata);
+
+ /* Find the transaction for the received message. */
+ PJ_LOG(5, (THIS_FILE, "finding tsx with key=%.*s",
+ rdata->key.slen, rdata->key.ptr));
+
+ /* Start lock mutex in the endpoint. */
+ pj_mutex_lock(endpt->tsx_table_mutex);
+
+ /* Find the transaction in the hash table. */
+ tsx = pj_hash_get( endpt->tsx_table, rdata->key.ptr, rdata->key.slen );
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->tsx_table_mutex);
+
+ /* If the transaction is not found... */
+ if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+ /*
+ * For response message, discard the message, except if the response is
+ * an 2xx class response to INVITE, which in this case it must be
+ * passed to TU to be acked.
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+
+ /* Inform TU about the 200 message, only if it's INVITE. */
+ if (PJSIP_IS_STATUS_IN_CLASS(msg->line.status.code, 200) &&
+ rdata->cseq->method.id == PJSIP_INVITE_METHOD)
+ {
+ pjsip_event e;
+
+ /* Should not happen for UA. Tsx theoritically lives until
+ * all responses are absorbed.
+ */
+ pj_assert(0);
+
+ PJSIP_EVENT_INIT_RX_200_MSG(e, rdata);
+ endpt_do_event( endpt, &e );
+
+ } else {
+ /* Just discard the response, inform TU. */
+ pjsip_event e;
+
PJSIP_EVENT_INIT_DISCARD_MSG(e, rdata,
- PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST)); - endpt_do_event( endpt, &e ); - } - - /* - * For non-ACK request message, create a new transaction. - */ + PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_CALL_TSX_DOES_NOT_EXIST));
+ endpt_do_event( endpt, &e );
+ }
+
+ /*
+ * For non-ACK request message, create a new transaction.
+ */
} else if (rdata->msg->line.req.method.id != PJSIP_ACK_METHOD) {
pj_status_t status;
- - /* Create transaction, mutex is locked there. */ - status = pjsip_endpt_create_tsx(endpt, &tsx); +
+ /* Create transaction, mutex is locked there. */
+ status = pjsip_endpt_create_tsx(endpt, &tsx);
if (status != PJ_SUCCESS) {
PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
- "Unable to create transaction")); + "Unable to create transaction"));
return;
- } - - /* Initialize transaction as UAS. */ - pjsip_tsx_init_uas( tsx, rdata ); - - /* Register transaction, mutex is locked there. */ - pjsip_endpt_register_tsx( endpt, tsx ); - - a_new_transaction_just_been_created = PJ_TRUE; - } - } - - /* If transaction is found (or newly created), pass the message. - * Otherwise if it's an ACK request, pass directly to TU. - */ - if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) { - /* Dispatch message to transaction. */ - pjsip_tsx_on_rx_msg( tsx, rdata ); - - } else if (rdata->msg->line.req.method.id == PJSIP_ACK_METHOD) { - /* - * This is an ACK message, but the INVITE transaction could not - * be found (possibly because the branch parameter in Via in ACK msg - * is different than the branch in original INVITE). This happens with - * SER! - */ - pjsip_event event; - - PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata); - endpt_do_event( endpt, &event ); - } - - /* - * If a new request message has just been receieved, but no modules - * seem to be able to handle the request message, then terminate the - * transaction. - * - * Ideally for cases like "unsupported method", we should be able to - * answer the request statelessly. But we can not do that since the - * endpoint shoule be able to be used as both user agent and proxy stack, - * and a proxy stack should be able to handle arbitrary methods. - */ - if (a_new_transaction_just_been_created && tsx->status_code < 100) { - /* Certainly no modules has sent any response message. - * Check that any modules has attached a module data. - */ - int i; - for (i=0; i<PJSIP_MAX_MODULE; ++i) { - if (tsx->module_data[i] != NULL) { - break; - } - } - if (i == PJSIP_MAX_MODULE) { - /* No modules have attached itself to the transaction. - * Terminate the transaction with 501/Not Implemented. - */ + }
+
+ /* Initialize transaction as UAS. */
+ pjsip_tsx_init_uas( tsx, rdata );
+
+ /* Register transaction, mutex is locked there. */
+ pjsip_endpt_register_tsx( endpt, tsx );
+
+ a_new_transaction_just_been_created = PJ_TRUE;
+ }
+ }
+
+ /* If transaction is found (or newly created), pass the message.
+ * Otherwise if it's an ACK request, pass directly to TU.
+ */
+ if (tsx && tsx->state != PJSIP_TSX_STATE_TERMINATED) {
+ /* Dispatch message to transaction. */
+ pjsip_tsx_on_rx_msg( tsx, rdata );
+
+ } else if (rdata->msg->line.req.method.id == PJSIP_ACK_METHOD) {
+ /*
+ * This is an ACK message, but the INVITE transaction could not
+ * be found (possibly because the branch parameter in Via in ACK msg
+ * is different than the branch in original INVITE). This happens with
+ * SER!
+ */
+ pjsip_event event;
+
+ PJSIP_EVENT_INIT_RX_ACK_MSG(event,rdata);
+ endpt_do_event( endpt, &event );
+ }
+
+ /*
+ * If a new request message has just been receieved, but no modules
+ * seem to be able to handle the request message, then terminate the
+ * transaction.
+ *
+ * Ideally for cases like "unsupported method", we should be able to
+ * answer the request statelessly. But we can not do that since the
+ * endpoint shoule be able to be used as both user agent and proxy stack,
+ * and a proxy stack should be able to handle arbitrary methods.
+ */
+ if (a_new_transaction_just_been_created && tsx->status_code < 100) {
+ /* Certainly no modules has sent any response message.
+ * Check that any modules has attached a module data.
+ */
+ int i;
+ for (i=0; i<PJSIP_MAX_MODULE; ++i) {
+ if (tsx->module_data[i] != NULL) {
+ break;
+ }
+ }
+ if (i == PJSIP_MAX_MODULE) {
+ /* No modules have attached itself to the transaction.
+ * Terminate the transaction with 501/Not Implemented.
+ */
pjsip_tx_data *tdata;
- pj_status_t status; - - if (tsx->method.id == PJSIP_OPTIONS_METHOD) { + pj_status_t status;
+
+ if (tsx->method.id == PJSIP_OPTIONS_METHOD) {
status = pjsip_endpt_create_response(endpt, rdata, 200,
- &tdata); - } else { - status = pjsip_endpt_create_response(endpt, rdata, + &tdata);
+ } else {
+ status = pjsip_endpt_create_response(endpt, rdata,
PJSIP_SC_METHOD_NOT_ALLOWED,
- &tdata); + &tdata);
}
if (status != PJ_SUCCESS) {
@@ -843,202 +865,202 @@ static void endpt_transport_callback( pjsip_endpoint *endpt, "Unable to create response"));
return;
}
- - if (endpt->allow_hdr) { - pjsip_msg_add_hdr( tdata->msg, - pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr)); - } - pjsip_tsx_on_tx_msg( tsx, tdata ); - - } else { - /* - * If a module has registered itself in the transaction but it - * hasn't responded the request, chances are the module wouldn't - * respond to the request at all. We terminate the request here - * with 500/Internal Server Error, to be safe. - */ +
+ if (endpt->allow_hdr) {
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, endpt->allow_hdr));
+ }
+ pjsip_tsx_on_tx_msg( tsx, tdata );
+
+ } else {
+ /*
+ * If a module has registered itself in the transaction but it
+ * hasn't responded the request, chances are the module wouldn't
+ * respond to the request at all. We terminate the request here
+ * with 500/Internal Server Error, to be safe.
+ */
pjsip_tx_data *tdata;
pj_status_t status;
- +
status = pjsip_endpt_create_response(endpt, rdata, 500, &tdata);
if (status != PJ_SUCCESS) {
PJSIP_ENDPT_LOG_ERROR((endpt, THIS_FILE, status,
"Unable to create response"));
return;
}
- - pjsip_tsx_on_tx_msg(tsx, tdata); - } - } -} - -/* - * Create transmit data buffer. - */ +
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ }
+ }
+}
+
+/*
+ * Create transmit data buffer.
+ */
PJ_DEF(pj_status_t) pjsip_endpt_create_tdata( pjsip_endpoint *endpt,
pjsip_tx_data **p_tdata)
-{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()")); - return pjsip_tx_data_create(endpt->transport_mgr, p_tdata); -} - -/* - * Resolve - */ -PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt, - pj_pool_t *pool, - pjsip_host_port *target, - void *token, - pjsip_resolver_callback *cb) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()")); - pjsip_resolve( endpt->resolver, pool, target, token, cb); -} - -/* - * Find/create transport. - */ -PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt, - pj_pool_t *pool, - pjsip_transport_type_e type, - const pj_sockaddr_in *remote, - void *token, - pjsip_transport_completion_callback *cb) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_get_transport()")); - pjsip_transport_get( endpt->transport_mgr, pool, type, - remote, token, cb); -} - - -PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt, - pjsip_transport_type_e type, - pj_sockaddr_in *addr, - const pj_sockaddr_in *addr_name) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_listener()")); - return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name ); -} - -PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt, - pj_sock_t sock, - const pj_sockaddr_in *addr_name) -{ - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_udp_listener()")); - return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name ); -} - -PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail ) -{ -#if PJ_LOG_MAX_LEVEL >= 3 - unsigned count; - pj_hash_iterator_t itr_val; - pj_hash_iterator_t *itr; - - PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()")); - - /* Lock mutex. */ - pj_mutex_lock(endpt->mutex); - - PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt)); - - /* Dumping pool factory. */ - (*endpt->pf->dump_status)(endpt->pf, detail); - - /* Pool health. */ - PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u", - pj_pool_get_capacity(endpt->pool), - pj_pool_get_used_size(endpt->pool))); - - /* Transaction tables. */ - count = pj_hash_count(endpt->tsx_table); - PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count)); - - if (count && detail) { - pj_hash_iterator_t it_val; - pj_hash_iterator_t *it; - pj_time_val now; - - PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:")); - - pj_gettimeofday(&now); - it = pj_hash_first(endpt->tsx_table, &it_val); - - while (it != NULL) { - int timeout_diff; - - /* Get the transaction. No need to lock transaction's mutex - * since we already hold endpoint mutex, so that no transactions - * will be deleted. - */ - pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it); - - const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC"); - - if (tsx->timeout_timer._timer_id != -1) { - if (tsx->timeout_timer._timer_value.sec > now.sec) { - timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec; - } else { - timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec; - timeout_diff = 0 - timeout_diff; - } - } else { - timeout_diff = -1; - } - - PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds", - tsx->obj_name, role, - tsx->method.name.slen, tsx->method.name.ptr, - tsx->cseq, - pjsip_tsx_state_str(tsx->state), - timeout_diff)); - - it = pj_hash_next(endpt->tsx_table, it); - } - } - - /* Transports. - * Note: transport is not properly locked in this function. - * See pjsip_transport_first, pjsip_transport_next. - */ - itr = pjsip_transport_first( endpt->transport_mgr, &itr_val ); - if (itr) { - PJ_LOG(3, (THIS_FILE, " Dumping transports:")); - - do { - char src_addr[128], dst_addr[128]; - int src_port, dst_port; - const pj_sockaddr_in *addr; - pjsip_transport_t *t; - - t = pjsip_transport_this(endpt->transport_mgr, itr); - addr = pjsip_transport_get_local_addr(t); - pj_native_strcpy(src_addr, pj_inet_ntoa(addr->sin_addr)); - src_port = pj_ntohs(addr->sin_port); - - addr = pjsip_transport_get_remote_addr(t); - pj_native_strcpy(dst_addr, pj_inet_ntoa(addr->sin_addr)); - dst_port = pj_ntohs(addr->sin_port); - - PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)", - pjsip_transport_get_type_name(t), - pjsip_transport_get_obj_name(t), - src_addr, src_port, - dst_addr, dst_port, - pjsip_transport_get_ref_cnt(t))); - - itr = pjsip_transport_next(endpt->transport_mgr, itr); - } while (itr); - } - - /* Timer. */ - PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries", - pj_timer_heap_count(endpt->timer_heap))); - - /* Unlock mutex. */ - pj_mutex_unlock(endpt->mutex); -#else - PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled.")); -#endif -} - +{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_tdata()"));
+ return pjsip_tx_data_create(endpt->transport_mgr, p_tdata);
+}
+
+/*
+ * Resolve
+ */
+PJ_DEF(void) pjsip_endpt_resolve( pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ pjsip_host_port *target,
+ void *token,
+ pjsip_resolver_callback *cb)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_resolve()"));
+ pjsip_resolve( endpt->resolver, pool, target, token, cb);
+}
+
+/*
+ * Find/create transport.
+ */
+PJ_DEF(void) pjsip_endpt_get_transport( pjsip_endpoint *endpt,
+ pj_pool_t *pool,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ void *token,
+ pjsip_transport_completion_callback *cb)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_get_transport()"));
+ pjsip_transport_get( endpt->transport_mgr, pool, type,
+ remote, token, cb);
+}
+
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_listener( pjsip_endpoint *endpt,
+ pjsip_transport_type_e type,
+ pj_sockaddr_in *addr,
+ const pj_sockaddr_in *addr_name)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_listener()"));
+ return pjsip_create_listener( endpt->transport_mgr, type, addr, addr_name );
+}
+
+PJ_DEF(pj_status_t) pjsip_endpt_create_udp_listener( pjsip_endpoint *endpt,
+ pj_sock_t sock,
+ const pj_sockaddr_in *addr_name)
+{
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_create_udp_listener()"));
+ return pjsip_create_udp_listener( endpt->transport_mgr, sock, addr_name );
+}
+
+PJ_DEF(void) pjsip_endpt_dump( pjsip_endpoint *endpt, pj_bool_t detail )
+{
+#if PJ_LOG_MAX_LEVEL >= 3
+ unsigned count;
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+ PJ_LOG(5, (THIS_FILE, "pjsip_endpt_dump()"));
+
+ /* Lock mutex. */
+ pj_mutex_lock(endpt->mutex);
+
+ PJ_LOG(3, (THIS_FILE, "Dumping endpoint %p:", endpt));
+
+ /* Dumping pool factory. */
+ (*endpt->pf->dump_status)(endpt->pf, detail);
+
+ /* Pool health. */
+ PJ_LOG(3, (THIS_FILE," Endpoint pool capacity=%u, used_size=%u",
+ pj_pool_get_capacity(endpt->pool),
+ pj_pool_get_used_size(endpt->pool)));
+
+ /* Transaction tables. */
+ count = pj_hash_count(endpt->tsx_table);
+ PJ_LOG(3, (THIS_FILE, " Number of transactions: %u", count));
+
+ if (count && detail) {
+ pj_hash_iterator_t it_val;
+ pj_hash_iterator_t *it;
+ pj_time_val now;
+
+ PJ_LOG(3, (THIS_FILE, " Dumping transaction tables:"));
+
+ pj_gettimeofday(&now);
+ it = pj_hash_first(endpt->tsx_table, &it_val);
+
+ while (it != NULL) {
+ int timeout_diff;
+
+ /* Get the transaction. No need to lock transaction's mutex
+ * since we already hold endpoint mutex, so that no transactions
+ * will be deleted.
+ */
+ pjsip_transaction *tsx = pj_hash_this(endpt->tsx_table, it);
+
+ const char *role = (tsx->role == PJSIP_ROLE_UAS ? "UAS" : "UAC");
+
+ if (tsx->timeout_timer._timer_id != -1) {
+ if (tsx->timeout_timer._timer_value.sec > now.sec) {
+ timeout_diff = tsx->timeout_timer._timer_value.sec - now.sec;
+ } else {
+ timeout_diff = now.sec - tsx->timeout_timer._timer_value.sec;
+ timeout_diff = 0 - timeout_diff;
+ }
+ } else {
+ timeout_diff = -1;
+ }
+
+ PJ_LOG(3, (THIS_FILE, " %s %s %10.*s %.9u %s t=%ds",
+ tsx->obj_name, role,
+ tsx->method.name.slen, tsx->method.name.ptr,
+ tsx->cseq,
+ pjsip_tsx_state_str(tsx->state),
+ timeout_diff));
+
+ it = pj_hash_next(endpt->tsx_table, it);
+ }
+ }
+
+ /* Transports.
+ * Note: transport is not properly locked in this function.
+ * See pjsip_transport_first, pjsip_transport_next.
+ */
+ itr = pjsip_transport_first( endpt->transport_mgr, &itr_val );
+ if (itr) {
+ PJ_LOG(3, (THIS_FILE, " Dumping transports:"));
+
+ do {
+ char src_addr[128], dst_addr[128];
+ int src_port, dst_port;
+ const pj_sockaddr_in *addr;
+ pjsip_transport_t *t;
+
+ t = pjsip_transport_this(endpt->transport_mgr, itr);
+ addr = pjsip_transport_get_local_addr(t);
+ pj_native_strcpy(src_addr, pj_inet_ntoa(addr->sin_addr));
+ src_port = pj_ntohs(addr->sin_port);
+
+ addr = pjsip_transport_get_remote_addr(t);
+ pj_native_strcpy(dst_addr, pj_inet_ntoa(addr->sin_addr));
+ dst_port = pj_ntohs(addr->sin_port);
+
+ PJ_LOG(3, (THIS_FILE, " %s %s %s:%d --> %s:%d (refcnt=%d)",
+ pjsip_transport_get_type_name(t),
+ pjsip_transport_get_obj_name(t),
+ src_addr, src_port,
+ dst_addr, dst_port,
+ pjsip_transport_get_ref_cnt(t)));
+
+ itr = pjsip_transport_next(endpt->transport_mgr, itr);
+ } while (itr);
+ }
+
+ /* Timer. */
+ PJ_LOG(3,(THIS_FILE, " Timer heap has %u entries",
+ pj_timer_heap_count(endpt->timer_heap)));
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(endpt->mutex);
+#else
+ PJ_LOG(3,(THIS_FILE, "pjsip_end_dump: can't dump because it's disabled."));
+#endif
+}
+
diff --git a/pjsip/src/pjsip/sip_misc.c b/pjsip/src/pjsip/sip_misc.c index a9562397..ce1ceb89 100644 --- a/pjsip/src/pjsip/sip_misc.c +++ b/pjsip/src/pjsip/sip_misc.c @@ -1,166 +1,188 @@ -/* $Id$ - */ -#include <pjsip/sip_misc.h> -#include <pjsip/sip_transport.h> -#include <pjsip/sip_msg.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_event.h> -#include <pjsip/sip_transaction.h> -#include <pjsip/sip_module.h> -#include <pj/log.h> -#include <pj/string.h> -#include <pj/guid.h> -#include <pj/pool.h> -#include <pj/except.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_module.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/guid.h>
+#include <pj/pool.h>
+#include <pj/except.h>
#include <pj/rand.h>
#include <pj/assert.h>
#include <pj/errno.h>
- -#define THIS_FILE "endpoint" - -static const char *event_str[] = -{ - "UNIDENTIFIED", - "TIMER", - "TX_MSG", - "RX_MSG", - "TRANSPORT_ERROR", - "TSX_STATE", - "RX_2XX_RESPONSE", - "RX_ACK", - "DISCARD_MSG", - "USER", - "BEFORE_TX", -}; - -static pj_str_t str_TEXT = { "text", 4}, - str_PLAIN = { "plain", 5 }; -static int aux_mod_id; - -struct aux_tsx_data -{ - void *token; - void (*cb)(void*,pjsip_event*); -}; - -static pj_status_t aux_tsx_init( pjsip_endpoint *endpt, - struct pjsip_module *mod, pj_uint32_t id ) -{ - PJ_UNUSED_ARG(endpt); - PJ_UNUSED_ARG(mod); - - aux_mod_id = id; - return 0; -} - -static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event ) -{ - pjsip_transaction *tsx; - struct aux_tsx_data *tsx_data; - - PJ_UNUSED_ARG(mod); - - if (event->type != PJSIP_EVENT_TSX_STATE) +
+#define THIS_FILE "endpoint"
+
+static const char *event_str[] =
+{
+ "UNIDENTIFIED",
+ "TIMER",
+ "TX_MSG",
+ "RX_MSG",
+ "TRANSPORT_ERROR",
+ "TSX_STATE",
+ "RX_2XX_RESPONSE",
+ "RX_ACK",
+ "DISCARD_MSG",
+ "USER",
+ "BEFORE_TX",
+};
+
+static pj_str_t str_TEXT = { "text", 4},
+ str_PLAIN = { "plain", 5 };
+static int aux_mod_id;
+
+struct aux_tsx_data
+{
+ void *token;
+ void (*cb)(void*,pjsip_event*);
+};
+
+static pj_status_t aux_tsx_init( pjsip_endpoint *endpt,
+ struct pjsip_module *mod, pj_uint32_t id )
+{
+ PJ_UNUSED_ARG(endpt);
+ PJ_UNUSED_ARG(mod);
+
+ aux_mod_id = id;
+ return 0;
+}
+
+static void aux_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
+{
+ pjsip_transaction *tsx;
+ struct aux_tsx_data *tsx_data;
+
+ PJ_UNUSED_ARG(mod);
+
+ if (event->type != PJSIP_EVENT_TSX_STATE)
return;
pj_assert(event->body.tsx_state.tsx != NULL);
- tsx = event->body.tsx_state.tsx; - if (tsx == NULL) - return; - if (tsx->module_data[aux_mod_id] == NULL) - return; - if (tsx->status_code < 200) - return; - - /* Call the callback, if any, and prevent the callback to be called again - * by clearing the transaction's module_data. - */ - tsx_data = tsx->module_data[aux_mod_id]; - tsx->module_data[aux_mod_id] = NULL; - - if (tsx_data->cb) { - (*tsx_data->cb)(tsx_data->token, event); - } -} - -pjsip_module aux_tsx_module = -{ - { "Aux-Tsx", 7}, /* Name. */ - 0, /* Flag */ - 128, /* Priority */ - NULL, /* Arbitrary data. */ - 0, /* Number of methods supported (none). */ - { 0 }, /* Array of methods (none) */ - &aux_tsx_init, /* init_module() */ - NULL, /* start_module() */ - NULL, /* deinit_module() */ - &aux_tsx_handler, /* tsx_handler() */ -}; - -PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, - pjsip_tx_data *tdata, - int timeout, - void *token, - void (*cb)(void*,pjsip_event*)) -{ - pjsip_transaction *tsx; + tsx = event->body.tsx_state.tsx;
+ if (tsx == NULL)
+ return;
+ if (tsx->module_data[aux_mod_id] == NULL)
+ return;
+ if (tsx->status_code < 200)
+ return;
+
+ /* Call the callback, if any, and prevent the callback to be called again
+ * by clearing the transaction's module_data.
+ */
+ tsx_data = tsx->module_data[aux_mod_id];
+ tsx->module_data[aux_mod_id] = NULL;
+
+ if (tsx_data->cb) {
+ (*tsx_data->cb)(tsx_data->token, event);
+ }
+}
+
+pjsip_module aux_tsx_module =
+{
+ { "Aux-Tsx", 7}, /* Name. */
+ 0, /* Flag */
+ 128, /* Priority */
+ NULL, /* Arbitrary data. */
+ 0, /* Number of methods supported (none). */
+ { 0 }, /* Array of methods (none) */
+ &aux_tsx_init, /* init_module() */
+ NULL, /* start_module() */
+ NULL, /* deinit_module() */
+ &aux_tsx_handler, /* tsx_handler() */
+};
+
+PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ int timeout,
+ void *token,
+ void (*cb)(void*,pjsip_event*))
+{
+ pjsip_transaction *tsx;
struct aux_tsx_data *tsx_data;
- pj_status_t status; - - status = pjsip_endpt_create_tsx(endpt, &tsx); - if (!tsx) { - pjsip_tx_data_dec_ref(tdata); - return -1; - } - - tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data)); - tsx_data->token = token; - tsx_data->cb = cb; - tsx->module_data[aux_mod_id] = tsx_data; - - if (pjsip_tsx_init_uac(tsx, tdata) != 0) { - pjsip_endpt_destroy_tsx(endpt, tsx); - pjsip_tx_data_dec_ref(tdata); - return -1; - } - - pjsip_endpt_register_tsx(endpt, tsx); - pjsip_tx_data_invalidate_msg(tdata); - pjsip_tsx_on_tx_msg(tsx, tdata); - pjsip_tx_data_dec_ref(tdata); - return 0; -} - -/* - * Initialize transmit data (msg) with the headers and optional body. - * This will just put the headers in the message as it is. Be carefull - * when calling this function because once a header is put in a message, - * it CAN NOT be put in other message until the first message is deleted, - * because the way the header is put in the list. - * That's why the session will shallow_clone it's headers before calling - * this function. - */ + pj_status_t status;
+
+ status = pjsip_endpt_create_tsx(endpt, &tsx);
+ if (!tsx) {
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data));
+ tsx_data->token = token;
+ tsx_data->cb = cb;
+ tsx->module_data[aux_mod_id] = tsx_data;
+
+ if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
+ pjsip_endpt_destroy_tsx(endpt, tsx);
+ pjsip_tx_data_dec_ref(tdata);
+ return -1;
+ }
+
+ pjsip_endpt_register_tsx(endpt, tsx);
+ pjsip_tx_data_invalidate_msg(tdata);
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ pjsip_tx_data_dec_ref(tdata);
+ return 0;
+}
+
+/*
+ * Initialize transmit data (msg) with the headers and optional body.
+ * This will just put the headers in the message as it is. Be carefull
+ * when calling this function because once a header is put in a message,
+ * it CAN NOT be put in other message until the first message is deleted,
+ * because the way the header is put in the list.
+ * That's why the session will shallow_clone it's headers before calling
+ * this function.
+ */
static void init_request_throw( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata, - pjsip_method *method, - pjsip_uri *param_target, - pjsip_from_hdr *param_from, - pjsip_to_hdr *param_to, - pjsip_contact_hdr *param_contact, - pjsip_cid_hdr *param_call_id, - pjsip_cseq_hdr *param_cseq, - const pj_str_t *param_text) -{ - pjsip_msg *msg; - pjsip_msg_body *body; + pjsip_tx_data *tdata,
+ pjsip_method *method,
+ pjsip_uri *param_target,
+ pjsip_from_hdr *param_from,
+ pjsip_to_hdr *param_to,
+ pjsip_contact_hdr *param_contact,
+ pjsip_cid_hdr *param_call_id,
+ pjsip_cseq_hdr *param_cseq,
+ const pj_str_t *param_text)
+{
+ pjsip_msg *msg;
+ pjsip_msg_body *body;
const pjsip_hdr *endpt_hdr;
- - /* Create the message. */ - msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); - - /* Init request URI. */ - pj_memcpy(&msg->line.req.method, method, sizeof(*method)); - msg->line.req.uri = param_target; +
+ /* Create the message. */
+ msg = tdata->msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+
+ /* Init request URI. */
+ pj_memcpy(&msg->line.req.method, method, sizeof(*method));
+ msg->line.req.uri = param_target;
/* Add additional request headers from endpoint. */
endpt_hdr = pjsip_endpt_get_request_headers(endpt)->next;
@@ -170,539 +192,539 @@ static void init_request_throw( pjsip_endpoint *endpt, endpt_hdr = endpt_hdr->next;
}
- /* Add From header. */ - if (param_from->tag.slen == 0) - pj_create_unique_string(tdata->pool, ¶m_from->tag); - pjsip_msg_add_hdr(msg, (void*)param_from); - - /* Add To header. */ - pjsip_msg_add_hdr(msg, (void*)param_to); - - /* Add Contact header. */ - if (param_contact) { - pjsip_msg_add_hdr(msg, (void*)param_contact); - } - - /* Add Call-ID header. */ - pjsip_msg_add_hdr(msg, (void*)param_call_id); - - /* Add CSeq header. */ - pjsip_msg_add_hdr(msg, (void*)param_cseq); - - /* Create message body. */ - if (param_text) { - body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body)); - body->content_type.type = str_TEXT; - body->content_type.subtype = str_PLAIN; - body->data = pj_pool_alloc(tdata->pool, param_text->slen ); - pj_memcpy(body->data, param_text->ptr, param_text->slen); - body->len = param_text->slen; - body->print_body = &pjsip_print_text_body; - msg->body = body; - } -} - -/* - * Create arbitrary request. - */ -PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt, - const pjsip_method *method, - const pj_str_t *param_target, - const pj_str_t *param_from, - const pj_str_t *param_to, - const pj_str_t *param_contact, - const pj_str_t *param_call_id, - int param_cseq, + /* Add From header. */
+ if (param_from->tag.slen == 0)
+ pj_create_unique_string(tdata->pool, ¶m_from->tag);
+ pjsip_msg_add_hdr(msg, (void*)param_from);
+
+ /* Add To header. */
+ pjsip_msg_add_hdr(msg, (void*)param_to);
+
+ /* Add Contact header. */
+ if (param_contact) {
+ pjsip_msg_add_hdr(msg, (void*)param_contact);
+ }
+
+ /* Add Call-ID header. */
+ pjsip_msg_add_hdr(msg, (void*)param_call_id);
+
+ /* Add CSeq header. */
+ pjsip_msg_add_hdr(msg, (void*)param_cseq);
+
+ /* Create message body. */
+ if (param_text) {
+ body = pj_pool_calloc(tdata->pool, 1, sizeof(pjsip_msg_body));
+ body->content_type.type = str_TEXT;
+ body->content_type.subtype = str_PLAIN;
+ body->data = pj_pool_alloc(tdata->pool, param_text->slen );
+ pj_memcpy(body->data, param_text->ptr, param_text->slen);
+ body->len = param_text->slen;
+ body->print_body = &pjsip_print_text_body;
+ msg->body = body;
+ }
+}
+
+/*
+ * Create arbitrary request.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_request( pjsip_endpoint *endpt,
+ const pjsip_method *method,
+ const pj_str_t *param_target,
+ const pj_str_t *param_from,
+ const pj_str_t *param_to,
+ const pj_str_t *param_contact,
+ const pj_str_t *param_call_id,
+ int param_cseq,
const pj_str_t *param_text,
- pjsip_tx_data **p_tdata) -{ - pjsip_uri *target; - pjsip_tx_data *tdata; - pjsip_from_hdr *from; - pjsip_to_hdr *to; - pjsip_contact_hdr *contact; - pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */ - pjsip_cid_hdr *call_id; + pjsip_tx_data **p_tdata)
+{
+ pjsip_uri *target;
+ pjsip_tx_data *tdata;
+ pjsip_from_hdr *from;
+ pjsip_to_hdr *to;
+ pjsip_contact_hdr *contact;
+ pjsip_cseq_hdr *cseq = NULL; /* = NULL, warning in VC6 */
+ pjsip_cid_hdr *call_id;
pj_str_t tmp;
- pj_status_t status; - PJ_USE_EXCEPTION; - - PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request()")); - - status = pjsip_endpt_create_tdata(endpt, &tdata); - if (status != PJ_SUCCESS) - return status; - - /* Init reference counter to 1. */ - pjsip_tx_data_add_ref(tdata); - - PJ_TRY { - /* Request target. */ - pj_strdup_with_null(tdata->pool, &tmp, param_target); - target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0); - if (target == NULL) { - PJ_LOG(4,(THIS_FILE, "Error creating request: invalid target %s", - tmp.ptr)); - goto on_error; - } - - /* From */ - from = pjsip_from_hdr_create(tdata->pool); - pj_strdup_with_null(tdata->pool, &tmp, param_from); - from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (from->uri == NULL) { - PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'From' URI '%s'", - tmp.ptr)); - goto on_error; - } - pj_create_unique_string(tdata->pool, &from->tag); - - /* To */ - to = pjsip_to_hdr_create(tdata->pool); - pj_strdup_with_null(tdata->pool, &tmp, param_to); - to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (to->uri == NULL) { - PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'To' URI '%s'", - tmp.ptr)); - goto on_error; - } - - /* Contact. */ - if (param_contact) { - contact = pjsip_contact_hdr_create(tdata->pool); - pj_strdup_with_null(tdata->pool, &tmp, param_contact); - contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, - PJSIP_PARSE_URI_AS_NAMEADDR); - if (contact->uri == NULL) { - PJ_LOG(4,(THIS_FILE, - "Error creating request: invalid 'Contact' URI '%s'", - tmp.ptr)); - goto on_error; - } - } else { - contact = NULL; - } - - /* Call-ID */ - call_id = pjsip_cid_hdr_create(tdata->pool); - if (param_call_id != NULL && param_call_id->slen) - pj_strdup(tdata->pool, &call_id->id, param_call_id); - else - pj_create_unique_string(tdata->pool, &call_id->id); - - /* CSeq */ - cseq = pjsip_cseq_hdr_create(tdata->pool); - if (param_cseq >= 0) - cseq->cseq = param_cseq; - else - cseq->cseq = pj_rand() & 0xFFFF; - - /* Method */ - pjsip_method_copy(tdata->pool, &cseq->method, method); - - /* Create the request. */ + pj_status_t status;
+ PJ_USE_EXCEPTION;
+
+ PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request()"));
+
+ status = pjsip_endpt_create_tdata(endpt, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Init reference counter to 1. */
+ pjsip_tx_data_add_ref(tdata);
+
+ PJ_TRY {
+ /* Request target. */
+ pj_strdup_with_null(tdata->pool, &tmp, param_target);
+ target = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen, 0);
+ if (target == NULL) {
+ PJ_LOG(4,(THIS_FILE, "Error creating request: invalid target %s",
+ tmp.ptr));
+ goto on_error;
+ }
+
+ /* From */
+ from = pjsip_from_hdr_create(tdata->pool);
+ pj_strdup_with_null(tdata->pool, &tmp, param_from);
+ from->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (from->uri == NULL) {
+ PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'From' URI '%s'",
+ tmp.ptr));
+ goto on_error;
+ }
+ pj_create_unique_string(tdata->pool, &from->tag);
+
+ /* To */
+ to = pjsip_to_hdr_create(tdata->pool);
+ pj_strdup_with_null(tdata->pool, &tmp, param_to);
+ to->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (to->uri == NULL) {
+ PJ_LOG(4,(THIS_FILE, "Error creating request: invalid 'To' URI '%s'",
+ tmp.ptr));
+ goto on_error;
+ }
+
+ /* Contact. */
+ if (param_contact) {
+ contact = pjsip_contact_hdr_create(tdata->pool);
+ pj_strdup_with_null(tdata->pool, &tmp, param_contact);
+ contact->uri = pjsip_parse_uri( tdata->pool, tmp.ptr, tmp.slen,
+ PJSIP_PARSE_URI_AS_NAMEADDR);
+ if (contact->uri == NULL) {
+ PJ_LOG(4,(THIS_FILE,
+ "Error creating request: invalid 'Contact' URI '%s'",
+ tmp.ptr));
+ goto on_error;
+ }
+ } else {
+ contact = NULL;
+ }
+
+ /* Call-ID */
+ call_id = pjsip_cid_hdr_create(tdata->pool);
+ if (param_call_id != NULL && param_call_id->slen)
+ pj_strdup(tdata->pool, &call_id->id, param_call_id);
+ else
+ pj_create_unique_string(tdata->pool, &call_id->id);
+
+ /* CSeq */
+ cseq = pjsip_cseq_hdr_create(tdata->pool);
+ if (param_cseq >= 0)
+ cseq->cseq = param_cseq;
+ else
+ cseq->cseq = pj_rand() & 0xFFFF;
+
+ /* Method */
+ pjsip_method_copy(tdata->pool, &cseq->method, method);
+
+ /* Create the request. */
init_request_throw( endpt, tdata, &cseq->method, target, from, to,
- contact, call_id, cseq, param_text); - } - PJ_DEFAULT { - status = PJ_ENOMEM; - goto on_error; - } - PJ_END - - PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.", - tdata->obj_name, - cseq->cseq, - cseq->method.name.slen, - cseq->method.name.ptr)); -
- *p_tdata = tdata; - return PJ_SUCCESS; - -on_error: - pjsip_tx_data_dec_ref(tdata); - return status; -} - -PJ_DEF(pj_status_t) -pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt, - const pjsip_method *method, - const pjsip_uri *param_target, - const pjsip_from_hdr *param_from, - const pjsip_to_hdr *param_to, - const pjsip_contact_hdr *param_contact, - const pjsip_cid_hdr *param_call_id, - int param_cseq, + contact, call_id, cseq, param_text);
+ }
+ PJ_DEFAULT {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ PJ_END
+
+ PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
+ tdata->obj_name,
+ cseq->cseq,
+ cseq->method.name.slen,
+ cseq->method.name.ptr));
+
+ *p_tdata = tdata;
+ return PJ_SUCCESS;
+
+on_error:
+ pjsip_tx_data_dec_ref(tdata);
+ return status;
+}
+
+PJ_DEF(pj_status_t)
+pjsip_endpt_create_request_from_hdr( pjsip_endpoint *endpt,
+ const pjsip_method *method,
+ const pjsip_uri *param_target,
+ const pjsip_from_hdr *param_from,
+ const pjsip_to_hdr *param_to,
+ const pjsip_contact_hdr *param_contact,
+ const pjsip_cid_hdr *param_call_id,
+ int param_cseq,
const pj_str_t *param_text,
- pjsip_tx_data **p_tdata) -{ - pjsip_uri *target; - pjsip_tx_data *tdata; - pjsip_from_hdr *from; - pjsip_to_hdr *to; - pjsip_contact_hdr *contact; - pjsip_cid_hdr *call_id; + pjsip_tx_data **p_tdata)
+{
+ pjsip_uri *target;
+ pjsip_tx_data *tdata;
+ pjsip_from_hdr *from;
+ pjsip_to_hdr *to;
+ pjsip_contact_hdr *contact;
+ pjsip_cid_hdr *call_id;
pjsip_cseq_hdr *cseq = NULL; /* The NULL because warning in VC6 */
- pj_status_t status; - PJ_USE_EXCEPTION; - - PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()")); - - status = pjsip_endpt_create_tdata(endpt, &tdata); - if (status != PJ_SUCCESS) - return status; - - pjsip_tx_data_add_ref(tdata); - - PJ_TRY { - target = pjsip_uri_clone(tdata->pool, param_target); - from = pjsip_hdr_shallow_clone(tdata->pool, param_from); - pjsip_fromto_set_from(from); - to = pjsip_hdr_shallow_clone(tdata->pool, param_to); - pjsip_fromto_set_to(to); - if (param_contact) - contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact); - else - contact = NULL; - call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id); - cseq = pjsip_cseq_hdr_create(tdata->pool); - if (param_cseq >= 0) - cseq->cseq = param_cseq; - else - cseq->cseq = pj_rand() % 0xFFFF; - pjsip_method_copy(tdata->pool, &cseq->method, method); - + pj_status_t status;
+ PJ_USE_EXCEPTION;
+
+ PJ_LOG(5,(THIS_FILE, "Entering pjsip_endpt_create_request_from_hdr()"));
+
+ status = pjsip_endpt_create_tdata(endpt, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ pjsip_tx_data_add_ref(tdata);
+
+ PJ_TRY {
+ target = pjsip_uri_clone(tdata->pool, param_target);
+ from = pjsip_hdr_shallow_clone(tdata->pool, param_from);
+ pjsip_fromto_set_from(from);
+ to = pjsip_hdr_shallow_clone(tdata->pool, param_to);
+ pjsip_fromto_set_to(to);
+ if (param_contact)
+ contact = pjsip_hdr_shallow_clone(tdata->pool, param_contact);
+ else
+ contact = NULL;
+ call_id = pjsip_hdr_shallow_clone(tdata->pool, param_call_id);
+ cseq = pjsip_cseq_hdr_create(tdata->pool);
+ if (param_cseq >= 0)
+ cseq->cseq = param_cseq;
+ else
+ cseq->cseq = pj_rand() % 0xFFFF;
+ pjsip_method_copy(tdata->pool, &cseq->method, method);
+
init_request_throw(endpt, tdata, &cseq->method, target, from, to,
- contact, call_id, cseq, param_text); - } - PJ_DEFAULT { - status = PJ_ENOMEM; - goto on_error; - } - PJ_END; - - PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.", - tdata->obj_name, - cseq->cseq, - cseq->method.name.slen, + contact, call_id, cseq, param_text);
+ }
+ PJ_DEFAULT {
+ status = PJ_ENOMEM;
+ goto on_error;
+ }
+ PJ_END;
+
+ PJ_LOG(4,(THIS_FILE, "Request %s (%d %.*s) created.",
+ tdata->obj_name,
+ cseq->cseq,
+ cseq->method.name.slen,
cseq->method.name.ptr));
- *p_tdata = tdata; - return PJ_SUCCESS; - -on_error: - pjsip_tx_data_dec_ref(tdata); - return status; -} - -/* - * Construct a minimal response message for the received request. - */ -PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt, - const pjsip_rx_data *rdata, + *p_tdata = tdata;
+ return PJ_SUCCESS;
+
+on_error:
+ pjsip_tx_data_dec_ref(tdata);
+ return status;
+}
+
+/*
+ * Construct a minimal response message for the received request.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_response( pjsip_endpoint *endpt,
+ const pjsip_rx_data *rdata,
int code,
- pjsip_tx_data **p_tdata) -{ - pjsip_tx_data *tdata; - pjsip_msg *msg, *req_msg; - pjsip_hdr *hdr; - pjsip_via_hdr *via; + pjsip_tx_data **p_tdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_msg *msg, *req_msg;
+ pjsip_hdr *hdr;
+ pjsip_via_hdr *via;
pjsip_rr_hdr *rr;
- pj_status_t status; - - /* rdata must be a request message. */ - req_msg = rdata->msg; - pj_assert(req_msg->type == PJSIP_REQUEST_MSG); - - /* Log this action. */ - PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)", - rdata, code)); - - /* Create a new transmit buffer. */ - status = pjsip_endpt_create_tdata( endpt, &tdata); - if (status != PJ_SUCCESS) - return status; - - /* Create new response message. */ - tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG); - - /* Set status code and reason text. */ - msg->line.status.code = code; - msg->line.status.reason = *pjsip_get_status_text(code); - - /* Set TX data attributes. */ - tdata->rx_timestamp = rdata->timestamp; - - /* Copy all the via headers, in order. */ - via = rdata->via; - while (via) { - pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via)); - via = via->next; - if (via != (void*)&req_msg->hdr) - via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via); - else - break; - } - - /* Copy all Record-Route headers, in order. */ - rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL); - while (rr) { - pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr)); - rr = rr->next; - if (rr != (void*)&req_msg->hdr) - rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr); - else - break; - } - - /* Copy Call-ID header. */ - hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL); - pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr)); - - /* Copy From header. */ - hdr = pjsip_hdr_clone(tdata->pool, rdata->from); - pjsip_msg_add_hdr( msg, hdr); - - /* Copy To header. */ - hdr = pjsip_hdr_clone(tdata->pool, rdata->to); - pjsip_msg_add_hdr( msg, hdr); - - /* Copy CSeq header. */ - hdr = pjsip_hdr_clone(tdata->pool, rdata->cseq); - pjsip_msg_add_hdr( msg, hdr); - + pj_status_t status;
+
+ /* rdata must be a request message. */
+ req_msg = rdata->msg;
+ pj_assert(req_msg->type == PJSIP_REQUEST_MSG);
+
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_response(rdata=%p, code=%d)",
+ rdata, code));
+
+ /* Create a new transmit buffer. */
+ status = pjsip_endpt_create_tdata( endpt, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Create new response message. */
+ tdata->msg = msg = pjsip_msg_create(tdata->pool, PJSIP_RESPONSE_MSG);
+
+ /* Set status code and reason text. */
+ msg->line.status.code = code;
+ msg->line.status.reason = *pjsip_get_status_text(code);
+
+ /* Set TX data attributes. */
+ tdata->rx_timestamp = rdata->timestamp;
+
+ /* Copy all the via headers, in order. */
+ via = rdata->via;
+ while (via) {
+ pjsip_msg_add_hdr( msg, pjsip_hdr_clone(tdata->pool, via));
+ via = via->next;
+ if (via != (void*)&req_msg->hdr)
+ via = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, via);
+ else
+ break;
+ }
+
+ /* Copy all Record-Route headers, in order. */
+ rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, NULL);
+ while (rr) {
+ pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, rr));
+ rr = rr->next;
+ if (rr != (void*)&req_msg->hdr)
+ rr = pjsip_msg_find_hdr(req_msg, PJSIP_H_RECORD_ROUTE, rr);
+ else
+ break;
+ }
+
+ /* Copy Call-ID header. */
+ hdr = pjsip_msg_find_hdr( req_msg, PJSIP_H_CALL_ID, NULL);
+ pjsip_msg_add_hdr(msg, pjsip_hdr_clone(tdata->pool, hdr));
+
+ /* Copy From header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->from);
+ pjsip_msg_add_hdr( msg, hdr);
+
+ /* Copy To header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->to);
+ pjsip_msg_add_hdr( msg, hdr);
+
+ /* Copy CSeq header. */
+ hdr = pjsip_hdr_clone(tdata->pool, rdata->cseq);
+ pjsip_msg_add_hdr( msg, hdr);
+
/* All done. */
- *p_tdata = tdata; - return PJ_SUCCESS; -} - - -/* - * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of - * RFC3261). Note that the generation of ACK for 2xx response is different, - * and one must not use this function to generate such ACK. - */ -PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt, - pjsip_tx_data *tdata, - const pjsip_rx_data *rdata ) -{ - pjsip_msg *ack_msg, *invite_msg; - pjsip_to_hdr *to; - pjsip_from_hdr *from; - pjsip_cseq_hdr *cseq; - pjsip_hdr *hdr; - - /* Make compiler happy. */ - PJ_UNUSED_ARG(endpt); - - /* rdata must be a final response. */ - pj_assert(rdata->msg->type==PJSIP_RESPONSE_MSG && - rdata->msg->line.status.code >= 300); - - /* Log this action. */ - PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata)); - - /* Create new request message. */ - ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); - pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD ); - - /* The original INVITE message. */ - invite_msg = tdata->msg; - - /* Copy Request-Uri from the original INVITE. */ - ack_msg->line.req.uri = invite_msg->line.req.uri; - - /* Copy Call-ID from the original INVITE */ - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL); - pjsip_msg_add_hdr( ack_msg, hdr ); - - /* Copy From header from the original INVITE. */ - from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg, - PJSIP_H_FROM, NULL); - pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from ); - - /* Copy To header from the original INVITE. */ - to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg, - PJSIP_H_TO, NULL); - pj_strdup(tdata->pool, &to->tag, &rdata->to->tag); - pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to ); - - /* Must contain single Via, just as the original INVITE. */ - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL); - pjsip_msg_insert_first_hdr( ack_msg, hdr ); - - /* Must have the same CSeq value as the original INVITE, but method - * changed to ACK - */ - cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg, - PJSIP_H_CSEQ, NULL); - pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD ); - pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq ); - - /* If the original INVITE has Route headers, those header fields MUST - * appear in the ACK. - */ - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); - while (hdr != NULL) { - pjsip_msg_add_hdr( ack_msg, hdr ); - hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL); - } - - /* Set the message in the "tdata" to point to the ACK message. */ - tdata->msg = ack_msg; - - /* Reset transmit packet buffer, to force 're-printing' of message. */ - tdata->buf.cur = tdata->buf.start; - - /* We're done. - * "tdata" parameter now contains the ACK message. - */ -} - - -/* - * Construct CANCEL request for the previously sent request, according to - * chapter 9.1 of RFC3261. - */ -PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt, + *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Construct ACK for 3xx-6xx final response (according to chapter 17.1.1 of
+ * RFC3261). Note that the generation of ACK for 2xx response is different,
+ * and one must not use this function to generate such ACK.
+ */
+PJ_DEF(void) pjsip_endpt_create_ack(pjsip_endpoint *endpt,
+ pjsip_tx_data *tdata,
+ const pjsip_rx_data *rdata )
+{
+ pjsip_msg *ack_msg, *invite_msg;
+ pjsip_to_hdr *to;
+ pjsip_from_hdr *from;
+ pjsip_cseq_hdr *cseq;
+ pjsip_hdr *hdr;
+
+ /* Make compiler happy. */
+ PJ_UNUSED_ARG(endpt);
+
+ /* rdata must be a final response. */
+ pj_assert(rdata->msg->type==PJSIP_RESPONSE_MSG &&
+ rdata->msg->line.status.code >= 300);
+
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_ack(rdata=%p)", rdata));
+
+ /* Create new request message. */
+ ack_msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG);
+ pjsip_method_set( &ack_msg->line.req.method, PJSIP_ACK_METHOD );
+
+ /* The original INVITE message. */
+ invite_msg = tdata->msg;
+
+ /* Copy Request-Uri from the original INVITE. */
+ ack_msg->line.req.uri = invite_msg->line.req.uri;
+
+ /* Copy Call-ID from the original INVITE */
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_CALL_ID, NULL);
+ pjsip_msg_add_hdr( ack_msg, hdr );
+
+ /* Copy From header from the original INVITE. */
+ from = (pjsip_from_hdr*)pjsip_msg_find_remove_hdr(invite_msg,
+ PJSIP_H_FROM, NULL);
+ pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)from );
+
+ /* Copy To header from the original INVITE. */
+ to = (pjsip_to_hdr*)pjsip_msg_find_remove_hdr( invite_msg,
+ PJSIP_H_TO, NULL);
+ pj_strdup(tdata->pool, &to->tag, &rdata->to->tag);
+ pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*)to );
+
+ /* Must contain single Via, just as the original INVITE. */
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_VIA, NULL);
+ pjsip_msg_insert_first_hdr( ack_msg, hdr );
+
+ /* Must have the same CSeq value as the original INVITE, but method
+ * changed to ACK
+ */
+ cseq = (pjsip_cseq_hdr*) pjsip_msg_find_remove_hdr( invite_msg,
+ PJSIP_H_CSEQ, NULL);
+ pjsip_method_set( &cseq->method, PJSIP_ACK_METHOD );
+ pjsip_msg_add_hdr( ack_msg, (pjsip_hdr*) cseq );
+
+ /* If the original INVITE has Route headers, those header fields MUST
+ * appear in the ACK.
+ */
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ while (hdr != NULL) {
+ pjsip_msg_add_hdr( ack_msg, hdr );
+ hdr = pjsip_msg_find_remove_hdr( invite_msg, PJSIP_H_ROUTE, NULL);
+ }
+
+ /* Set the message in the "tdata" to point to the ACK message. */
+ tdata->msg = ack_msg;
+
+ /* Reset transmit packet buffer, to force 're-printing' of message. */
+ tdata->buf.cur = tdata->buf.start;
+
+ /* We're done.
+ * "tdata" parameter now contains the ACK message.
+ */
+}
+
+
+/*
+ * Construct CANCEL request for the previously sent request, according to
+ * chapter 9.1 of RFC3261.
+ */
+PJ_DEF(pj_status_t) pjsip_endpt_create_cancel( pjsip_endpoint *endpt,
pjsip_tx_data *req_tdata,
- pjsip_tx_data **p_tdata) -{ - pjsip_msg *req_msg; /* the original request. */ - pjsip_tx_data *cancel_tdata; - pjsip_msg *cancel_msg; - pjsip_hdr *hdr; - pjsip_cseq_hdr *req_cseq, *cseq; + pjsip_tx_data **p_tdata)
+{
+ pjsip_msg *req_msg; /* the original request. */
+ pjsip_tx_data *cancel_tdata;
+ pjsip_msg *cancel_msg;
+ pjsip_hdr *hdr;
+ pjsip_cseq_hdr *req_cseq, *cseq;
pjsip_uri *req_uri;
- pj_status_t status; - - /* Log this action. */ - PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata)); - - /* Get the original request. */ - req_msg = req_tdata->msg; - - /* The transmit buffer must INVITE request. */ - PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG && + pj_status_t status;
+
+ /* Log this action. */
+ PJ_LOG(5,(THIS_FILE, "pjsip_endpt_create_cancel(tdata=%p)", req_tdata));
+
+ /* Get the original request. */
+ req_msg = req_tdata->msg;
+
+ /* The transmit buffer must INVITE request. */
+ PJ_ASSERT_RETURN(req_msg->type == PJSIP_REQUEST_MSG &&
req_msg->line.req.method.id == PJSIP_INVITE_METHOD,
- PJ_EINVAL); - - /* Create new transmit buffer. */ - status = pjsip_endpt_create_tdata( endpt, &cancel_tdata); - if (status != PJ_SUCCESS) { - return status; - } - - /* Create CANCEL request message. */ - cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG); - cancel_tdata->msg = cancel_msg; - - /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are - * copied from the original request. - */ - /* Set request line. */ - pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD); - req_uri = req_msg->line.req.uri; - cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri); - - /* Copy Call-ID */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL); - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* Copy From header. */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL); - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* Copy To header. */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL); - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* Create new CSeq with equal number, but method set to CANCEL. */ - req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL); - cseq = pjsip_cseq_hdr_create(cancel_tdata->pool); - cseq->cseq = req_cseq->cseq; - pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD); - pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq); - - /* Must only have single Via which matches the top-most Via in the - * request being cancelled. - */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL); - pjsip_msg_insert_first_hdr(cancel_msg, - pjsip_hdr_clone(cancel_tdata->pool, hdr)); - - /* If the original request has Route header, the CANCEL request must also - * has exactly the same. - * Copy "Route" header from the request. - */ - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL); - while (hdr != NULL) { - pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr)); - hdr = hdr->next; - if (hdr != &cancel_msg->hdr) - hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr); - else - break; - } - - /* Done. - * Return the transmit buffer containing the CANCEL request. + PJ_EINVAL);
+
+ /* Create new transmit buffer. */
+ status = pjsip_endpt_create_tdata( endpt, &cancel_tdata);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ /* Create CANCEL request message. */
+ cancel_msg = pjsip_msg_create(cancel_tdata->pool, PJSIP_REQUEST_MSG);
+ cancel_tdata->msg = cancel_msg;
+
+ /* Request-URI, Call-ID, From, To, and the numeric part of the CSeq are
+ * copied from the original request.
+ */
+ /* Set request line. */
+ pjsip_method_set(&cancel_msg->line.req.method, PJSIP_CANCEL_METHOD);
+ req_uri = req_msg->line.req.uri;
+ cancel_msg->line.req.uri = pjsip_uri_clone(cancel_tdata->pool, req_uri);
+
+ /* Copy Call-ID */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_CALL_ID, NULL);
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* Copy From header. */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_FROM, NULL);
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* Copy To header. */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_TO, NULL);
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* Create new CSeq with equal number, but method set to CANCEL. */
+ req_cseq = (pjsip_cseq_hdr*) pjsip_msg_find_hdr(req_msg, PJSIP_H_CSEQ, NULL);
+ cseq = pjsip_cseq_hdr_create(cancel_tdata->pool);
+ cseq->cseq = req_cseq->cseq;
+ pjsip_method_set(&cseq->method, PJSIP_CANCEL_METHOD);
+ pjsip_msg_add_hdr(cancel_msg, (pjsip_hdr*)cseq);
+
+ /* Must only have single Via which matches the top-most Via in the
+ * request being cancelled.
+ */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_VIA, NULL);
+ pjsip_msg_insert_first_hdr(cancel_msg,
+ pjsip_hdr_clone(cancel_tdata->pool, hdr));
+
+ /* If the original request has Route header, the CANCEL request must also
+ * has exactly the same.
+ * Copy "Route" header from the request.
+ */
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, NULL);
+ while (hdr != NULL) {
+ pjsip_msg_add_hdr(cancel_msg, pjsip_hdr_clone(cancel_tdata->pool, hdr));
+ hdr = hdr->next;
+ if (hdr != &cancel_msg->hdr)
+ hdr = pjsip_msg_find_hdr(req_msg, PJSIP_H_ROUTE, hdr);
+ else
+ break;
+ }
+
+ /* Done.
+ * Return the transmit buffer containing the CANCEL request.
+ */
+ *p_tdata = cancel_tdata;
+ return PJ_SUCCESS;
+}
+
+/* Get the address parameters (host, port, flag, TTL, etc) to send the
+ * response.
+ */
+PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool,
+ const pjsip_transport_t *req_transport,
+ const pjsip_via_hdr *via,
+ pjsip_host_port *send_addr)
+{
+ /* Determine the destination address (section 18.2.2):
+ * - for TCP, SCTP, or TLS, send the response using the transport where
+ * the request was received.
+ * - if maddr parameter is present, send to this address using the port
+ * in sent-by or 5060. If multicast is used, the TTL in the Via must
+ * be used, or 1 if ttl parameter is not present.
+ * - otherwise if received parameter is present, set to this address.
+ * - otherwise send to the address in sent-by.
*/
- *p_tdata = cancel_tdata; - return PJ_SUCCESS; -} - -/* Get the address parameters (host, port, flag, TTL, etc) to send the - * response. - */ -PJ_DEF(pj_status_t) pjsip_get_response_addr(pj_pool_t *pool, - const pjsip_transport_t *req_transport, - const pjsip_via_hdr *via, - pjsip_host_port *send_addr) -{ - /* Determine the destination address (section 18.2.2): - * - for TCP, SCTP, or TLS, send the response using the transport where - * the request was received. - * - if maddr parameter is present, send to this address using the port - * in sent-by or 5060. If multicast is used, the TTL in the Via must - * be used, or 1 if ttl parameter is not present. - * - otherwise if received parameter is present, set to this address. - * - otherwise send to the address in sent-by. - */ - send_addr->flag = pjsip_transport_get_flag(req_transport); - send_addr->type = pjsip_transport_get_type(req_transport); - - if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) { - const pj_sockaddr_in *remote_addr; - remote_addr = pjsip_transport_get_remote_addr(req_transport); - pj_strdup2(pool, &send_addr->host, - pj_inet_ntoa(remote_addr->sin_addr)); - send_addr->port = pj_sockaddr_in_get_port(remote_addr); - - } else { - /* Set the host part */ - if (via->maddr_param.slen) { - pj_strdup(pool, &send_addr->host, &via->maddr_param); - } else if (via->recvd_param.slen) { - pj_strdup(pool, &send_addr->host, &via->recvd_param); - } else { - pj_strdup(pool, &send_addr->host, &via->sent_by.host); - } - - /* Set the port */ - send_addr->port = via->sent_by.port; - } - - return PJ_SUCCESS; -} - -/* - * Get the event string from the event ID. - */ -PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e) -{ - return event_str[e]; -} - + send_addr->flag = pjsip_transport_get_flag(req_transport);
+ send_addr->type = pjsip_transport_get_type(req_transport);
+
+ if (PJSIP_TRANSPORT_IS_RELIABLE(req_transport)) {
+ const pj_sockaddr_in *remote_addr;
+ remote_addr = pjsip_transport_get_remote_addr(req_transport);
+ pj_strdup2(pool, &send_addr->host,
+ pj_inet_ntoa(remote_addr->sin_addr));
+ send_addr->port = pj_sockaddr_in_get_port(remote_addr);
+
+ } else {
+ /* Set the host part */
+ if (via->maddr_param.slen) {
+ pj_strdup(pool, &send_addr->host, &via->maddr_param);
+ } else if (via->recvd_param.slen) {
+ pj_strdup(pool, &send_addr->host, &via->recvd_param);
+ } else {
+ pj_strdup(pool, &send_addr->host, &via->sent_by.host);
+ }
+
+ /* Set the port */
+ send_addr->port = via->sent_by.port;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get the event string from the event ID.
+ */
+PJ_DEF(const char *) pjsip_event_str(pjsip_event_id_e e)
+{
+ return event_str[e];
+}
+
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c index 587df864..6fd32f43 100644 --- a/pjsip/src/pjsip/sip_msg.c +++ b/pjsip/src/pjsip/sip_msg.c @@ -1,1416 +1,1438 @@ -/* $Id$ - */ -#include <pjsip/sip_msg.h> -#include <pjsip/print_util.h> -#include <pj/string.h> -#include <pj/pool.h> - -/* - * Include inline definitions here if functions are NOT inlined. - */ -#if PJ_FUNCTIONS_ARE_INLINED==0 -# include <pjsip/sip_msg_i.h> -#endif - -static const pj_str_t method_names[] = -{ - { "INVITE", 6 }, - { "CANCEL", 6 }, - { "ACK", 3 }, - { "BYE", 3 }, - { "REGISTER", 8 }, - { "OPTIONS", 7 }, -}; - -const pj_str_t pjsip_hdr_names[] = -{ - { "Accept", 6 }, // PJSIP_H_ACCEPT, - { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING, - { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE, - { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO, - { "Allow", 5 }, // PJSIP_H_ALLOW, - { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO, - { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION, - { "Call-ID", 7 }, // PJSIP_H_CALL_ID, - { "Call-Info", 9 }, // PJSIP_H_CALL_INFO, - { "Contact", 7 }, // PJSIP_H_CONTACT, - { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION, - { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING, - { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE, - { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH, - { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE, - { "CSeq", 4 }, // PJSIP_H_CSEQ, - { "Date", 4 }, // PJSIP_H_DATE, - { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO, - { "Expires", 7 }, // PJSIP_H_EXPIRES, - { "From", 4 }, // PJSIP_H_FROM, - { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO, - { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS, - { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION, - { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES, - { "Organization", 12 }, // PJSIP_H_ORGANIZATION, - { "Priority", 8 }, // PJSIP_H_PRIORITY, - { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE, - { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION, - { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE, - { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE, - { "Reply-To", 8 }, // PJSIP_H_REPLY_TO, - { "Require", 7 }, // PJSIP_H_REQUIRE, - { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER, - { "Route", 5 }, // PJSIP_H_ROUTE, - { "Server", 6 }, // PJSIP_H_SERVER, - { "Subject", 7 }, // PJSIP_H_SUBJECT, - { "Supported", 9 }, // PJSIP_H_SUPPORTED, - { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP, - { "To", 2 }, // PJSIP_H_TO, - { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED, - { "User-Agent", 10 }, // PJSIP_H_USER_AGENT, - { "Via", 3 }, // PJSIP_H_VIA, - { "Warning", 7 }, // PJSIP_H_WARNING, - { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE, - - { "_Unknown-Header", 15 }, // PJSIP_H_OTHER, -}; - -static pj_str_t status_phrase[710]; -static int print_media_type(char *buf, const pjsip_media_type *media); - -static int init_status_phrase() -{ - int i; - pj_str_t default_reason_phrase = { "Default status message", 22}; - - for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i) - status_phrase[i] = default_reason_phrase; - - pj_strset2( &status_phrase[100], "Trying"); - pj_strset2( &status_phrase[180], "Ringing"); - pj_strset2( &status_phrase[181], "Call Is Being Forwarded"); - pj_strset2( &status_phrase[182], "Queued"); - pj_strset2( &status_phrase[183], "Session Progress"); - - pj_strset2( &status_phrase[200], "OK"); - - pj_strset2( &status_phrase[300], "Multiple Choices"); - pj_strset2( &status_phrase[301], "Moved Permanently"); - pj_strset2( &status_phrase[302], "Moved Temporarily"); - pj_strset2( &status_phrase[305], "Use Proxy"); - pj_strset2( &status_phrase[380], "Alternative Service"); - - pj_strset2( &status_phrase[400], "Bad Request"); - pj_strset2( &status_phrase[401], "Unauthorized"); - pj_strset2( &status_phrase[402], "Payment Required"); - pj_strset2( &status_phrase[403], "Forbidden"); - pj_strset2( &status_phrase[404], "Not Found"); - pj_strset2( &status_phrase[405], "Method Not Allowed"); - pj_strset2( &status_phrase[406], "Not Acceptable"); - pj_strset2( &status_phrase[407], "Proxy Authentication Required"); - pj_strset2( &status_phrase[408], "Request Timeout"); - pj_strset2( &status_phrase[410], "Gone"); - pj_strset2( &status_phrase[413], "Request Entity Too Large"); - pj_strset2( &status_phrase[414], "Request URI Too Long"); - pj_strset2( &status_phrase[415], "Unsupported Media Type"); - pj_strset2( &status_phrase[416], "Unsupported URI Scheme"); - pj_strset2( &status_phrase[420], "Bad Extension"); - pj_strset2( &status_phrase[421], "Extension Required"); - pj_strset2( &status_phrase[423], "Interval Too Brief"); - pj_strset2( &status_phrase[480], "Temporarily Unavailable"); - pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist"); - pj_strset2( &status_phrase[482], "Loop Detected"); - pj_strset2( &status_phrase[483], "Too Many Hops"); - pj_strset2( &status_phrase[484], "Address Incompleted"); - pj_strset2( &status_phrase[485], "Ambiguous"); - pj_strset2( &status_phrase[486], "Busy Here"); - pj_strset2( &status_phrase[487], "Request Terminated"); - pj_strset2( &status_phrase[488], "Not Acceptable Here"); - pj_strset2( &status_phrase[491], "Request Pending"); - pj_strset2( &status_phrase[493], "Undecipherable"); - - pj_strset2( &status_phrase[500], "Internal Server Error"); - pj_strset2( &status_phrase[501], "Not Implemented"); - pj_strset2( &status_phrase[502], "Bad Gateway"); - pj_strset2( &status_phrase[503], "Service Unavailable"); - pj_strset2( &status_phrase[504], "Server Timeout"); - pj_strset2( &status_phrase[505], "Version Not Supported"); - pj_strset2( &status_phrase[513], "Message Too Large"); - - pj_strset2( &status_phrase[600], "Busy Everywhere"); - pj_strset2( &status_phrase[603], "Decline"); - pj_strset2( &status_phrase[604], "Does Not Exist Anywhere"); - pj_strset2( &status_phrase[606], "Not Acceptable"); - - pj_strset2( &status_phrase[701], "No response from destination server"); - pj_strset2( &status_phrase[702], "Unable to resolve destination server"); - pj_strset2( &status_phrase[703], "Error sending message to destination server"); - - return 1; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Method. - */ - -PJ_DEF(void) pjsip_method_init( pjsip_method *m, - pj_pool_t *pool, - const pj_str_t *str) -{ - pj_str_t dup; - pjsip_method_init_np(m, pj_strdup(pool, &dup, str)); -} - -PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me ) -{ - m->id = me; - m->name = method_names[me]; -} - -PJ_DEF(void) pjsip_method_init_np(pjsip_method *m, - pj_str_t *str) -{ - int i; - for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) { - if (pj_stricmp(str, &method_names[i])==0) { - m->id = (pjsip_method_e)i; - m->name = method_names[i]; - return; - } - } - m->id = PJSIP_OTHER_METHOD; - m->name = *str; -} - -PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool, - pjsip_method *method, - const pjsip_method *rhs ) -{ - method->id = rhs->id; - if (rhs->id != PJSIP_OTHER_METHOD) { - method->name = rhs->name; - } else { - pj_strdup(pool, &method->name, &rhs->name); - } -} - - -PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2) -{ - if (m1->id == m2->id) { - if (m1->id != PJSIP_OTHER_METHOD) - return 0; - return pj_stricmp(&m1->name, &m2->name); - } - - return ( m1->id < m2->id ) ? -1 : 1; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Message. - */ - -PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type) -{ - pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg)); - pj_list_init(&msg->hdr); - msg->type = type; - msg->body = NULL; - return msg; -} - -PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg, - pjsip_hdr_e hdr_type, void *start) -{ - pjsip_hdr *hdr=start, *end=&msg->hdr; - - if (hdr == NULL) { - hdr = msg->hdr.next; - } - for (; hdr!=end; hdr = hdr->next) { - if (hdr->type == hdr_type) - return hdr; - } - return NULL; -} - -PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg, - const pj_str_t *name, void *start) -{ - pjsip_hdr *hdr=start, *end=&msg->hdr; - - if (hdr == NULL) { - hdr = msg->hdr.next; - } - for (; hdr!=end; hdr = hdr->next) { - if (hdr->type < PJSIP_H_OTHER) { - if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0) - return hdr; - } else { - if (pj_stricmp(&hdr->name, name) == 0) - return hdr; - } - } - return NULL; -} - -PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg, - pjsip_hdr_e hdr_type, void *start) -{ - pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start); - if (hdr) { - pj_list_erase(hdr); - } - return hdr; -} - -PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size) -{ - char *p=buf, *end=buf+size; - int len; - pjsip_hdr *hdr; - pj_str_t clen_hdr = { "Content-Length: ", 16}; - - /* Get a wild guess on how many bytes are typically needed. - * We'll check this later in detail, but this serves as a quick check. - */ - if (size < 256) - return -1; - - /* Print request line or status line depending on message type */ - if (msg->type == PJSIP_REQUEST_MSG) { - pjsip_uri *uri; - - /* Add method. */ - len = msg->line.req.method.name.slen; - pj_memcpy(p, msg->line.req.method.name.ptr, len); - p += len; - *p++ = ' '; - - /* Add URI */ - uri = pjsip_uri_get_uri(msg->line.req.uri); - len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p); - if (len < 1) - return -1; - p += len; - - /* Add ' SIP/2.0' */ - if (end-p < 16) - return -1; - pj_memcpy(p, " SIP/2.0\r\n", 10); - p += 10; - - } else { - - /* Add 'SIP/2.0 ' */ - pj_memcpy(p, "SIP/2.0 ", 8); - p += 8; - - /* Add status code. */ - len = pj_utoa(msg->line.status.code, p); - p += len; - *p++ = ' '; - - /* Add reason text. */ - len = msg->line.status.reason.slen; - pj_memcpy(p, msg->line.status.reason.ptr, len ); - p += len; - - /* Add newline. */ - *p++ = '\r'; - *p++ = '\n'; - } - - /* Print each of the headers. */ - for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) { - len = (*hdr->vptr->print_on)(hdr, p, end-p); - if (len < 1) - return -1; - p += len; - - if (p+3 >= end) - return -1; - - *p++ = '\r'; - *p++ = '\n'; - } - - /* Process message body. */ - if (msg->body) { - pj_str_t ctype_hdr = { "Content-Type: ", 14}; - int len; - const pjsip_media_type *media = &msg->body->content_type; - char *clen_pos; - - /* Add Content-Type header. */ - if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) { - return -1; - } - pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen); - p += ctype_hdr.slen; - p += print_media_type(p, media); - *p++ = '\r'; - *p++ = '\n'; - - /* Add Content-Length header. */ - if ((end-p) < clen_hdr.slen+12+2) { - return -1; - } - pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); - p += clen_hdr.slen; - - /* Print blanks after "Content-Type:", this is where we'll put - * the content length value after we know the length of the - * body. - */ - pj_memset(p, ' ', 12); - clen_pos = p; - p += 12; - *p++ = '\r'; - *p++ = '\n'; - - /* Add blank newline. */ - *p++ = '\r'; - *p++ = '\n'; - - /* Print the message body itself. */ - len = (*msg->body->print_body)(msg->body, p, end-p); - if (len < 0) { - return -1; - } - p += len; - - /* Now that we have the length of the body, print this to the - * Content-Length header. - */ - len = pj_utoa(len, clen_pos); - clen_pos[len] = ' '; - - } else { - /* There's no message body. - * Add Content-Length with zero value. - */ - if ((end-p) < clen_hdr.slen+8) { - return -1; - } - pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen); - p += clen_hdr.slen; - *p++ = '0'; - *p++ = '\r'; - *p++ = '\n'; - *p++ = '\r'; - *p++ = '\n'; - } - - *p = '\0'; - return p-buf; -} - -/////////////////////////////////////////////////////////////////////////////// -PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr ) -{ - const pjsip_hdr *hdr = hdr_ptr; - return (*hdr->vptr->clone)(pool, hdr_ptr); -} - - -PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr ) -{ - const pjsip_hdr *hdr = hdr_ptr; - return (*hdr->vptr->shallow_clone)(pool, hdr_ptr); -} - -PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len) -{ - pjsip_hdr *hdr = hdr_ptr; - return (*hdr->vptr->print_on)(hdr_ptr, buf, len); -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Status/Reason Phrase - */ - -PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code) -{ - static int is_initialized; - if (is_initialized == 0) { - is_initialized = 1; - init_status_phrase(); - } - - return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ? - &status_phrase[code] : &status_phrase[0]; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Generic pjsip_hdr_names/hvalue header. - */ - -static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool, - const pjsip_generic_string_hdr *hdr); -static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_string_hdr *hdr ); - -static pjsip_hdr_vptr generic_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone, - (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 ) -{ - pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr)); - 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; - 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) -{ - pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname); - pj_strdup(pool, &hdr->hvalue, hvalue); - return hdr; -} - -static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - - if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen); - p += hdr->hvalue.slen; - *p = '\0'; - - return p - buf; -} - -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); - - hdr->type = rhs->type; - hdr->sname = hdr->name; - pj_strdup( pool, &hdr->hvalue, &rhs->hvalue); - return hdr; -} - -static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_string_hdr *rhs ) -{ - pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Generic pjsip_hdr_names/integer value header. - */ - -static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *hdr); -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *hdr ); - -static pjsip_hdr_vptr generic_int_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone, - (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 ) -{ - pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr)); - 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; - 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) -{ - pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname); - hdr->ivalue = value; - return hdr; -} - -static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - - if ((pj_ssize_t)size < hdr->name.slen + 15) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - p += pj_utoa(hdr->ivalue, p); - - return p - buf; -} - -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *rhs) -{ - pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_int_hdr *rhs ) -{ - pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Generic array header. - */ -static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size); -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *hdr); -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *hdr); - -static pjsip_hdr_vptr generic_array_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone, - (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) -{ - pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr)); - init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr); - if (hnames) { - pj_strdup(pool, &hdr->name, hnames); - hdr->sname = hdr->name; - } - hdr->count = 0; - return hdr; - -} - -static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf, *endbuf = buf+size; - - copy_advance(p, hdr->name); - *p++ = ':'; - *p++ = ' '; - - if (hdr->count > 0) { - unsigned i; - int printed; - copy_advance(p, hdr->values[0]); - for (i=1; i<hdr->count; ++i) { - copy_advance_pair(p, ", ", 2, hdr->values[i]); - } - } - - return p - buf; -} - -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *rhs) -{ - unsigned i; - pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - - pj_memcpy(hdr, rhs, sizeof(*hdr)); - for (i=0; i<rhs->count; ++i) { - pj_strdup(pool, &hdr->values[i], &rhs->values[i]); - } - - return hdr; -} - - -static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_generic_array_hdr *rhs) -{ - pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Accept header. - */ -PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool) -{ - pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Allow header. - */ - -PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool) -{ - pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Call-ID header. - */ - -PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool ) -{ - pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Content-Length header. - */ -static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size); -static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr); -#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone - -static pjsip_hdr_vptr clen_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print, -}; - -PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool ) -{ - pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr)); - init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr); - hdr->len = 0; - return hdr; -} - -static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - int len; - - if ((pj_ssize_t)size < hdr->name.slen + 14) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - len = pj_utoa(hdr->len, p); - p += len; - *p = '\0'; - - return p-buf; -} - -static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs) -{ - pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool); - hdr->len = rhs->len; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * CSeq header. - */ -static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size); -static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr); -static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr ); - -static pjsip_hdr_vptr cseq_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print, -}; - -PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool ) -{ - pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr)); - init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr); - hdr->cseq = 0; - hdr->method.id = PJSIP_OTHER_METHOD; - hdr->method.name.ptr = NULL; - hdr->method.name.slen = 0; - return hdr; -} - -static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size) -{ - char *p = buf; - int len; - - if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15) - return -1; - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - len = pj_utoa(hdr->cseq, p); - p += len; - *p++ = ' '; - - pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen); - p += hdr->method.name.slen; - - *p = '\0'; - - return p-buf; -} - -static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, - const pjsip_cseq_hdr *rhs) -{ - pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool); - hdr->cseq = rhs->cseq; - pjsip_method_copy(pool, &hdr->method, &rhs->method); - return hdr; -} - -static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_cseq_hdr *rhs ) -{ - pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Contact header. - */ -static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size); -static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr); -static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *); - -static pjsip_hdr_vptr contact_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print, -}; - -PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool ) -{ - pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr); - hdr->expires = -1; - return hdr; -} - -static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, - pj_size_t size) -{ - if (hdr->star) { - char *p = buf; - if ((pj_ssize_t)size < hdr->name.slen + 6) - return -1; - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - *p++ = '*'; - *p = '\0'; - return p - buf; - - } else { - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri, - buf, endbuf-buf); - if (printed < 1) - return -1; - - buf += printed; - - if (hdr->q1000) { - if (buf+19 >= endbuf) - return -1; - - /* - printed = sprintf(buf, ";q=%u.%03u", - hdr->q1000/1000, hdr->q1000 % 1000); - */ - pj_memcpy(buf, ";q=", 3); - printed = pj_utoa(hdr->q1000/1000, buf+3); - buf += printed + 3; - *buf++ = '.'; - printed = pj_utoa(hdr->q1000 % 1000, buf); - buf += printed; - } - - if (hdr->expires >= 0) { - if (buf+23 >= endbuf) - return -1; - - pj_memcpy(buf, ";expires=", 9); - printed = pj_utoa(hdr->expires, buf+9); - buf += printed + 9; - } - - if (hdr->other_param.slen) { - copy_advance(buf, hdr->other_param); - } - - *buf = '\0'; - return buf-startbuf; - } -} - -static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, - const pjsip_contact_hdr *rhs) -{ - pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool); - - hdr->star = rhs->star; - if (hdr->star) - return hdr; - - hdr->uri = pjsip_uri_clone(pool, rhs->uri); - hdr->q1000 = rhs->q1000; - hdr->expires = rhs->expires; - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_contact_hdr *rhs) -{ - pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Content-Type header.. - */ -static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size); -static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *hdr); -#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone - -static pjsip_hdr_vptr ctype_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print, -}; - -PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool ) -{ - pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr); - return hdr; -} - -static int print_media_type(char *buf, const pjsip_media_type *media) -{ - char *p = buf; - - pj_memcpy(p, media->type.ptr, media->type.slen); - p += media->type.slen; - *p++ = '/'; - pj_memcpy(p, media->subtype.ptr, media->subtype.slen); - p += media->subtype.slen; - - if (media->param.slen) { - pj_memcpy(p, media->param.ptr, media->param.slen); - p += media->param.slen; - } - - return p-buf; -} - -static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, - char *buf, pj_size_t size) -{ - char *p = buf; - int len; - - if ((pj_ssize_t)size < hdr->name.slen + - hdr->media.type.slen + hdr->media.subtype.slen + - hdr->media.param.slen + 8) - { - return -1; - } - - pj_memcpy(p, hdr->name.ptr, hdr->name.slen); - p += hdr->name.slen; - *p++ = ':'; - *p++ = ' '; - - len = print_media_type(p, &hdr->media); - p += len; - - *p = '\0'; - return p-buf; -} - -static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, - const pjsip_ctype_hdr *rhs) -{ - pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool); - pj_strdup(pool, &hdr->media.type, &rhs->media.type); - pj_strdup(pool, &hdr->media.param, &rhs->media.param); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Expires header. - */ -PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool ) -{ - pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * To or From header. - */ -static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, - char *buf, pj_size_t size); -static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *hdr); -static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *hdr); - - -static pjsip_hdr_vptr fromto_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print, -}; - -PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool ) -{ - pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr); - return hdr; -} - -PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool ) -{ - pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr); - return hdr; -} - -PJ_DEF(pjsip_from_hdr*) pjsip_fromto_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 ) -{ - hdr->type = PJSIP_H_TO; - hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO]; - return hdr; -} - -static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf); - if (printed < 1) - return -1; - - buf += printed; - - copy_advance_pair(buf, ";tag=", 5, hdr->tag); - if (hdr->other_param.slen) - copy_advance(buf, hdr->other_param); - - return buf-startbuf; -} - -static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *rhs) -{ - pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool); - - hdr->type = rhs->type; - hdr->name = rhs->name; - hdr->sname = rhs->sname; - hdr->uri = pjsip_uri_clone(pool, rhs->uri); - pj_strdup( pool, &hdr->tag, &rhs->tag); - pj_strdup( pool, &hdr->other_param, &rhs->other_param); - - return hdr; -} - -static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_fromto_hdr *rhs) -{ - pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Max-Forwards header. - */ -PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool) -{ - pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Min-Expires header. - */ -PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool) -{ - pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Record-Route and Route header. - */ -static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size ); -static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); -static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r ); - -static pjsip_hdr_vptr routing_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print, -}; - -PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool ) -{ - pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr); - pjsip_name_addr_init(&hdr->name_addr); - pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param)); - return hdr; -} - -PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool ) -{ - pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr); - pjsip_name_addr_init(&hdr->name_addr); - pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param)); - return hdr; -} - -PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr ) -{ - hdr->type = PJSIP_H_RECORD_ROUTE; - hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE]; - return hdr; -} - -PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr ) -{ - hdr->type = PJSIP_H_ROUTE; - hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE]; - return hdr; -} - -static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr, - char *buf, pj_size_t size ) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf); - if (printed < 1) - return -1; - buf += printed; - - if (hdr->other_param.slen) { - copy_advance(buf, hdr->other_param); - } - - return buf-startbuf; -} - -static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, - const pjsip_routing_hdr *rhs ) -{ - pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - - init_hdr(hdr, rhs->type, rhs->vptr); - pjsip_name_addr_init(&hdr->name_addr); - pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr); - pj_strdup( pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_routing_hdr *rhs ) -{ - pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Require header. - */ -PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool) -{ - pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Retry-After header. - */ -PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool) -{ - pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr); - hdr->ivalue = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Supported header. - */ -PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool) -{ - pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - - -/////////////////////////////////////////////////////////////////////////////// -/* - * Unsupported header. - */ -PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool) -{ - pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr); - hdr->count = 0; - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * Via header. - */ -static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size); -static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr); -static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr ); - -static pjsip_hdr_vptr via_hdr_vptr = -{ - (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone, - (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone, - (pjsip_hdr_print_fptr) &pjsip_via_hdr_print, -}; - -PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool ) -{ - pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); - init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr); - hdr->sent_by.port = 5060; - hdr->ttl_param = -1; - hdr->rport_param = -1; - return hdr; -} - -static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, - char *buf, pj_size_t size) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - pj_str_t sip_ver = { "SIP/2.0/", 8 }; - - if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen + - hdr->transport.slen + hdr->sent_by.host.slen + 12) - { - return -1; - } - - /* pjsip_hdr_names */ - copy_advance(buf, hdr->name); - *buf++ = ':'; - *buf++ = ' '; - - /* SIP/2.0/transport host:port */ - pj_memcpy(buf, sip_ver.ptr, sip_ver.slen); - buf += sip_ver.slen; - pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen); - buf += hdr->transport.slen; - *buf++ = ' '; - pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen); - buf += hdr->sent_by.host.slen; - *buf++ = ':'; - printed = pj_utoa(hdr->sent_by.port, buf); - buf += printed; - - if (hdr->ttl_param >= 0) { - size = endbuf-buf; - if (size < 14) - return -1; - pj_memcpy(buf, ";ttl=", 5); - printed = pj_utoa(hdr->ttl_param, buf+5); - buf += printed + 5; - } - - if (hdr->rport_param >= 0) { - size = endbuf-buf; - if (size < 14) - return -1; - pj_memcpy(buf, ";rport", 6); - buf += 6; - if (hdr->rport_param > 0) { - *buf++ = '='; - buf += pj_utoa(hdr->rport_param, buf); - } - } - - - copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param); - copy_advance_pair(buf, ";received=", 10, hdr->recvd_param); - copy_advance_pair(buf, ";branch=", 8, hdr->branch_param); - copy_advance(buf, hdr->other_param); - - *buf = '\0'; - return buf-startbuf; -} - -static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, - const pjsip_via_hdr *rhs) -{ - pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool); - pj_strdup(pool, &hdr->transport, &rhs->transport); - pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host); - hdr->sent_by.port = rhs->sent_by.port; - hdr->ttl_param = rhs->ttl_param; - hdr->rport_param = rhs->rport_param; - pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param); - pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param); - pj_strdup(pool, &hdr->branch_param, &rhs->branch_param); - pj_strdup(pool, &hdr->other_param, &rhs->other_param); - return hdr; -} - -static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, - const pjsip_via_hdr *rhs ) -{ - pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); - pj_memcpy(hdr, rhs, sizeof(*hdr)); - return hdr; -} - -/////////////////////////////////////////////////////////////////////////////// -/* - * General purpose function to textual data in a SIP body. - */ -PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) -{ - if (size < msg_body->len) - return -1; - pj_memcpy(buf, msg_body->data, msg_body->len); - return msg_body->len; -} +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_msg.h>
+#include <pjsip/print_util.h>
+#include <pj/string.h>
+#include <pj/pool.h>
+
+/*
+ * Include inline definitions here if functions are NOT inlined.
+ */
+#if PJ_FUNCTIONS_ARE_INLINED==0
+# include <pjsip/sip_msg_i.h>
+#endif
+
+static const pj_str_t method_names[] =
+{
+ { "INVITE", 6 },
+ { "CANCEL", 6 },
+ { "ACK", 3 },
+ { "BYE", 3 },
+ { "REGISTER", 8 },
+ { "OPTIONS", 7 },
+};
+
+const pj_str_t pjsip_hdr_names[] =
+{
+ { "Accept", 6 }, // PJSIP_H_ACCEPT,
+ { "Accept-Encoding", 15 }, // PJSIP_H_ACCEPT_ENCODING,
+ { "Accept-Language", 15 }, // PJSIP_H_ACCEPT_LANGUAGE,
+ { "Alert-Info", 10 }, // PJSIP_H_ALERT_INFO,
+ { "Allow", 5 }, // PJSIP_H_ALLOW,
+ { "Authentication-Info",19 }, // PJSIP_H_AUTHENTICATION_INFO,
+ { "Authorization", 13 }, // PJSIP_H_AUTHORIZATION,
+ { "Call-ID", 7 }, // PJSIP_H_CALL_ID,
+ { "Call-Info", 9 }, // PJSIP_H_CALL_INFO,
+ { "Contact", 7 }, // PJSIP_H_CONTACT,
+ { "Content-Disposition",19 }, // PJSIP_H_CONTENT_DISPOSITION,
+ { "Content-Encoding", 16 }, // PJSIP_H_CONTENT_ENCODING,
+ { "Content-Language", 16 }, // PJSIP_H_CONTENT_LANGUAGE,
+ { "Content-Length", 14 }, // PJSIP_H_CONTENT_LENGTH,
+ { "Content-Type", 12 }, // PJSIP_H_CONTENT_TYPE,
+ { "CSeq", 4 }, // PJSIP_H_CSEQ,
+ { "Date", 4 }, // PJSIP_H_DATE,
+ { "Error-Info", 10 }, // PJSIP_H_ERROR_INFO,
+ { "Expires", 7 }, // PJSIP_H_EXPIRES,
+ { "From", 4 }, // PJSIP_H_FROM,
+ { "In-Reply-To", 11 }, // PJSIP_H_IN_REPLY_TO,
+ { "Max-Forwards", 12 }, // PJSIP_H_MAX_FORWARDS,
+ { "MIME-Version", 12 }, // PJSIP_H_MIME_VERSION,
+ { "Min-Expires", 11 }, // PJSIP_H_MIN_EXPIRES,
+ { "Organization", 12 }, // PJSIP_H_ORGANIZATION,
+ { "Priority", 8 }, // PJSIP_H_PRIORITY,
+ { "Proxy-Authenticate", 18 }, // PJSIP_H_PROXY_AUTHENTICATE,
+ { "Proxy-Authorization",19 }, // PJSIP_H_PROXY_AUTHORIZATION,
+ { "Proxy-Require", 13 }, // PJSIP_H_PROXY_REQUIRE,
+ { "Record-Route", 12 }, // PJSIP_H_RECORD_ROUTE,
+ { "Reply-To", 8 }, // PJSIP_H_REPLY_TO,
+ { "Require", 7 }, // PJSIP_H_REQUIRE,
+ { "Retry-After", 11 }, // PJSIP_H_RETRY_AFTER,
+ { "Route", 5 }, // PJSIP_H_ROUTE,
+ { "Server", 6 }, // PJSIP_H_SERVER,
+ { "Subject", 7 }, // PJSIP_H_SUBJECT,
+ { "Supported", 9 }, // PJSIP_H_SUPPORTED,
+ { "Timestamp", 9 }, // PJSIP_H_TIMESTAMP,
+ { "To", 2 }, // PJSIP_H_TO,
+ { "Unsupported", 11 }, // PJSIP_H_UNSUPPORTED,
+ { "User-Agent", 10 }, // PJSIP_H_USER_AGENT,
+ { "Via", 3 }, // PJSIP_H_VIA,
+ { "Warning", 7 }, // PJSIP_H_WARNING,
+ { "WWW-Authenticate", 16 }, // PJSIP_H_WWW_AUTHENTICATE,
+
+ { "_Unknown-Header", 15 }, // PJSIP_H_OTHER,
+};
+
+static pj_str_t status_phrase[710];
+static int print_media_type(char *buf, const pjsip_media_type *media);
+
+static int init_status_phrase()
+{
+ int i;
+ pj_str_t default_reason_phrase = { "Default status message", 22};
+
+ for (i=0; i<PJ_ARRAY_SIZE(status_phrase); ++i)
+ status_phrase[i] = default_reason_phrase;
+
+ pj_strset2( &status_phrase[100], "Trying");
+ pj_strset2( &status_phrase[180], "Ringing");
+ pj_strset2( &status_phrase[181], "Call Is Being Forwarded");
+ pj_strset2( &status_phrase[182], "Queued");
+ pj_strset2( &status_phrase[183], "Session Progress");
+
+ pj_strset2( &status_phrase[200], "OK");
+
+ pj_strset2( &status_phrase[300], "Multiple Choices");
+ pj_strset2( &status_phrase[301], "Moved Permanently");
+ pj_strset2( &status_phrase[302], "Moved Temporarily");
+ pj_strset2( &status_phrase[305], "Use Proxy");
+ pj_strset2( &status_phrase[380], "Alternative Service");
+
+ pj_strset2( &status_phrase[400], "Bad Request");
+ pj_strset2( &status_phrase[401], "Unauthorized");
+ pj_strset2( &status_phrase[402], "Payment Required");
+ pj_strset2( &status_phrase[403], "Forbidden");
+ pj_strset2( &status_phrase[404], "Not Found");
+ pj_strset2( &status_phrase[405], "Method Not Allowed");
+ pj_strset2( &status_phrase[406], "Not Acceptable");
+ pj_strset2( &status_phrase[407], "Proxy Authentication Required");
+ pj_strset2( &status_phrase[408], "Request Timeout");
+ pj_strset2( &status_phrase[410], "Gone");
+ pj_strset2( &status_phrase[413], "Request Entity Too Large");
+ pj_strset2( &status_phrase[414], "Request URI Too Long");
+ pj_strset2( &status_phrase[415], "Unsupported Media Type");
+ pj_strset2( &status_phrase[416], "Unsupported URI Scheme");
+ pj_strset2( &status_phrase[420], "Bad Extension");
+ pj_strset2( &status_phrase[421], "Extension Required");
+ pj_strset2( &status_phrase[423], "Interval Too Brief");
+ pj_strset2( &status_phrase[480], "Temporarily Unavailable");
+ pj_strset2( &status_phrase[481], "Call/Transaction Does Not Exist");
+ pj_strset2( &status_phrase[482], "Loop Detected");
+ pj_strset2( &status_phrase[483], "Too Many Hops");
+ pj_strset2( &status_phrase[484], "Address Incompleted");
+ pj_strset2( &status_phrase[485], "Ambiguous");
+ pj_strset2( &status_phrase[486], "Busy Here");
+ pj_strset2( &status_phrase[487], "Request Terminated");
+ pj_strset2( &status_phrase[488], "Not Acceptable Here");
+ pj_strset2( &status_phrase[491], "Request Pending");
+ pj_strset2( &status_phrase[493], "Undecipherable");
+
+ pj_strset2( &status_phrase[500], "Internal Server Error");
+ pj_strset2( &status_phrase[501], "Not Implemented");
+ pj_strset2( &status_phrase[502], "Bad Gateway");
+ pj_strset2( &status_phrase[503], "Service Unavailable");
+ pj_strset2( &status_phrase[504], "Server Timeout");
+ pj_strset2( &status_phrase[505], "Version Not Supported");
+ pj_strset2( &status_phrase[513], "Message Too Large");
+
+ pj_strset2( &status_phrase[600], "Busy Everywhere");
+ pj_strset2( &status_phrase[603], "Decline");
+ pj_strset2( &status_phrase[604], "Does Not Exist Anywhere");
+ pj_strset2( &status_phrase[606], "Not Acceptable");
+
+ pj_strset2( &status_phrase[701], "No response from destination server");
+ pj_strset2( &status_phrase[702], "Unable to resolve destination server");
+ pj_strset2( &status_phrase[703], "Error sending message to destination server");
+
+ return 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Method.
+ */
+
+PJ_DEF(void) pjsip_method_init( pjsip_method *m,
+ pj_pool_t *pool,
+ const pj_str_t *str)
+{
+ pj_str_t dup;
+ pjsip_method_init_np(m, pj_strdup(pool, &dup, str));
+}
+
+PJ_DEF(void) pjsip_method_set( pjsip_method *m, pjsip_method_e me )
+{
+ m->id = me;
+ m->name = method_names[me];
+}
+
+PJ_DEF(void) pjsip_method_init_np(pjsip_method *m,
+ pj_str_t *str)
+{
+ int i;
+ for (i=0; i<PJ_ARRAY_SIZE(method_names); ++i) {
+ if (pj_stricmp(str, &method_names[i])==0) {
+ m->id = (pjsip_method_e)i;
+ m->name = method_names[i];
+ return;
+ }
+ }
+ m->id = PJSIP_OTHER_METHOD;
+ m->name = *str;
+}
+
+PJ_DEF(void) pjsip_method_copy( pj_pool_t *pool,
+ pjsip_method *method,
+ const pjsip_method *rhs )
+{
+ method->id = rhs->id;
+ if (rhs->id != PJSIP_OTHER_METHOD) {
+ method->name = rhs->name;
+ } else {
+ pj_strdup(pool, &method->name, &rhs->name);
+ }
+}
+
+
+PJ_DEF(int) pjsip_method_cmp( const pjsip_method *m1, const pjsip_method *m2)
+{
+ if (m1->id == m2->id) {
+ if (m1->id != PJSIP_OTHER_METHOD)
+ return 0;
+ return pj_stricmp(&m1->name, &m2->name);
+ }
+
+ return ( m1->id < m2->id ) ? -1 : 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Message.
+ */
+
+PJ_DEF(pjsip_msg*) pjsip_msg_create( pj_pool_t *pool, pjsip_msg_type_e type)
+{
+ pjsip_msg *msg = pj_pool_alloc(pool, sizeof(pjsip_msg));
+ pj_list_init(&msg->hdr);
+ msg->type = type;
+ msg->body = NULL;
+ return msg;
+}
+
+PJ_DEF(void*) pjsip_msg_find_hdr( pjsip_msg *msg,
+ pjsip_hdr_e hdr_type, void *start)
+{
+ pjsip_hdr *hdr=start, *end=&msg->hdr;
+
+ if (hdr == NULL) {
+ hdr = msg->hdr.next;
+ }
+ for (; hdr!=end; hdr = hdr->next) {
+ if (hdr->type == hdr_type)
+ return hdr;
+ }
+ return NULL;
+}
+
+PJ_DEF(void*) pjsip_msg_find_hdr_by_name( pjsip_msg *msg,
+ const pj_str_t *name, void *start)
+{
+ pjsip_hdr *hdr=start, *end=&msg->hdr;
+
+ if (hdr == NULL) {
+ hdr = msg->hdr.next;
+ }
+ for (; hdr!=end; hdr = hdr->next) {
+ if (hdr->type < PJSIP_H_OTHER) {
+ if (pj_stricmp(&pjsip_hdr_names[hdr->type], name) == 0)
+ return hdr;
+ } else {
+ if (pj_stricmp(&hdr->name, name) == 0)
+ return hdr;
+ }
+ }
+ return NULL;
+}
+
+PJ_DEF(void*) pjsip_msg_find_remove_hdr( pjsip_msg *msg,
+ pjsip_hdr_e hdr_type, void *start)
+{
+ pjsip_hdr *hdr = pjsip_msg_find_hdr(msg, hdr_type, start);
+ if (hdr) {
+ pj_list_erase(hdr);
+ }
+ return hdr;
+}
+
+PJ_DEF(pj_ssize_t) pjsip_msg_print( pjsip_msg *msg, char *buf, pj_size_t size)
+{
+ char *p=buf, *end=buf+size;
+ int len;
+ pjsip_hdr *hdr;
+ pj_str_t clen_hdr = { "Content-Length: ", 16};
+
+ /* Get a wild guess on how many bytes are typically needed.
+ * We'll check this later in detail, but this serves as a quick check.
+ */
+ if (size < 256)
+ return -1;
+
+ /* Print request line or status line depending on message type */
+ if (msg->type == PJSIP_REQUEST_MSG) {
+ pjsip_uri *uri;
+
+ /* Add method. */
+ len = msg->line.req.method.name.slen;
+ pj_memcpy(p, msg->line.req.method.name.ptr, len);
+ p += len;
+ *p++ = ' ';
+
+ /* Add URI */
+ uri = pjsip_uri_get_uri(msg->line.req.uri);
+ len = pjsip_uri_print( PJSIP_URI_IN_REQ_URI, uri, p, end-p);
+ if (len < 1)
+ return -1;
+ p += len;
+
+ /* Add ' SIP/2.0' */
+ if (end-p < 16)
+ return -1;
+ pj_memcpy(p, " SIP/2.0\r\n", 10);
+ p += 10;
+
+ } else {
+
+ /* Add 'SIP/2.0 ' */
+ pj_memcpy(p, "SIP/2.0 ", 8);
+ p += 8;
+
+ /* Add status code. */
+ len = pj_utoa(msg->line.status.code, p);
+ p += len;
+ *p++ = ' ';
+
+ /* Add reason text. */
+ len = msg->line.status.reason.slen;
+ pj_memcpy(p, msg->line.status.reason.ptr, len );
+ p += len;
+
+ /* Add newline. */
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+
+ /* Print each of the headers. */
+ for (hdr=msg->hdr.next; hdr!=&msg->hdr; hdr=hdr->next) {
+ len = (*hdr->vptr->print_on)(hdr, p, end-p);
+ if (len < 1)
+ return -1;
+ p += len;
+
+ if (p+3 >= end)
+ return -1;
+
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+
+ /* Process message body. */
+ if (msg->body) {
+ pj_str_t ctype_hdr = { "Content-Type: ", 14};
+ int len;
+ const pjsip_media_type *media = &msg->body->content_type;
+ char *clen_pos;
+
+ /* Add Content-Type header. */
+ if ( (end-p) < 24+media->type.slen+media->subtype.slen+media->param.slen) {
+ return -1;
+ }
+ pj_memcpy(p, ctype_hdr.ptr, ctype_hdr.slen);
+ p += ctype_hdr.slen;
+ p += print_media_type(p, media);
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Add Content-Length header. */
+ if ((end-p) < clen_hdr.slen+12+2) {
+ return -1;
+ }
+ pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
+ p += clen_hdr.slen;
+
+ /* Print blanks after "Content-Type:", this is where we'll put
+ * the content length value after we know the length of the
+ * body.
+ */
+ pj_memset(p, ' ', 12);
+ clen_pos = p;
+ p += 12;
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Add blank newline. */
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* Print the message body itself. */
+ len = (*msg->body->print_body)(msg->body, p, end-p);
+ if (len < 0) {
+ return -1;
+ }
+ p += len;
+
+ /* Now that we have the length of the body, print this to the
+ * Content-Length header.
+ */
+ len = pj_utoa(len, clen_pos);
+ clen_pos[len] = ' ';
+
+ } else {
+ /* There's no message body.
+ * Add Content-Length with zero value.
+ */
+ if ((end-p) < clen_hdr.slen+8) {
+ return -1;
+ }
+ pj_memcpy(p, clen_hdr.ptr, clen_hdr.slen);
+ p += clen_hdr.slen;
+ *p++ = '0';
+ *p++ = '\r';
+ *p++ = '\n';
+ *p++ = '\r';
+ *p++ = '\n';
+ }
+
+ *p = '\0';
+ return p-buf;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+PJ_DEF(void*) pjsip_hdr_clone( pj_pool_t *pool, const void *hdr_ptr )
+{
+ const pjsip_hdr *hdr = hdr_ptr;
+ return (*hdr->vptr->clone)(pool, hdr_ptr);
+}
+
+
+PJ_DEF(void*) pjsip_hdr_shallow_clone( pj_pool_t *pool, const void *hdr_ptr )
+{
+ const pjsip_hdr *hdr = hdr_ptr;
+ return (*hdr->vptr->shallow_clone)(pool, hdr_ptr);
+}
+
+PJ_DEF(int) pjsip_hdr_print_on( void *hdr_ptr, char *buf, pj_size_t len)
+{
+ pjsip_hdr *hdr = hdr_ptr;
+ return (*hdr->vptr->print_on)(hdr_ptr, buf, len);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Status/Reason Phrase
+ */
+
+PJ_DEF(const pj_str_t*) pjsip_get_status_text(int code)
+{
+ static int is_initialized;
+ if (is_initialized == 0) {
+ is_initialized = 1;
+ init_status_phrase();
+ }
+
+ return (code>=100 && code<(sizeof(status_phrase)/sizeof(status_phrase[0]))) ?
+ &status_phrase[code] : &status_phrase[0];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic pjsip_hdr_names/hvalue header.
+ */
+
+static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_string_hdr *hdr);
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_string_hdr *hdr );
+
+static pjsip_hdr_vptr generic_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_generic_string_hdr_shallow_clone,
+ (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 )
+{
+ pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_string_hdr));
+ 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;
+ 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)
+{
+ pjsip_generic_string_hdr *hdr = pjsip_generic_string_hdr_create(pool, hname);
+ pj_strdup(pool, &hdr->hvalue, hvalue);
+ return hdr;
+}
+
+static int pjsip_generic_string_hdr_print( pjsip_generic_string_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+
+ if ((pj_ssize_t)size < hdr->name.slen + hdr->hvalue.slen + 5)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+ pj_memcpy(p, hdr->hvalue.ptr, hdr->hvalue.slen);
+ p += hdr->hvalue.slen;
+ *p = '\0';
+
+ return p - buf;
+}
+
+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);
+
+ hdr->type = rhs->type;
+ hdr->sname = hdr->name;
+ pj_strdup( pool, &hdr->hvalue, &rhs->hvalue);
+ return hdr;
+}
+
+static pjsip_generic_string_hdr* pjsip_generic_string_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_string_hdr *rhs )
+{
+ pjsip_generic_string_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic pjsip_hdr_names/integer value header.
+ */
+
+static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *hdr);
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *hdr );
+
+static pjsip_hdr_vptr generic_int_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_generic_int_hdr_shallow_clone,
+ (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 )
+{
+ pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_int_hdr));
+ 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;
+ 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)
+{
+ pjsip_generic_int_hdr *hdr = pjsip_generic_int_hdr_create(pool, hname);
+ hdr->ivalue = value;
+ return hdr;
+}
+
+static int pjsip_generic_int_hdr_print( pjsip_generic_int_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+
+ if ((pj_ssize_t)size < hdr->name.slen + 15)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ p += pj_utoa(hdr->ivalue, p);
+
+ return p - buf;
+}
+
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *rhs)
+{
+ pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+static pjsip_generic_int_hdr* pjsip_generic_int_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_int_hdr *rhs )
+{
+ pjsip_generic_int_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Generic array header.
+ */
+static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *hdr);
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *hdr);
+
+static pjsip_hdr_vptr generic_array_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_generic_array_hdr_shallow_clone,
+ (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)
+{
+ pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_generic_array_hdr));
+ init_hdr(hdr, PJSIP_H_OTHER, &generic_array_hdr_vptr);
+ if (hnames) {
+ pj_strdup(pool, &hdr->name, hnames);
+ hdr->sname = hdr->name;
+ }
+ hdr->count = 0;
+ return hdr;
+
+}
+
+static int pjsip_generic_array_hdr_print( pjsip_generic_array_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf, *endbuf = buf+size;
+
+ copy_advance(p, hdr->name);
+ *p++ = ':';
+ *p++ = ' ';
+
+ if (hdr->count > 0) {
+ unsigned i;
+ int printed;
+ copy_advance(p, hdr->values[0]);
+ for (i=1; i<hdr->count; ++i) {
+ copy_advance_pair(p, ", ", 2, hdr->values[i]);
+ }
+ }
+
+ return p - buf;
+}
+
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *rhs)
+{
+ unsigned i;
+ pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ for (i=0; i<rhs->count; ++i) {
+ pj_strdup(pool, &hdr->values[i], &rhs->values[i]);
+ }
+
+ return hdr;
+}
+
+
+static pjsip_generic_array_hdr* pjsip_generic_array_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_generic_array_hdr *rhs)
+{
+ pjsip_generic_array_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Accept header.
+ */
+PJ_DEF(pjsip_accept_hdr*) pjsip_accept_hdr_create(pj_pool_t *pool)
+{
+ pjsip_accept_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_ACCEPT, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Allow header.
+ */
+
+PJ_DEF(pjsip_allow_hdr*) pjsip_allow_hdr_create(pj_pool_t *pool)
+{
+ pjsip_allow_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_ALLOW, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Call-ID header.
+ */
+
+PJ_DEF(pjsip_cid_hdr*) pjsip_cid_hdr_create( pj_pool_t *pool )
+{
+ pjsip_cid_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_CALL_ID, &generic_hdr_vptr);
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Content-Length header.
+ */
+static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *hdr);
+#define pjsip_clen_hdr_shallow_clone pjsip_clen_hdr_clone
+
+static pjsip_hdr_vptr clen_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_clen_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_clen_hdr_print,
+};
+
+PJ_DEF(pjsip_clen_hdr*) pjsip_clen_hdr_create( pj_pool_t *pool )
+{
+ pjsip_clen_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_clen_hdr));
+ init_hdr(hdr, PJSIP_H_CONTENT_LENGTH, &clen_hdr_vptr);
+ hdr->len = 0;
+ return hdr;
+}
+
+static int pjsip_clen_hdr_print( pjsip_clen_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ int len;
+
+ if ((pj_ssize_t)size < hdr->name.slen + 14)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ len = pj_utoa(hdr->len, p);
+ p += len;
+ *p = '\0';
+
+ return p-buf;
+}
+
+static pjsip_clen_hdr* pjsip_clen_hdr_clone( pj_pool_t *pool, const pjsip_clen_hdr *rhs)
+{
+ pjsip_clen_hdr *hdr = pjsip_clen_hdr_create(pool);
+ hdr->len = rhs->len;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * CSeq header.
+ */
+static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr);
+static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool, const pjsip_cseq_hdr *hdr );
+
+static pjsip_hdr_vptr cseq_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_cseq_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_cseq_hdr_print,
+};
+
+PJ_DEF(pjsip_cseq_hdr*) pjsip_cseq_hdr_create( pj_pool_t *pool )
+{
+ pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(pjsip_cseq_hdr));
+ init_hdr(hdr, PJSIP_H_CSEQ, &cseq_hdr_vptr);
+ hdr->cseq = 0;
+ hdr->method.id = PJSIP_OTHER_METHOD;
+ hdr->method.name.ptr = NULL;
+ hdr->method.name.slen = 0;
+ return hdr;
+}
+
+static int pjsip_cseq_hdr_print( pjsip_cseq_hdr *hdr, char *buf, pj_size_t size)
+{
+ char *p = buf;
+ int len;
+
+ if ((pj_ssize_t)size < hdr->name.slen + hdr->method.name.slen + 15)
+ return -1;
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ len = pj_utoa(hdr->cseq, p);
+ p += len;
+ *p++ = ' ';
+
+ pj_memcpy(p, hdr->method.name.ptr, hdr->method.name.slen);
+ p += hdr->method.name.slen;
+
+ *p = '\0';
+
+ return p-buf;
+}
+
+static pjsip_cseq_hdr* pjsip_cseq_hdr_clone( pj_pool_t *pool,
+ const pjsip_cseq_hdr *rhs)
+{
+ pjsip_cseq_hdr *hdr = pjsip_cseq_hdr_create(pool);
+ hdr->cseq = rhs->cseq;
+ pjsip_method_copy(pool, &hdr->method, &rhs->method);
+ return hdr;
+}
+
+static pjsip_cseq_hdr* pjsip_cseq_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_cseq_hdr *rhs )
+{
+ pjsip_cseq_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Contact header.
+ */
+static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool, const pjsip_contact_hdr *hdr);
+static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool, const pjsip_contact_hdr *);
+
+static pjsip_hdr_vptr contact_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_contact_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_contact_hdr_print,
+};
+
+PJ_DEF(pjsip_contact_hdr*) pjsip_contact_hdr_create( pj_pool_t *pool )
+{
+ pjsip_contact_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_CONTACT, &contact_hdr_vptr);
+ hdr->expires = -1;
+ return hdr;
+}
+
+static int pjsip_contact_hdr_print( pjsip_contact_hdr *hdr, char *buf,
+ pj_size_t size)
+{
+ if (hdr->star) {
+ char *p = buf;
+ if ((pj_ssize_t)size < hdr->name.slen + 6)
+ return -1;
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+ *p++ = '*';
+ *p = '\0';
+ return p - buf;
+
+ } else {
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ printed = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, hdr->uri,
+ buf, endbuf-buf);
+ if (printed < 1)
+ return -1;
+
+ buf += printed;
+
+ if (hdr->q1000) {
+ if (buf+19 >= endbuf)
+ return -1;
+
+ /*
+ printed = sprintf(buf, ";q=%u.%03u",
+ hdr->q1000/1000, hdr->q1000 % 1000);
+ */
+ pj_memcpy(buf, ";q=", 3);
+ printed = pj_utoa(hdr->q1000/1000, buf+3);
+ buf += printed + 3;
+ *buf++ = '.';
+ printed = pj_utoa(hdr->q1000 % 1000, buf);
+ buf += printed;
+ }
+
+ if (hdr->expires >= 0) {
+ if (buf+23 >= endbuf)
+ return -1;
+
+ pj_memcpy(buf, ";expires=", 9);
+ printed = pj_utoa(hdr->expires, buf+9);
+ buf += printed + 9;
+ }
+
+ if (hdr->other_param.slen) {
+ copy_advance(buf, hdr->other_param);
+ }
+
+ *buf = '\0';
+ return buf-startbuf;
+ }
+}
+
+static pjsip_contact_hdr* pjsip_contact_hdr_clone( pj_pool_t *pool,
+ const pjsip_contact_hdr *rhs)
+{
+ pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(pool);
+
+ hdr->star = rhs->star;
+ if (hdr->star)
+ return hdr;
+
+ hdr->uri = pjsip_uri_clone(pool, rhs->uri);
+ hdr->q1000 = rhs->q1000;
+ hdr->expires = rhs->expires;
+ pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_contact_hdr* pjsip_contact_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_contact_hdr *rhs)
+{
+ pjsip_contact_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Content-Type header..
+ */
+static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool, const pjsip_ctype_hdr *hdr);
+#define pjsip_ctype_hdr_shallow_clone pjsip_ctype_hdr_clone
+
+static pjsip_hdr_vptr ctype_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_ctype_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_ctype_hdr_print,
+};
+
+PJ_DEF(pjsip_ctype_hdr*) pjsip_ctype_hdr_create( pj_pool_t *pool )
+{
+ pjsip_ctype_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_CONTENT_TYPE, &ctype_hdr_vptr);
+ return hdr;
+}
+
+static int print_media_type(char *buf, const pjsip_media_type *media)
+{
+ char *p = buf;
+
+ pj_memcpy(p, media->type.ptr, media->type.slen);
+ p += media->type.slen;
+ *p++ = '/';
+ pj_memcpy(p, media->subtype.ptr, media->subtype.slen);
+ p += media->subtype.slen;
+
+ if (media->param.slen) {
+ pj_memcpy(p, media->param.ptr, media->param.slen);
+ p += media->param.slen;
+ }
+
+ return p-buf;
+}
+
+static int pjsip_ctype_hdr_print( pjsip_ctype_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ char *p = buf;
+ int len;
+
+ if ((pj_ssize_t)size < hdr->name.slen +
+ hdr->media.type.slen + hdr->media.subtype.slen +
+ hdr->media.param.slen + 8)
+ {
+ return -1;
+ }
+
+ pj_memcpy(p, hdr->name.ptr, hdr->name.slen);
+ p += hdr->name.slen;
+ *p++ = ':';
+ *p++ = ' ';
+
+ len = print_media_type(p, &hdr->media);
+ p += len;
+
+ *p = '\0';
+ return p-buf;
+}
+
+static pjsip_ctype_hdr* pjsip_ctype_hdr_clone( pj_pool_t *pool,
+ const pjsip_ctype_hdr *rhs)
+{
+ pjsip_ctype_hdr *hdr = pjsip_ctype_hdr_create(pool);
+ pj_strdup(pool, &hdr->media.type, &rhs->media.type);
+ pj_strdup(pool, &hdr->media.param, &rhs->media.param);
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Expires header.
+ */
+PJ_DEF(pjsip_expires_hdr*) pjsip_expires_hdr_create( pj_pool_t *pool )
+{
+ pjsip_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_EXPIRES, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * To or From header.
+ */
+static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
+ char *buf, pj_size_t size);
+static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *hdr);
+static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *hdr);
+
+
+static pjsip_hdr_vptr fromto_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_fromto_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_fromto_hdr_print,
+};
+
+PJ_DEF(pjsip_from_hdr*) pjsip_from_hdr_create( pj_pool_t *pool )
+{
+ pjsip_from_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_FROM, &fromto_hdr_vptr);
+ return hdr;
+}
+
+PJ_DEF(pjsip_to_hdr*) pjsip_to_hdr_create( pj_pool_t *pool )
+{
+ pjsip_to_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_TO, &fromto_hdr_vptr);
+ return hdr;
+}
+
+PJ_DEF(pjsip_from_hdr*) pjsip_fromto_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 )
+{
+ hdr->type = PJSIP_H_TO;
+ hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_TO];
+ return hdr;
+}
+
+static int pjsip_fromto_hdr_print( pjsip_fromto_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ printed = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, hdr->uri, buf, endbuf-buf);
+ if (printed < 1)
+ return -1;
+
+ buf += printed;
+
+ copy_advance_pair(buf, ";tag=", 5, hdr->tag);
+ if (hdr->other_param.slen)
+ copy_advance(buf, hdr->other_param);
+
+ return buf-startbuf;
+}
+
+static pjsip_fromto_hdr* pjsip_fromto_hdr_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *rhs)
+{
+ pjsip_fromto_hdr *hdr = pjsip_from_hdr_create(pool);
+
+ hdr->type = rhs->type;
+ hdr->name = rhs->name;
+ hdr->sname = rhs->sname;
+ hdr->uri = pjsip_uri_clone(pool, rhs->uri);
+ pj_strdup( pool, &hdr->tag, &rhs->tag);
+ pj_strdup( pool, &hdr->other_param, &rhs->other_param);
+
+ return hdr;
+}
+
+static pjsip_fromto_hdr* pjsip_fromto_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_fromto_hdr *rhs)
+{
+ pjsip_fromto_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Max-Forwards header.
+ */
+PJ_DEF(pjsip_max_forwards_hdr*) pjsip_max_forwards_hdr_create(pj_pool_t *pool)
+{
+ pjsip_max_forwards_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_MAX_FORWARDS, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Min-Expires header.
+ */
+PJ_DECL(pjsip_min_expires_hdr*) pjsip_min_expires_hdr_create(pj_pool_t *pool)
+{
+ pjsip_min_expires_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_MIN_EXPIRES, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Record-Route and Route header.
+ */
+static int pjsip_routing_hdr_print( pjsip_routing_hdr *r, char *buf, pj_size_t size );
+static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
+static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool, const pjsip_routing_hdr *r );
+
+static pjsip_hdr_vptr routing_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_routing_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_routing_hdr_print,
+};
+
+PJ_DEF(pjsip_rr_hdr*) pjsip_rr_hdr_create( pj_pool_t *pool )
+{
+ pjsip_rr_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_RECORD_ROUTE, &routing_hdr_vptr);
+ pjsip_name_addr_init(&hdr->name_addr);
+ pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
+ return hdr;
+}
+
+PJ_DEF(pjsip_route_hdr*) pjsip_route_hdr_create( pj_pool_t *pool )
+{
+ pjsip_route_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_ROUTE, &routing_hdr_vptr);
+ pjsip_name_addr_init(&hdr->name_addr);
+ pj_memset(&hdr->other_param, 0, sizeof(hdr->other_param));
+ return hdr;
+}
+
+PJ_DEF(pjsip_rr_hdr*) pjsip_routing_hdr_set_rr( pjsip_routing_hdr *hdr )
+{
+ hdr->type = PJSIP_H_RECORD_ROUTE;
+ hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_RECORD_ROUTE];
+ return hdr;
+}
+
+PJ_DEF(pjsip_route_hdr*) pjsip_routing_hdr_set_route( pjsip_routing_hdr *hdr )
+{
+ hdr->type = PJSIP_H_ROUTE;
+ hdr->name = hdr->sname = pjsip_hdr_names[PJSIP_H_ROUTE];
+ return hdr;
+}
+
+static int pjsip_routing_hdr_print( pjsip_routing_hdr *hdr,
+ char *buf, pj_size_t size )
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ printed = pjsip_uri_print(PJSIP_URI_IN_ROUTING_HDR, &hdr->name_addr, buf, endbuf-buf);
+ if (printed < 1)
+ return -1;
+ buf += printed;
+
+ if (hdr->other_param.slen) {
+ copy_advance(buf, hdr->other_param);
+ }
+
+ return buf-startbuf;
+}
+
+static pjsip_routing_hdr* pjsip_routing_hdr_clone( pj_pool_t *pool,
+ const pjsip_routing_hdr *rhs )
+{
+ pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+
+ init_hdr(hdr, rhs->type, rhs->vptr);
+ pjsip_name_addr_init(&hdr->name_addr);
+ pjsip_name_addr_assign(pool, &hdr->name_addr, &rhs->name_addr);
+ pj_strdup( pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_routing_hdr* pjsip_routing_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_routing_hdr *rhs )
+{
+ pjsip_routing_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Require header.
+ */
+PJ_DEF(pjsip_require_hdr*) pjsip_require_hdr_create(pj_pool_t *pool)
+{
+ pjsip_require_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_REQUIRE, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Retry-After header.
+ */
+PJ_DEF(pjsip_retry_after_hdr*) pjsip_retry_after_hdr_create(pj_pool_t *pool)
+{
+ pjsip_retry_after_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_RETRY_AFTER, &generic_int_hdr_vptr);
+ hdr->ivalue = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Supported header.
+ */
+PJ_DEF(pjsip_supported_hdr*) pjsip_supported_hdr_create(pj_pool_t *pool)
+{
+ pjsip_supported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_SUPPORTED, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Unsupported header.
+ */
+PJ_DEF(pjsip_unsupported_hdr*) pjsip_unsupported_hdr_create(pj_pool_t *pool)
+{
+ pjsip_unsupported_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_UNSUPPORTED, &generic_array_hdr_vptr);
+ hdr->count = 0;
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * Via header.
+ */
+static int pjsip_via_hdr_print( pjsip_via_hdr *hdr, char *buf, pj_size_t size);
+static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr);
+static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool, const pjsip_via_hdr *hdr );
+
+static pjsip_hdr_vptr via_hdr_vptr =
+{
+ (pjsip_hdr_clone_fptr) &pjsip_via_hdr_clone,
+ (pjsip_hdr_clone_fptr) &pjsip_via_hdr_shallow_clone,
+ (pjsip_hdr_print_fptr) &pjsip_via_hdr_print,
+};
+
+PJ_DEF(pjsip_via_hdr*) pjsip_via_hdr_create( pj_pool_t *pool )
+{
+ pjsip_via_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ init_hdr(hdr, PJSIP_H_VIA, &via_hdr_vptr);
+ hdr->sent_by.port = 5060;
+ hdr->ttl_param = -1;
+ hdr->rport_param = -1;
+ return hdr;
+}
+
+static int pjsip_via_hdr_print( pjsip_via_hdr *hdr,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+ pj_str_t sip_ver = { "SIP/2.0/", 8 };
+
+ if ((pj_ssize_t)size < hdr->name.slen + sip_ver.slen +
+ hdr->transport.slen + hdr->sent_by.host.slen + 12)
+ {
+ return -1;
+ }
+
+ /* pjsip_hdr_names */
+ copy_advance(buf, hdr->name);
+ *buf++ = ':';
+ *buf++ = ' ';
+
+ /* SIP/2.0/transport host:port */
+ pj_memcpy(buf, sip_ver.ptr, sip_ver.slen);
+ buf += sip_ver.slen;
+ pj_memcpy(buf, hdr->transport.ptr, hdr->transport.slen);
+ buf += hdr->transport.slen;
+ *buf++ = ' ';
+ pj_memcpy(buf, hdr->sent_by.host.ptr, hdr->sent_by.host.slen);
+ buf += hdr->sent_by.host.slen;
+ *buf++ = ':';
+ printed = pj_utoa(hdr->sent_by.port, buf);
+ buf += printed;
+
+ if (hdr->ttl_param >= 0) {
+ size = endbuf-buf;
+ if (size < 14)
+ return -1;
+ pj_memcpy(buf, ";ttl=", 5);
+ printed = pj_utoa(hdr->ttl_param, buf+5);
+ buf += printed + 5;
+ }
+
+ if (hdr->rport_param >= 0) {
+ size = endbuf-buf;
+ if (size < 14)
+ return -1;
+ pj_memcpy(buf, ";rport", 6);
+ buf += 6;
+ if (hdr->rport_param > 0) {
+ *buf++ = '=';
+ buf += pj_utoa(hdr->rport_param, buf);
+ }
+ }
+
+
+ copy_advance_pair(buf, ";maddr=", 7, hdr->maddr_param);
+ copy_advance_pair(buf, ";received=", 10, hdr->recvd_param);
+ copy_advance_pair(buf, ";branch=", 8, hdr->branch_param);
+ copy_advance(buf, hdr->other_param);
+
+ *buf = '\0';
+ return buf-startbuf;
+}
+
+static pjsip_via_hdr* pjsip_via_hdr_clone( pj_pool_t *pool,
+ const pjsip_via_hdr *rhs)
+{
+ pjsip_via_hdr *hdr = pjsip_via_hdr_create(pool);
+ pj_strdup(pool, &hdr->transport, &rhs->transport);
+ pj_strdup(pool, &hdr->sent_by.host, &rhs->sent_by.host);
+ hdr->sent_by.port = rhs->sent_by.port;
+ hdr->ttl_param = rhs->ttl_param;
+ hdr->rport_param = rhs->rport_param;
+ pj_strdup(pool, &hdr->maddr_param, &rhs->maddr_param);
+ pj_strdup(pool, &hdr->recvd_param, &rhs->recvd_param);
+ pj_strdup(pool, &hdr->branch_param, &rhs->branch_param);
+ pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ return hdr;
+}
+
+static pjsip_via_hdr* pjsip_via_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_via_hdr *rhs )
+{
+ pjsip_via_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
+ pj_memcpy(hdr, rhs, sizeof(*hdr));
+ return hdr;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+/*
+ * General purpose function to textual data in a SIP body.
+ */
+PJ_DEF(int) pjsip_print_text_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+ if (size < msg_body->len)
+ return -1;
+ pj_memcpy(buf, msg_body->data, msg_body->len);
+ return msg_body->len;
+}
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c index 63cc3b53..fc502e8d 100644 --- a/pjsip/src/pjsip/sip_parser.c +++ b/pjsip/src/pjsip/sip_parser.c @@ -1,532 +1,554 @@ -/* $Id$ - */ -#include <pjsip/sip_parser.h> -#include <pjsip/sip_uri.h> -#include <pjsip/sip_msg.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_msg.h>
#include <pjsip/sip_auth_parser.h>
#include <pjsip/sip_errno.h>
-#include <pjsip/sip_transport.h> /* rdata structure */ -#include <pjlib-util/scanner.h> -#include <pj/except.h> -#include <pj/log.h> -#include <pj/hash.h> -#include <pj/os.h> -#include <pj/pool.h> -#include <pj/string.h> -#include <pj/ctype.h> +#include <pjsip/sip_transport.h> /* rdata structure */
+#include <pjlib-util/scanner.h>
+#include <pj/except.h>
+#include <pj/log.h>
+#include <pj/hash.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <pj/ctype.h>
#include <pj/assert.h>
- -#define RESERVED ";/?:@&=+$," -#define MARK "-_.!~*'()" -#define ESCAPED "%" -#define USER "&=+$,;?/" -#define PASS "&=+$," +
+#define RESERVED ";/?:@&=+$,"
+#define MARK "-_.!~*'()"
+#define ESCAPED "%"
+#define USER "&=+$,;?/"
+#define PASS "&=+$,"
#define TOKEN "-.!%*_=`'~+" /* '+' is because of application/pidf+xml
- * in Content-Type! */ -#define HOST "_-." -#define HEX_DIGIT "abcdefABCDEF" -#define PARAM_CHAR "[]/:&+$" MARK "%" - -#define PJSIP_VERSION "SIP/2.0" -#define PJSIP_SYN_ERR_EXCEPTION 1 - -#define UNREACHED(expr) - -typedef struct handler_rec -{ - char hname[PJSIP_MAX_HNAME_LEN+1]; - pj_size_t hname_len; - pj_uint32_t hname_hash; - pjsip_parse_hdr_func *handler; -} handler_rec; - -static handler_rec handler[127]; -static unsigned handler_count; -static int parser_is_initialized; - - -/* - * Global vars (also extern). - */ -const pj_str_t pjsip_USER_STR = { "user", 4}; -const pj_str_t pjsip_METHOD_STR = { "method", 6}; -const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9}; -const pj_str_t pjsip_MADDR_STR = { "maddr", 5 }; -const pj_str_t pjsip_LR_STR = { "lr", 2 }; -const pj_str_t pjsip_SIP_STR = { "sip", 3 }; -const pj_str_t pjsip_SIPS_STR = { "sips", 4 }; -const pj_str_t pjsip_TEL_STR = { "tel", 3 }; -const pj_str_t pjsip_BRANCH_STR = { "branch", 6 }; -const pj_str_t pjsip_TTL_STR = { "ttl", 3 }; -const pj_str_t pjsip_PNAME_STR = { "received", 8 }; -const pj_str_t pjsip_Q_STR = { "q", 1 }; -const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 }; -const pj_str_t pjsip_TAG_STR = { "tag", 3 }; -const pj_str_t pjsip_RPORT_STR = { "rport", 5}; + * in Content-Type! */
+#define HOST "_-."
+#define HEX_DIGIT "abcdefABCDEF"
+#define PARAM_CHAR "[]/:&+$" MARK "%"
+
+#define PJSIP_VERSION "SIP/2.0"
+#define PJSIP_SYN_ERR_EXCEPTION 1
+
+#define UNREACHED(expr)
+
+typedef struct handler_rec
+{
+ char hname[PJSIP_MAX_HNAME_LEN+1];
+ pj_size_t hname_len;
+ pj_uint32_t hname_hash;
+ pjsip_parse_hdr_func *handler;
+} handler_rec;
+
+static handler_rec handler[127];
+static unsigned handler_count;
+static int parser_is_initialized;
+
+
+/*
+ * Global vars (also extern).
+ */
+const pj_str_t pjsip_USER_STR = { "user", 4};
+const pj_str_t pjsip_METHOD_STR = { "method", 6};
+const pj_str_t pjsip_TRANSPORT_STR = { "transport", 9};
+const pj_str_t pjsip_MADDR_STR = { "maddr", 5 };
+const pj_str_t pjsip_LR_STR = { "lr", 2 };
+const pj_str_t pjsip_SIP_STR = { "sip", 3 };
+const pj_str_t pjsip_SIPS_STR = { "sips", 4 };
+const pj_str_t pjsip_TEL_STR = { "tel", 3 };
+const pj_str_t pjsip_BRANCH_STR = { "branch", 6 };
+const pj_str_t pjsip_TTL_STR = { "ttl", 3 };
+const pj_str_t pjsip_PNAME_STR = { "received", 8 };
+const pj_str_t pjsip_Q_STR = { "q", 1 };
+const pj_str_t pjsip_EXPIRES_STR = { "expires", 7 };
+const pj_str_t pjsip_TAG_STR = { "tag", 3 };
+const pj_str_t pjsip_RPORT_STR = { "rport", 5};
/* Character Input Specification buffer. */
static pj_cis_buf_t cis_buf;
-/* Character Input Specifications. */ -pj_cis_t pjsip_HOST_SPEC, /* For scanning host part. */ - pjsip_DIGIT_SPEC, /* Decimal digits */ - pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */ - pjsip_ALNUM_SPEC, /* Decimal + Alpha. */ - pjsip_TOKEN_SPEC, /* Token. */ - pjsip_HEX_SPEC, /* Hexadecimal digits. */ +/* Character Input Specifications. */
+pj_cis_t pjsip_HOST_SPEC, /* For scanning host part. */
+ pjsip_DIGIT_SPEC, /* Decimal digits */
+ pjsip_ALPHA_SPEC, /* Alpha (A-Z, a-z) */
+ pjsip_ALNUM_SPEC, /* Decimal + Alpha. */
+ pjsip_TOKEN_SPEC, /* Token. */
+ pjsip_HEX_SPEC, /* Hexadecimal digits. */
pjsip_PARAM_CHAR_SPEC, /* For scanning pname (or pvalue when
- * it's not quoted.) */ - pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */ - pjsip_PASSWD_SPEC, /* Password. */ - pjsip_USER_SPEC, /* User */ - pjsip_ARRAY_ELEMENTS, /* Array separator. */ - pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/ + * it's not quoted.) */
+ pjsip_PROBE_USER_HOST_SPEC, /* Hostname characters. */
+ pjsip_PASSWD_SPEC, /* Password. */
+ pjsip_USER_SPEC, /* User */
+ pjsip_ARRAY_ELEMENTS, /* Array separator. */
+ pjsip_NEWLINE_OR_EOF_SPEC, /* For eating up header.*/
pjsip_DISPLAY_SCAN_SPEC; /* Used when searching for display name
- * in URL. */ - - -/* - * Forward decl. - */ -static pjsip_msg * int_parse_msg( pjsip_parse_ctx *ctx, - pjsip_parser_err_report *err_list); -static void int_parse_param( pj_scanner *scanner, - pj_str_t *pname, - pj_str_t *pvalue); -static void int_parse_req_line( pj_scanner *scanner, - pj_pool_t *pool, - pjsip_request_line *req_line); -static int int_is_next_user( pj_scanner *scanner); -static void int_parse_status_line( pj_scanner *scanner, - pjsip_status_line *line); -static void int_parse_user_pass( pj_scanner *scanner, - pj_str_t *user, - pj_str_t *pass); -static void int_parse_uri_host_port( pj_scanner *scanner, - pj_str_t *p_host, - int *p_port); -static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner, + * in URL. */
+
+
+/*
+ * Forward decl.
+ */
+static pjsip_msg * int_parse_msg( pjsip_parse_ctx *ctx,
+ pjsip_parser_err_report *err_list);
+static void int_parse_param( pj_scanner *scanner,
+ pj_str_t *pname,
+ pj_str_t *pvalue);
+static void int_parse_req_line( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pjsip_request_line *req_line);
+static int int_is_next_user( pj_scanner *scanner);
+static void int_parse_status_line( pj_scanner *scanner,
+ pjsip_status_line *line);
+static void int_parse_user_pass( pj_scanner *scanner,
+ pj_str_t *user,
+ pj_str_t *pass);
+static void int_parse_uri_host_port( pj_scanner *scanner,
+ pj_str_t *p_host,
+ int *p_port);
+static pjsip_uri * int_parse_uri_or_name_addr( pj_scanner *scanner,
pj_pool_t *pool,
- unsigned option); -static pjsip_url * int_parse_sip_url( pj_scanner *scanner, - pj_pool_t *pool, - pj_bool_t parse_params); + unsigned option);
+static pjsip_url * int_parse_sip_url( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_bool_t parse_params);
static pjsip_name_addr *
- int_parse_name_addr( pj_scanner *scanner, - pj_pool_t *pool ); + int_parse_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool );
static void parse_hdr_end( pj_scanner *scanner );
- -static pjsip_hdr* parse_hdr_accept( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_allow( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_call_id( pjsip_parse_ctx *ctx); -static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx); -static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_expires( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx); -static pjsip_hdr* parse_hdr_min_expires( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_retry_after( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_supported( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_unsupported( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx ); -static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx); - -/* Convert non NULL terminated string to integer. */ +
+static pjsip_hdr* parse_hdr_accept( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_allow( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_call_id( pjsip_parse_ctx *ctx);
+static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx);
+static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_expires( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_max_forwards( pjsip_parse_ctx *ctx);
+static pjsip_hdr* parse_hdr_min_expires( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_retry_after( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_supported( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_unsupported( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx );
+static pjsip_hdr* parse_hdr_generic_string( pjsip_parse_ctx *ctx);
+
+/* Convert non NULL terminated string to integer. */
static unsigned long pj_strtoul_mindigit(const pj_str_t *str,
- unsigned mindig) -{ - unsigned long value; - unsigned i; - - value = 0; - for (i=0; i<(unsigned)str->slen; ++i) { - value = value * 10 + (str->ptr[i] - '0'); - } - for (; i<mindig; ++i) { - value = value * 10; - } - return value; -} - -/* Case insensitive comparison */ -#define parser_stricmp(str1, str2) pj_stricmp(&str1, &str2) - - -/* Syntax error handler for parser. */ -static void on_syntax_error(pj_scanner *scanner) -{ - PJ_UNUSED_ARG(scanner); - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); -} - -/* Concatenate unrecognized params into single string. */ -void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool, + unsigned mindig)
+{
+ unsigned long value;
+ unsigned i;
+
+ value = 0;
+ for (i=0; i<(unsigned)str->slen; ++i) {
+ value = value * 10 + (str->ptr[i] - '0');
+ }
+ for (; i<mindig; ++i) {
+ value = value * 10;
+ }
+ return value;
+}
+
+/* Case insensitive comparison */
+#define parser_stricmp(str1, str2) pj_stricmp(&str1, &str2)
+
+
+/* Syntax error handler for parser. */
+static void on_syntax_error(pj_scanner *scanner)
+{
+ PJ_UNUSED_ARG(scanner);
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+}
+
+/* Concatenate unrecognized params into single string. */
+void pjsip_concat_param_imp( pj_str_t *param, pj_pool_t *pool,
const pj_str_t *pname, const pj_str_t *pvalue,
- int sepchar) -{ - char *new_param, *p; - int len; - - len = param->slen + pname->slen + pvalue->slen + 3; - p = new_param = pj_pool_alloc(pool, len); - - if (param->slen) { - int old_len = param->slen; - pj_memcpy(p, param->ptr, old_len); - p += old_len; - } - *p++ = (char)sepchar; - pj_memcpy(p, pname->ptr, pname->slen); - p += pname->slen; - - if (pvalue->slen) { - *p++ = '='; - pj_memcpy(p, pvalue->ptr, pvalue->slen); - p += pvalue->slen; - } - - *p = '\0'; - - param->ptr = new_param; - param->slen = p - new_param; -} - -/* Concatenate unrecognized params into single string. */ -static void concat_param( pj_str_t *param, pj_pool_t *pool, - const pj_str_t *pname, const pj_str_t *pvalue ) -{ - pjsip_concat_param_imp(param, pool, pname, pvalue, ';'); -} - -/* Initialize static properties of the parser. */ -static pj_status_t init_parser() -{ + int sepchar)
+{
+ char *new_param, *p;
+ int len;
+
+ len = param->slen + pname->slen + pvalue->slen + 3;
+ p = new_param = pj_pool_alloc(pool, len);
+
+ if (param->slen) {
+ int old_len = param->slen;
+ pj_memcpy(p, param->ptr, old_len);
+ p += old_len;
+ }
+ *p++ = (char)sepchar;
+ pj_memcpy(p, pname->ptr, pname->slen);
+ p += pname->slen;
+
+ if (pvalue->slen) {
+ *p++ = '=';
+ pj_memcpy(p, pvalue->ptr, pvalue->slen);
+ p += pvalue->slen;
+ }
+
+ *p = '\0';
+
+ param->ptr = new_param;
+ param->slen = p - new_param;
+}
+
+/* Concatenate unrecognized params into single string. */
+static void concat_param( pj_str_t *param, pj_pool_t *pool,
+ const pj_str_t *pname, const pj_str_t *pvalue )
+{
+ pjsip_concat_param_imp(param, pool, pname, pvalue, ';');
+}
+
+/* Initialize static properties of the parser. */
+static pj_status_t init_parser()
+{
static int initialized;
- pj_status_t status; + pj_status_t status;
+
+ if (initialized)
+ return PJ_SUCCESS;
- if (initialized) - return PJ_SUCCESS; - - initialized = 1; + initialized = 1;
pj_cis_buf_init(&cis_buf);
status = pj_cis_init(&cis_buf, &pjsip_DIGIT_SPEC);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- pj_cis_add_num(&pjsip_DIGIT_SPEC); + pj_cis_add_num(&pjsip_DIGIT_SPEC);
status = pj_cis_init(&cis_buf, &pjsip_ALPHA_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_alpha( &pjsip_ALPHA_SPEC ); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_alpha( &pjsip_ALPHA_SPEC );
status = pj_cis_init(&cis_buf, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_alpha( &pjsip_ALNUM_SPEC ); - pj_cis_add_num( &pjsip_ALNUM_SPEC ); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_alpha( &pjsip_ALNUM_SPEC );
+ pj_cis_add_num( &pjsip_ALNUM_SPEC );
status = pj_cis_init(&cis_buf, &pjsip_NEWLINE_OR_EOF_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str(&pjsip_NEWLINE_OR_EOF_SPEC, "\r\n"); - //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str(&pjsip_NEWLINE_OR_EOF_SPEC, "\r\n");
+ //pj_cs_set(pjsip_NEWLINE_OR_EOF_SPEC, 0);
status = pj_cis_init(&cis_buf, &pjsip_ARRAY_ELEMENTS);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_ARRAY_ELEMENTS, ",\r\n"); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_ARRAY_ELEMENTS, ",\r\n");
status = pj_cis_dup(&pjsip_TOKEN_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_TOKEN_SPEC, TOKEN); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_TOKEN_SPEC, TOKEN);
status = pj_cis_dup(&pjsip_HOST_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_HOST_SPEC, HOST); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_HOST_SPEC, HOST);
status = pj_cis_dup(&pjsip_HEX_SPEC, &pjsip_DIGIT_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_HEX_SPEC, HEX_DIGIT); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_HEX_SPEC, HEX_DIGIT);
status = pj_cis_dup(&pjsip_PARAM_CHAR_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str(&pjsip_PARAM_CHAR_SPEC, PARAM_CHAR); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str(&pjsip_PARAM_CHAR_SPEC, PARAM_CHAR);
status = pj_cis_dup(&pjsip_USER_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_USER_SPEC, MARK ESCAPED USER ); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_USER_SPEC, MARK ESCAPED USER );
status = pj_cis_dup(&pjsip_PASSWD_SPEC, &pjsip_ALNUM_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_PASSWD_SPEC, MARK ESCAPED PASS); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_PASSWD_SPEC, MARK ESCAPED PASS);
status = pj_cis_init(&cis_buf, &pjsip_PROBE_USER_HOST_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_PROBE_USER_HOST_SPEC, "@ \n>"); - pj_cis_invert( &pjsip_PROBE_USER_HOST_SPEC ); + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_PROBE_USER_HOST_SPEC, "@ \n>");
+ pj_cis_invert( &pjsip_PROBE_USER_HOST_SPEC );
status = pj_cis_init(&cis_buf, &pjsip_DISPLAY_SCAN_SPEC);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - pj_cis_add_str( &pjsip_DISPLAY_SCAN_SPEC, ":\r\n<"); - + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+ pj_cis_add_str( &pjsip_DISPLAY_SCAN_SPEC, ":\r\n<");
+
status = pjsip_register_hdr_parser( "Accept", NULL, &parse_hdr_accept);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Allow", NULL, &parse_hdr_allow);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Call-ID", NULL, &parse_hdr_call_id);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Contact", "m", &parse_hdr_contact);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Content-Length", NULL,
&parse_hdr_content_len);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Content-Type", NULL,
&parse_hdr_content_type);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "CSeq", NULL, &parse_hdr_cseq);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Expires", NULL, &parse_hdr_expires);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "From", "f", &parse_hdr_from);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Max-Forwards", NULL,
&parse_hdr_max_forwards);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Min-Expires", NULL,
&parse_hdr_min_expires);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Record-Route", NULL, &parse_hdr_rr);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Route", NULL, &parse_hdr_route);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Require", NULL, &parse_hdr_require);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Retry-After", NULL,
&parse_hdr_retry_after);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Supported", "k",
&parse_hdr_supported);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "To", "t", &parse_hdr_to);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Unsupported", NULL,
&parse_hdr_unsupported);
PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
- +
status = pjsip_register_hdr_parser( "Via", NULL, &parse_hdr_via);
- PJ_ASSERT_RETURN(status == PJ_SUCCESS, status); - - /* Register auth parser. */ - status = pjsip_auth_init_parser(); -
- return status; -} - -static void init_sip_parser(void) -{ - if (!parser_is_initialized) { - /* Prevent race cond. */ - pj_enter_critical_section(); - if (!parser_is_initialized) { - init_parser(); - parser_is_initialized = 1; - } - pj_leave_critical_section(); - } -} - -/* Compare the handler record with header name, and return: - * - 0 if handler match. - * - <0 if handler is 'less' than the header name. - * - >0 if handler is 'greater' than header name. - */ -static int compare_handler( const handler_rec *r1, - const char *name, - pj_size_t name_len, - pj_uint32_t hash ) -{ - /* Compare length. */ - if (r1->hname_len < name_len) - return -1; - if (r1->hname_len > name_len) - return 1; - - /* Length is equal, compare hashed value. */ - if (r1->hname_hash < hash) - return -1; - if (r1->hname_hash > hash) - return 1; - - /* Equal length and equal hash. compare the strings. */ - return pj_native_strcmp(r1->hname, name); -} - -/* Register one handler for one header name. */ + PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
+
+ /* Register auth parser. */
+ status = pjsip_auth_init_parser();
+
+ return status;
+}
+
+static void init_sip_parser(void)
+{
+ if (!parser_is_initialized) {
+ /* Prevent race cond. */
+ pj_enter_critical_section();
+ if (!parser_is_initialized) {
+ init_parser();
+ parser_is_initialized = 1;
+ }
+ pj_leave_critical_section();
+ }
+}
+
+/* Compare the handler record with header name, and return:
+ * - 0 if handler match.
+ * - <0 if handler is 'less' than the header name.
+ * - >0 if handler is 'greater' than header name.
+ */
+static int compare_handler( const handler_rec *r1,
+ const char *name,
+ pj_size_t name_len,
+ pj_uint32_t hash )
+{
+ /* Compare length. */
+ if (r1->hname_len < name_len)
+ return -1;
+ if (r1->hname_len > name_len)
+ return 1;
+
+ /* Length is equal, compare hashed value. */
+ if (r1->hname_hash < hash)
+ return -1;
+ if (r1->hname_hash > hash)
+ return 1;
+
+ /* Equal length and equal hash. compare the strings. */
+ return pj_native_strcmp(r1->hname, name);
+}
+
+/* Register one handler for one header name. */
static pj_status_t int_register_parser( const char *name,
- pjsip_parse_hdr_func *fptr ) -{ - unsigned pos; - handler_rec rec; - unsigned i; - - if (handler_count >= PJ_ARRAY_SIZE(handler)) { - return PJ_ETOOMANY; - } - - /* Initialize temporary handler. */ - rec.handler = fptr; - rec.hname_len = strlen(name); - if (rec.hname_len >= sizeof(rec.hname)) { - return PJ_ENAMETOOLONG; - } - /* Name is copied in lowercase. */ - for (i=0; i<rec.hname_len; ++i) { - rec.hname[i] = (char)pj_tolower(name[i]); - } - rec.hname[i] = '\0'; - /* Hash value is calculated from the lowercase name. */ - rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING); - - /* Get the pos to insert the new handler. */ - for (pos=0; pos < handler_count; ++pos) { - int d; + pjsip_parse_hdr_func *fptr )
+{
+ unsigned pos;
+ handler_rec rec;
+ unsigned i;
+
+ if (handler_count >= PJ_ARRAY_SIZE(handler)) {
+ return PJ_ETOOMANY;
+ }
+
+ /* Initialize temporary handler. */
+ rec.handler = fptr;
+ rec.hname_len = strlen(name);
+ if (rec.hname_len >= sizeof(rec.hname)) {
+ return PJ_ENAMETOOLONG;
+ }
+ /* Name is copied in lowercase. */
+ for (i=0; i<rec.hname_len; ++i) {
+ rec.hname[i] = (char)pj_tolower(name[i]);
+ }
+ rec.hname[i] = '\0';
+ /* Hash value is calculated from the lowercase name. */
+ rec.hname_hash = pj_hash_calc(0, rec.hname, PJ_HASH_KEY_STRING);
+
+ /* Get the pos to insert the new handler. */
+ for (pos=0; pos < handler_count; ++pos) {
+ int d;
d = compare_handler(&handler[pos], rec.hname, rec.hname_len,
- rec.hname_hash); - if (d == 0) { - pj_assert(0); - return PJ_EEXISTS; - } - if (d > 0) { - break; - } - } - - /* Shift handlers. */ - if (pos != handler_count) { + rec.hname_hash);
+ if (d == 0) {
+ pj_assert(0);
+ return PJ_EEXISTS;
+ }
+ if (d > 0) {
+ break;
+ }
+ }
+
+ /* Shift handlers. */
+ if (pos != handler_count) {
pj_memmove( &handler[pos+1], &handler[pos],
- (handler_count-pos)*sizeof(handler_rec)); - } - /* Add new handler. */ - pj_memcpy( &handler[pos], &rec, sizeof(handler_rec)); - ++handler_count; - - return PJ_SUCCESS; -} - -/* Register parser handler. If both header name and short name are valid, - * then two instances of handler will be registered. - */ -PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname, - const char *hshortname, - pjsip_parse_hdr_func *fptr) + (handler_count-pos)*sizeof(handler_rec));
+ }
+ /* Add new handler. */
+ pj_memcpy( &handler[pos], &rec, sizeof(handler_rec));
+ ++handler_count;
+
+ return PJ_SUCCESS;
+}
+
+/* Register parser handler. If both header name and short name are valid,
+ * then two instances of handler will be registered.
+ */
+PJ_DEF(pj_status_t) pjsip_register_hdr_parser( const char *hname,
+ const char *hshortname,
+ pjsip_parse_hdr_func *fptr)
{
pj_status_t status;
- status = int_register_parser(hname, fptr); - if (status != PJ_SUCCESS) { - return status; - } + status = int_register_parser(hname, fptr);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
if (hshortname) {
status = int_register_parser(hshortname, fptr);
- if (status != PJ_SUCCESS) - return status; - } - return PJ_SUCCESS; -} - -/* Find handler to parse the header name. */ -static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname) -{ - handler_rec *first; - char hname_copy[PJSIP_MAX_HNAME_LEN]; - pj_uint32_t hash; - int comp; - unsigned n; + if (status != PJ_SUCCESS)
+ return status;
+ }
+ return PJ_SUCCESS;
+}
+
+/* Find handler to parse the header name. */
+static pjsip_parse_hdr_func * find_handler(const pj_str_t *hname)
+{
+ handler_rec *first;
+ char hname_copy[PJSIP_MAX_HNAME_LEN];
+ pj_uint32_t hash;
+ int comp;
+ unsigned n;
if (hname->slen >= PJSIP_MAX_HNAME_LEN) {
pj_assert(!"Header name is too long!");
return NULL;
}
- - /* Calculate hash value while converting the header to lowercase. - * Don't assume that 'hname' is NULL terminated. - */ +
+ /* Calculate hash value while converting the header to lowercase.
+ * Don't assume that 'hname' is NULL terminated.
+ */
hash = pj_hash_calc_tolower(0, hname_copy, hname);
- hname_copy[hname->slen] = '\0'; - - /* Binary search for the handler. */ - comp = -1; - first = &handler[0]; - n = handler_count; - for (; n > 0; ) { - unsigned half = n / 2; - handler_rec *mid = first + half; - - comp = compare_handler(mid, hname_copy, hname->slen, hash); - if (comp < 0) { - first = ++mid; - n -= half + 1; - } else if (comp==0) { - first = mid; - break; - } else { - n = half; - } - } - - return comp==0 ? first->handler : NULL; -} - -/* Public function to parse SIP message. */ + hname_copy[hname->slen] = '\0';
+
+ /* Binary search for the handler. */
+ comp = -1;
+ first = &handler[0];
+ n = handler_count;
+ for (; n > 0; ) {
+ unsigned half = n / 2;
+ handler_rec *mid = first + half;
+
+ comp = compare_handler(mid, hname_copy, hname->slen, hash);
+ if (comp < 0) {
+ first = ++mid;
+ n -= half + 1;
+ } else if (comp==0) {
+ first = mid;
+ break;
+ } else {
+ n = half;
+ }
+ }
+
+ return comp==0 ? first->handler : NULL;
+}
+
+/* Public function to parse SIP message. */
PJ_DEF(pjsip_msg*) pjsip_parse_msg( pj_pool_t *pool,
- char *buf, pj_size_t size, - pjsip_parser_err_report *err_list) -{ - pjsip_msg *msg = NULL; + char *buf, pj_size_t size,
+ pjsip_parser_err_report *err_list)
+{
+ pjsip_msg *msg = NULL;
pj_scanner scanner;
- pjsip_parse_ctx context; - PJ_USE_EXCEPTION; - - init_sip_parser(); - + pjsip_parse_ctx context;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error); + &on_syntax_error);
context.scanner = &scanner;
context.pool = pool;
context.rdata = NULL;
- - PJ_TRY { - msg = int_parse_msg(&context, err_list); - } - PJ_DEFAULT { - msg = NULL; - } - PJ_END - - pj_scan_fini(&scanner); - return msg; -} +
+ PJ_TRY {
+ msg = int_parse_msg(&context, err_list);
+ }
+ PJ_DEFAULT {
+ msg = NULL;
+ }
+ PJ_END
+
+ pj_scan_fini(&scanner);
+ return msg;
+}
/* Public function to parse as rdata.*/
PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size,
@@ -556,1151 +578,1151 @@ PJ_DEF(pjsip_msg *) pjsip_parse_rdata( char *buf, pj_size_t size, pj_scan_fini(&scanner);
return rdata->msg;
}
- -/* Determine if a message has been received. */ -PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size, - pj_bool_t is_datagram, pj_size_t *msg_size) -{ -#if PJ_HAS_TCP - const char *hdr_end; - const char *body_start; - const char *pos; - const char *line; - int content_length = -1; - - *msg_size = size; - - /* For datagram, the whole datagram IS the message. */ - if (is_datagram) { - return PJ_SUCCESS; - } - - - /* Find the end of header area by finding an empty line. */ - if ((pos = pj_native_strstr(buf, "\n\r\n")) == NULL) { - return PJSIP_EPARTIALMSG; - } - - hdr_end = pos+1; - body_start = pos+3; - - /* Find "Content-Length" header the hard way. */ - line = pj_native_strchr(buf, '\n'); - while (line && line < hdr_end-14) { - ++line; +
+/* Determine if a message has been received. */
+PJ_DEF(pj_bool_t) pjsip_find_msg( const char *buf, pj_size_t size,
+ pj_bool_t is_datagram, pj_size_t *msg_size)
+{
+#if PJ_HAS_TCP
+ const char *hdr_end;
+ const char *body_start;
+ const char *pos;
+ const char *line;
+ int content_length = -1;
+
+ *msg_size = size;
+
+ /* For datagram, the whole datagram IS the message. */
+ if (is_datagram) {
+ return PJ_SUCCESS;
+ }
+
+
+ /* Find the end of header area by finding an empty line. */
+ if ((pos = pj_native_strstr(buf, "\n\r\n")) == NULL) {
+ return PJSIP_EPARTIALMSG;
+ }
+
+ hdr_end = pos+1;
+ body_start = pos+3;
+
+ /* Find "Content-Length" header the hard way. */
+ line = pj_native_strchr(buf, '\n');
+ while (line && line < hdr_end-14) {
+ ++line;
if ( ((*line=='C' || *line=='c') &&
- pj_native_strncasecmp(line, "Content-Length", 14) == 0) || + pj_native_strncasecmp(line, "Content-Length", 14) == 0) ||
((*line=='l' || *line=='L') &&
- (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':'))) - { - /* Try to parse the header. */ - pj_scanner scanner; - PJ_USE_EXCEPTION; - - init_sip_parser(); - - pj_scan_init(&scanner, (char*)line, hdr_end-line, - PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error); - - PJ_TRY { - pj_str_t str_clen; - - /* Get "Content-Length" or "L" name */ - if (*line=='C' || *line=='c') - pj_scan_advance_n(&scanner, 14, PJ_TRUE); - else if (*line=='l' || *line=='L') - pj_scan_advance_n(&scanner, 1, PJ_TRUE); - - /* Get colon */ - if (pj_scan_get_char(&scanner) != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - /* Get number */ - pj_scan_get(&scanner, &pjsip_DIGIT_SPEC, &str_clen); - - /* Get newline. */ - pj_scan_get_newline(&scanner); - - /* Found a valid Content-Length header. */ - content_length = pj_strtoul(&str_clen); - } - PJ_END - - pj_scan_fini(&scanner); - } - - /* Found valid Content-Length? */ - if (content_length != -1) - break; - - /* Go to next line. */ - line = pj_native_strchr(line, '\n'); - } - - /* Found Content-Length? */ - if (content_length == -1) { - return PJSIP_EMISSINGHDR; - } - - /* Enough packet received? */ - *msg_size = (body_start - buf) + content_length; - return (*msg_size) <= size ? PJ_SUCCESS : PJSIP_EPARTIALMSG; -#else - PJ_UNUSED_ARG(buf); - PJ_UNUSED_ARG(is_datagram); - *msg_size = size; - return PJ_SUCCESS; -#endif -} - -/* Public function to parse URI */ -PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool, - char *buf, pj_size_t size, - unsigned option) -{ - PJ_USE_EXCEPTION; - pj_scanner scanner; - pjsip_uri *uri = NULL; - - init_sip_parser(); - - pj_scan_init(&scanner, buf, size, 0, &on_syntax_error); - - - PJ_TRY { - uri = int_parse_uri_or_name_addr(&scanner, pool, option); - } - PJ_END; - - /* Must have exhausted all inputs. */ + (*(line+1)==' ' || *(line+1)=='\t' || *(line+1)==':')))
+ {
+ /* Try to parse the header. */
+ pj_scanner scanner;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, (char*)line, hdr_end-line,
+ PJ_SCAN_AUTOSKIP_WS_HEADER, &on_syntax_error);
+
+ PJ_TRY {
+ pj_str_t str_clen;
+
+ /* Get "Content-Length" or "L" name */
+ if (*line=='C' || *line=='c')
+ pj_scan_advance_n(&scanner, 14, PJ_TRUE);
+ else if (*line=='l' || *line=='L')
+ pj_scan_advance_n(&scanner, 1, PJ_TRUE);
+
+ /* Get colon */
+ if (pj_scan_get_char(&scanner) != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ /* Get number */
+ pj_scan_get(&scanner, &pjsip_DIGIT_SPEC, &str_clen);
+
+ /* Get newline. */
+ pj_scan_get_newline(&scanner);
+
+ /* Found a valid Content-Length header. */
+ content_length = pj_strtoul(&str_clen);
+ }
+ PJ_END
+
+ pj_scan_fini(&scanner);
+ }
+
+ /* Found valid Content-Length? */
+ if (content_length != -1)
+ break;
+
+ /* Go to next line. */
+ line = pj_native_strchr(line, '\n');
+ }
+
+ /* Found Content-Length? */
+ if (content_length == -1) {
+ return PJSIP_EMISSINGHDR;
+ }
+
+ /* Enough packet received? */
+ *msg_size = (body_start - buf) + content_length;
+ return (*msg_size) <= size ? PJ_SUCCESS : PJSIP_EPARTIALMSG;
+#else
+ PJ_UNUSED_ARG(buf);
+ PJ_UNUSED_ARG(is_datagram);
+ *msg_size = size;
+ return PJ_SUCCESS;
+#endif
+}
+
+/* Public function to parse URI */
+PJ_DEF(pjsip_uri*) pjsip_parse_uri( pj_pool_t *pool,
+ char *buf, pj_size_t size,
+ unsigned option)
+{
+ PJ_USE_EXCEPTION;
+ pj_scanner scanner;
+ pjsip_uri *uri = NULL;
+
+ init_sip_parser();
+
+ pj_scan_init(&scanner, buf, size, 0, &on_syntax_error);
+
+
+ PJ_TRY {
+ uri = int_parse_uri_or_name_addr(&scanner, pool, option);
+ }
+ PJ_END;
+
+ /* Must have exhausted all inputs. */
if (pj_scan_is_eof(&scanner) || *scanner.curptr=='\r' ||
*scanner.curptr=='\n')
- { - /* Success. */ - pj_scan_fini(&scanner); - return uri; - } - - /* Still have some characters unparsed. */ - pj_scan_fini(&scanner); - return NULL; -} - -/* Generic function to print message body. - * This assumes that the 'data' member points to a contigous memory where the - * actual body is laid. - */ + {
+ /* Success. */
+ pj_scan_fini(&scanner);
+ return uri;
+ }
+
+ /* Still have some characters unparsed. */
+ pj_scan_fini(&scanner);
+ return NULL;
+}
+
+/* Generic function to print message body.
+ * This assumes that the 'data' member points to a contigous memory where the
+ * actual body is laid.
+ */
static int generic_print_body (pjsip_msg_body *msg_body,
- char *buf, pj_size_t size) -{ - pjsip_msg_body *body = msg_body; - if (size < body->len) - return 0; - - pj_memcpy (buf, body->data, body->len); - return body->len; -} - -/* Internal function to parse SIP message */ -static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx, - pjsip_parser_err_report *err_list) -{ - PJ_USE_EXCEPTION; - int ch; - pjsip_msg *msg; - pjsip_ctype_hdr *ctype_hdr = NULL; + char *buf, pj_size_t size)
+{
+ pjsip_msg_body *body = msg_body;
+ if (size < body->len)
+ return 0;
+
+ pj_memcpy (buf, body->data, body->len);
+ return body->len;
+}
+
+/* Internal function to parse SIP message */
+static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,
+ pjsip_parser_err_report *err_list)
+{
+ PJ_USE_EXCEPTION;
+ int ch;
+ pjsip_msg *msg;
+ pjsip_ctype_hdr *ctype_hdr = NULL;
pj_scanner *scanner = ctx->scanner;
pj_pool_t *pool = ctx->pool;
- - /* Skip leading newlines. */ - ch = *scanner->curptr; - while (ch=='\r' || ch=='\n') { - pj_scan_get_char(scanner); - ch = *scanner->curptr; - } - - msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG); - - /* Parse request or status line */ - if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) { - msg->type = PJSIP_RESPONSE_MSG; - int_parse_status_line( scanner, &msg->line.status ); - } else { - msg->type = PJSIP_REQUEST_MSG; - int_parse_req_line(scanner, pool, &msg->line.req ); - } - - /* Parse headers. */ - do { - pj_str_t hname; - pjsip_parse_hdr_func * handler; - pjsip_hdr *hdr = NULL; - - /* Get hname. */ - pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hname); - ch = pj_scan_get_char( scanner ); - if (ch != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - /* Find handler. */ - handler = find_handler(&hname); - - PJ_TRY { - /* Call the handler if found. - * If no handler is found, then treat the header as generic - * hname/hvalue pair. - */ - if (handler) { - hdr = (*handler)(ctx); - } else { - hdr = parse_hdr_generic_string(ctx); - hdr->type = PJSIP_H_OTHER; - hdr->name = hdr->sname = hname; - } - - /* Check if we've just parsed a Content-Type header. - * We will check for a message body if we've got Content-Type header. - */ - if (hdr->type == PJSIP_H_CONTENT_TYPE) { - ctype_hdr = (pjsip_ctype_hdr*)hdr; - } - - } - PJ_DEFAULT { - /* Exception was thrown during parsing. - * Skip until newline, and parse next header. - */ - pj_str_t token; - hdr = NULL; - +
+ /* Skip leading newlines. */
+ ch = *scanner->curptr;
+ while (ch=='\r' || ch=='\n') {
+ pj_scan_get_char(scanner);
+ ch = *scanner->curptr;
+ }
+
+ msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
+
+ /* Parse request or status line */
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) == 0) {
+ msg->type = PJSIP_RESPONSE_MSG;
+ int_parse_status_line( scanner, &msg->line.status );
+ } else {
+ msg->type = PJSIP_REQUEST_MSG;
+ int_parse_req_line(scanner, pool, &msg->line.req );
+ }
+
+ /* Parse headers. */
+ do {
+ pj_str_t hname;
+ pjsip_parse_hdr_func * handler;
+ pjsip_hdr *hdr = NULL;
+
+ /* Get hname. */
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hname);
+ ch = pj_scan_get_char( scanner );
+ if (ch != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ /* Find handler. */
+ handler = find_handler(&hname);
+
+ PJ_TRY {
+ /* Call the handler if found.
+ * If no handler is found, then treat the header as generic
+ * hname/hvalue pair.
+ */
+ if (handler) {
+ hdr = (*handler)(ctx);
+ } else {
+ hdr = parse_hdr_generic_string(ctx);
+ hdr->type = PJSIP_H_OTHER;
+ hdr->name = hdr->sname = hname;
+ }
+
+ /* Check if we've just parsed a Content-Type header.
+ * We will check for a message body if we've got Content-Type header.
+ */
+ if (hdr->type == PJSIP_H_CONTENT_TYPE) {
+ ctype_hdr = (pjsip_ctype_hdr*)hdr;
+ }
+
+ }
+ PJ_DEFAULT {
+ /* Exception was thrown during parsing.
+ * Skip until newline, and parse next header.
+ */
+ pj_str_t token;
+ hdr = NULL;
+
//PJ_LOG(4,("sipparser",
- // "Syntax error in line %d col %d (hname=%.*s)", - // scanner->line, scanner->col, hname.slen, hname.ptr)); - - if (err_list) { - pjsip_parser_err_report *err_info; - - err_info = pj_pool_alloc(pool, sizeof(*err_info)); - err_info->exception_code = PJ_GET_EXCEPTION(); - err_info->line = scanner->line; - err_info->col = scanner->col; - err_info->hname = hname; - - pj_list_insert_before(err_list, err_info); - } - - if (!pj_scan_is_eof(scanner)) { - pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &token); - parse_hdr_end(scanner); - } - } - PJ_END; - - if (hdr) { - /* Single parse of header line can produce multiple headers. - * For example, if one Contact: header contains Contact list - * separated by comma, then these Contacts will be split into - * different Contact headers. - * So here we must insert list instead of just insert one header. - */ - pj_list_insert_nodes_before(&msg->hdr, hdr); - } - - /* Parse until EOF or an empty line is found. */ - } while (!pj_scan_is_eof(scanner) && - *scanner->curptr != '\r' && *scanner->curptr != '\n'); - - /* If empty line is found, eat it. */ - if (!pj_scan_is_eof(scanner)) { - if (*scanner->curptr=='\r' || *scanner->curptr=='\n') { - pj_scan_get_newline(scanner); - } - } - - /* If we have Content-Type header, treat the rest of the message as body.*/ - if (ctype_hdr) { - pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); - pj_strdup(pool, &body->content_type.type, &ctype_hdr->media.type); - pj_strdup(pool, &body->content_type.subtype, &ctype_hdr->media.subtype); - pj_strdup(pool, &body->content_type.param, &ctype_hdr->media.param); - body->data = scanner->curptr; - body->len = scanner->end - scanner->curptr; - body->print_body = &generic_print_body; - - msg->body = body; - } - - return msg; -} - -/* Parse parameter (pname ["=" pvalue]). */ -void pjsip_parse_param_imp( pj_scanner *scanner, - pj_str_t *pname, pj_str_t *pvalue, - unsigned option) -{ - /* pname */ - pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pname); - - /* pvalue, if any */ - if (*scanner->curptr == '=') { - pj_scan_get_char(scanner); - /* pvalue can be a quoted string. */ - if (*scanner->curptr == '"') { - pj_scan_get_quote( scanner, '"', '"', pvalue); - if (option & PJSIP_PARSE_REMOVE_QUOTE) { - pvalue->ptr++; - pvalue->slen -= 2; - } - } else { - pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pvalue); - } - } else { - pvalue->ptr = NULL; - pvalue->slen = 0; - } -} - -/* Parse parameter (";" pname ["=" pvalue]). */ -static void int_parse_param( pj_scanner *scanner, - pj_str_t *pname, pj_str_t *pvalue) -{ - /* Get ';' character */ - pj_scan_get_char(scanner); - - /* Get pname and optionally pvalue */ - pjsip_parse_param_imp(scanner, pname, pvalue, 0); -} - -/* Parse host:port in URI. */ -static void int_parse_uri_host_port( pj_scanner *scanner, - pj_str_t *host, int *p_port) -{ - pj_scan_get( scanner, &pjsip_HOST_SPEC, host); - if (*scanner->curptr == ':') { - pj_str_t port; - pj_scan_get_char(scanner); - pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &port); - *p_port = pj_strtoul(&port); - } else { - *p_port = 0; - } -} - -/* Determine if the next token in an URI is a user specification. */ -static int int_is_next_user(pj_scanner *scanner) -{ - pj_str_t dummy; - int is_user; - - /* Find character '@'. If this character exist, then the token - * must be a username. - */ - if (pj_scan_peek( scanner, &pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@') - is_user = 1; - else - is_user = 0; - - return is_user; -} - -/* Parse user:pass tokens in an URI. */ -static void int_parse_user_pass( pj_scanner *scanner, - pj_str_t *user, pj_str_t *pass) -{ - pj_scan_get( scanner, &pjsip_USER_SPEC, user); - if ( *scanner->curptr == ':') { - pj_scan_get_char( scanner ); - pj_scan_get( scanner, &pjsip_PASSWD_SPEC, pass); - } else { - pass->ptr = NULL; - pass->slen = 0; - } - - /* Get the '@' */ - pj_scan_get_char( scanner ); -} - -/* Parse all types of URI. */ -static pjsip_uri *int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool, - unsigned opt) -{ - pjsip_uri *uri; - int is_name_addr = 0; - - if (*scanner->curptr=='"' || *scanner->curptr=='<') { - uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); - is_name_addr = 1; - } else { - pj_scan_state backtrack; - pj_str_t scheme; - int colon; - - pj_scan_save_state( scanner, &backtrack); - pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &scheme); - colon = pj_scan_get_char( scanner ); - pj_scan_restore_state( scanner, &backtrack); - + // "Syntax error in line %d col %d (hname=%.*s)",
+ // scanner->line, scanner->col, hname.slen, hname.ptr));
+
+ if (err_list) {
+ pjsip_parser_err_report *err_info;
+
+ err_info = pj_pool_alloc(pool, sizeof(*err_info));
+ err_info->exception_code = PJ_GET_EXCEPTION();
+ err_info->line = scanner->line;
+ err_info->col = scanner->col;
+ err_info->hname = hname;
+
+ pj_list_insert_before(err_list, err_info);
+ }
+
+ if (!pj_scan_is_eof(scanner)) {
+ pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &token);
+ parse_hdr_end(scanner);
+ }
+ }
+ PJ_END;
+
+ if (hdr) {
+ /* Single parse of header line can produce multiple headers.
+ * For example, if one Contact: header contains Contact list
+ * separated by comma, then these Contacts will be split into
+ * different Contact headers.
+ * So here we must insert list instead of just insert one header.
+ */
+ pj_list_insert_nodes_before(&msg->hdr, hdr);
+ }
+
+ /* Parse until EOF or an empty line is found. */
+ } while (!pj_scan_is_eof(scanner) &&
+ *scanner->curptr != '\r' && *scanner->curptr != '\n');
+
+ /* If empty line is found, eat it. */
+ if (!pj_scan_is_eof(scanner)) {
+ if (*scanner->curptr=='\r' || *scanner->curptr=='\n') {
+ pj_scan_get_newline(scanner);
+ }
+ }
+
+ /* If we have Content-Type header, treat the rest of the message as body.*/
+ if (ctype_hdr) {
+ pjsip_msg_body *body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
+ pj_strdup(pool, &body->content_type.type, &ctype_hdr->media.type);
+ pj_strdup(pool, &body->content_type.subtype, &ctype_hdr->media.subtype);
+ pj_strdup(pool, &body->content_type.param, &ctype_hdr->media.param);
+ body->data = scanner->curptr;
+ body->len = scanner->end - scanner->curptr;
+ body->print_body = &generic_print_body;
+
+ msg->body = body;
+ }
+
+ return msg;
+}
+
+/* Parse parameter (pname ["=" pvalue]). */
+void pjsip_parse_param_imp( pj_scanner *scanner,
+ pj_str_t *pname, pj_str_t *pvalue,
+ unsigned option)
+{
+ /* pname */
+ pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pname);
+
+ /* pvalue, if any */
+ if (*scanner->curptr == '=') {
+ pj_scan_get_char(scanner);
+ /* pvalue can be a quoted string. */
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote( scanner, '"', '"', pvalue);
+ if (option & PJSIP_PARSE_REMOVE_QUOTE) {
+ pvalue->ptr++;
+ pvalue->slen -= 2;
+ }
+ } else {
+ pj_scan_get(scanner, &pjsip_PARAM_CHAR_SPEC, pvalue);
+ }
+ } else {
+ pvalue->ptr = NULL;
+ pvalue->slen = 0;
+ }
+}
+
+/* Parse parameter (";" pname ["=" pvalue]). */
+static void int_parse_param( pj_scanner *scanner,
+ pj_str_t *pname, pj_str_t *pvalue)
+{
+ /* Get ';' character */
+ pj_scan_get_char(scanner);
+
+ /* Get pname and optionally pvalue */
+ pjsip_parse_param_imp(scanner, pname, pvalue, 0);
+}
+
+/* Parse host:port in URI. */
+static void int_parse_uri_host_port( pj_scanner *scanner,
+ pj_str_t *host, int *p_port)
+{
+ pj_scan_get( scanner, &pjsip_HOST_SPEC, host);
+ if (*scanner->curptr == ':') {
+ pj_str_t port;
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &port);
+ *p_port = pj_strtoul(&port);
+ } else {
+ *p_port = 0;
+ }
+}
+
+/* Determine if the next token in an URI is a user specification. */
+static int int_is_next_user(pj_scanner *scanner)
+{
+ pj_str_t dummy;
+ int is_user;
+
+ /* Find character '@'. If this character exist, then the token
+ * must be a username.
+ */
+ if (pj_scan_peek( scanner, &pjsip_PROBE_USER_HOST_SPEC, &dummy) == '@')
+ is_user = 1;
+ else
+ is_user = 0;
+
+ return is_user;
+}
+
+/* Parse user:pass tokens in an URI. */
+static void int_parse_user_pass( pj_scanner *scanner,
+ pj_str_t *user, pj_str_t *pass)
+{
+ pj_scan_get( scanner, &pjsip_USER_SPEC, user);
+ if ( *scanner->curptr == ':') {
+ pj_scan_get_char( scanner );
+ pj_scan_get( scanner, &pjsip_PASSWD_SPEC, pass);
+ } else {
+ pass->ptr = NULL;
+ pass->slen = 0;
+ }
+
+ /* Get the '@' */
+ pj_scan_get_char( scanner );
+}
+
+/* Parse all types of URI. */
+static pjsip_uri *int_parse_uri_or_name_addr( pj_scanner *scanner, pj_pool_t *pool,
+ unsigned opt)
+{
+ pjsip_uri *uri;
+ int is_name_addr = 0;
+
+ if (*scanner->curptr=='"' || *scanner->curptr=='<') {
+ uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ is_name_addr = 1;
+ } else {
+ pj_scan_state backtrack;
+ pj_str_t scheme;
+ int colon;
+
+ pj_scan_save_state( scanner, &backtrack);
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &scheme);
+ colon = pj_scan_get_char( scanner );
+ pj_scan_restore_state( scanner, &backtrack);
+
if (colon==':' &&
(parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
- parser_stricmp(scheme, pjsip_SIPS_STR)==0)) - { + parser_stricmp(scheme, pjsip_SIPS_STR)==0))
+ {
uri = (pjsip_uri*)
- int_parse_sip_url( scanner, pool, - (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0); - - } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) { - - /* Not supported. */ - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - UNREACHED({return NULL; /* Not reached. */}); - - } else { - uri = (pjsip_uri*)int_parse_name_addr( scanner, pool ); - is_name_addr = 1; - } - } - - /* Should we return the URI object as name address? */ - if (opt & PJSIP_PARSE_URI_AS_NAMEADDR) { - if (is_name_addr == 0) { - pjsip_name_addr *name_addr; - - name_addr = pjsip_name_addr_create(pool); - name_addr->uri = uri; - - uri = (pjsip_uri*)name_addr; - } - } - - return uri; -} - -/* Parse URI. */ -static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool, - pj_bool_t parse_params) -{ - if (*scanner->curptr=='"' || *scanner->curptr=='<') { - return (pjsip_uri*)int_parse_name_addr( scanner, pool ); - } else { - pj_str_t scheme; - int colon; - - /* Get scheme. */ - colon = pj_scan_peek(scanner, &pjsip_TOKEN_SPEC, &scheme); - if (colon != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - + int_parse_sip_url( scanner, pool,
+ (opt & PJSIP_PARSE_URI_IN_FROM_TO_HDR)== 0);
+
+ } else if (colon==':' && parser_stricmp( scheme, pjsip_TEL_STR)==0) {
+
+ /* Not supported. */
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({return NULL; /* Not reached. */});
+
+ } else {
+ uri = (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ is_name_addr = 1;
+ }
+ }
+
+ /* Should we return the URI object as name address? */
+ if (opt & PJSIP_PARSE_URI_AS_NAMEADDR) {
+ if (is_name_addr == 0) {
+ pjsip_name_addr *name_addr;
+
+ name_addr = pjsip_name_addr_create(pool);
+ name_addr->uri = uri;
+
+ uri = (pjsip_uri*)name_addr;
+ }
+ }
+
+ return uri;
+}
+
+/* Parse URI. */
+static pjsip_uri *int_parse_uri(pj_scanner *scanner, pj_pool_t *pool,
+ pj_bool_t parse_params)
+{
+ if (*scanner->curptr=='"' || *scanner->curptr=='<') {
+ return (pjsip_uri*)int_parse_name_addr( scanner, pool );
+ } else {
+ pj_str_t scheme;
+ int colon;
+
+ /* Get scheme. */
+ colon = pj_scan_peek(scanner, &pjsip_TOKEN_SPEC, &scheme);
+ if (colon != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
if ((parser_stricmp(scheme, pjsip_SIP_STR)==0 ||
- parser_stricmp(scheme, pjsip_SIPS_STR)==0)) - { - return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params); - - } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - UNREACHED({ return NULL; /* Not reached. */ }) - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - UNREACHED({ return NULL; /* Not reached. */ }) - } - } -} - -/* Parse "sip:" and "sips:" URI. */ -static pjsip_url *int_parse_sip_url( pj_scanner *scanner, - pj_pool_t *pool, - pj_bool_t parse_params) -{ - pj_str_t scheme; - pjsip_url *url; - int colon; - int skip_ws = scanner->skip_ws; - scanner->skip_ws = 0; - - pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &scheme); - colon = pj_scan_get_char(scanner); - if (colon != ':') { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - } - - if (parser_stricmp(scheme, pjsip_SIP_STR)==0) { - url = pjsip_url_create(pool, 0); - - } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) { - url = pjsip_url_create(pool, 1); - - } else { - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - /* should not reach here */ - UNREACHED({ - pj_assert(0); - return 0; - }) - } - - if (int_is_next_user(scanner)) { - int_parse_user_pass(scanner, &url->user, &url->passwd); - } - - /* Get host:port */ - int_parse_uri_host_port(scanner, &url->host, &url->port); - - /* Get URL parameters. */ - while ( parse_params && *scanner->curptr == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - - if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) { - url->user_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) { - url->method_param = pvalue; - - } else if (!parser_stricmp(pname,pjsip_TRANSPORT_STR) && pvalue.slen) { - url->transport_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { - url->ttl_param = pj_strtoul(&pvalue); - - } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { - url->maddr_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_LR_STR)) { - url->lr_param = 1; - - } else { - concat_param(&url->other_param, pool, &pname, &pvalue); - } - } - - /* Get header params. */ - if (parse_params && *scanner->curptr == '?') { + parser_stricmp(scheme, pjsip_SIPS_STR)==0))
+ {
+ return (pjsip_uri*)int_parse_sip_url( scanner, pool, parse_params);
+
+ } else if (parser_stricmp(scheme, pjsip_TEL_STR)==0) {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({ return NULL; /* Not reached. */ })
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ UNREACHED({ return NULL; /* Not reached. */ })
+ }
+ }
+}
+
+/* Parse "sip:" and "sips:" URI. */
+static pjsip_url *int_parse_sip_url( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pj_bool_t parse_params)
+{
+ pj_str_t scheme;
+ pjsip_url *url;
+ int colon;
+ int skip_ws = scanner->skip_ws;
+ scanner->skip_ws = 0;
+
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &scheme);
+ colon = pj_scan_get_char(scanner);
+ if (colon != ':') {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ }
+
+ if (parser_stricmp(scheme, pjsip_SIP_STR)==0) {
+ url = pjsip_url_create(pool, 0);
+
+ } else if (parser_stricmp(scheme, pjsip_SIPS_STR)==0) {
+ url = pjsip_url_create(pool, 1);
+
+ } else {
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ /* should not reach here */
+ UNREACHED({
+ pj_assert(0);
+ return 0;
+ })
+ }
+
+ if (int_is_next_user(scanner)) {
+ int_parse_user_pass(scanner, &url->user, &url->passwd);
+ }
+
+ /* Get host:port */
+ int_parse_uri_host_port(scanner, &url->host, &url->port);
+
+ /* Get URL parameters. */
+ while ( parse_params && *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_USER_STR) && pvalue.slen) {
+ url->user_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_METHOD_STR) && pvalue.slen) {
+ url->method_param = pvalue;
+
+ } else if (!parser_stricmp(pname,pjsip_TRANSPORT_STR) && pvalue.slen) {
+ url->transport_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+ url->ttl_param = pj_strtoul(&pvalue);
+
+ } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+ url->maddr_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_LR_STR)) {
+ url->lr_param = 1;
+
+ } else {
+ concat_param(&url->other_param, pool, &pname, &pvalue);
+ }
+ }
+
+ /* Get header params. */
+ if (parse_params && *scanner->curptr == '?') {
pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &url->header_param); - } - - scanner->skip_ws = skip_ws; - pj_scan_skip_whitespace(scanner); - return url; -} - -/* Parse nameaddr. */ -static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner, - pj_pool_t *pool ) -{ - int has_bracket; - pjsip_name_addr *name_addr; - - name_addr = pjsip_name_addr_create(pool); - - if (*scanner->curptr == '"') { - pj_scan_get_quote( scanner, '"', '"', &name_addr->display); - - } else if (*scanner->curptr != '<') { - int next; - pj_str_t dummy; - - /* This can be either the start of display name, - * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char. - * We're only interested in display name, because SIP URL - * will be parser later. - */ - next = pj_scan_peek_until(scanner, &pjsip_DISPLAY_SCAN_SPEC, &dummy); - if (next == '<') { - /* Ok, this is what we're looking for, a display name. */ - pj_scan_get_until_ch( scanner, '<', &name_addr->display); - pj_strtrim(&name_addr->display); - } - } - - /* Manually skip whitespace. */ - pj_scan_skip_whitespace(scanner); - - /* Get the SIP-URL */ - has_bracket = (*scanner->curptr == '<'); - if (has_bracket) - pj_scan_get_char(scanner); - name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE ); - if (has_bracket) - pj_scan_get_char(scanner); - - return name_addr; -} - - -/* Parse SIP request line. */ -static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool, - pjsip_request_line *req_line) -{ - pj_str_t token; - - pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &token); - pjsip_method_init_np( &req_line->method, &token); - - req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE); - if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0) - PJ_THROW( PJSIP_SYN_ERR_EXCEPTION); - pj_scan_advance_n (scanner, 7, 1); - pj_scan_get_newline( scanner ); -} - -/* Parse status line. */ -static void int_parse_status_line( pj_scanner *scanner, - pjsip_status_line *status_line) -{ - pj_str_t token; - - if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0) - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - pj_scan_advance_n( scanner, 7, 1); - - pj_scan_get( scanner, &pjsip_DIGIT_SPEC, &token); - status_line->code = pj_strtoul(&token); + &url->header_param);
+ }
+
+ scanner->skip_ws = skip_ws;
+ pj_scan_skip_whitespace(scanner);
+ return url;
+}
+
+/* Parse nameaddr. */
+static pjsip_name_addr *int_parse_name_addr( pj_scanner *scanner,
+ pj_pool_t *pool )
+{
+ int has_bracket;
+ pjsip_name_addr *name_addr;
+
+ name_addr = pjsip_name_addr_create(pool);
+
+ if (*scanner->curptr == '"') {
+ pj_scan_get_quote( scanner, '"', '"', &name_addr->display);
+
+ } else if (*scanner->curptr != '<') {
+ int next;
+ pj_str_t dummy;
+
+ /* This can be either the start of display name,
+ * the start of URL ("sip:", "sips:", "tel:", etc.), or '<' char.
+ * We're only interested in display name, because SIP URL
+ * will be parser later.
+ */
+ next = pj_scan_peek_until(scanner, &pjsip_DISPLAY_SCAN_SPEC, &dummy);
+ if (next == '<') {
+ /* Ok, this is what we're looking for, a display name. */
+ pj_scan_get_until_ch( scanner, '<', &name_addr->display);
+ pj_strtrim(&name_addr->display);
+ }
+ }
+
+ /* Manually skip whitespace. */
+ pj_scan_skip_whitespace(scanner);
+
+ /* Get the SIP-URL */
+ has_bracket = (*scanner->curptr == '<');
+ if (has_bracket)
+ pj_scan_get_char(scanner);
+ name_addr->uri = int_parse_uri( scanner, pool, PJ_TRUE );
+ if (has_bracket)
+ pj_scan_get_char(scanner);
+
+ return name_addr;
+}
+
+
+/* Parse SIP request line. */
+static void int_parse_req_line( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_request_line *req_line)
+{
+ pj_str_t token;
+
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &token);
+ pjsip_method_init_np( &req_line->method, &token);
+
+ req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION, 7) != 0)
+ PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
+ pj_scan_advance_n (scanner, 7, 1);
+ pj_scan_get_newline( scanner );
+}
+
+/* Parse status line. */
+static void int_parse_status_line( pj_scanner *scanner,
+ pjsip_status_line *status_line)
+{
+ pj_str_t token;
+
+ if (pj_scan_stricmp(scanner, PJSIP_VERSION, 7) != 0)
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+ pj_scan_advance_n( scanner, 7, 1);
+
+ pj_scan_get( scanner, &pjsip_DIGIT_SPEC, &token);
+ status_line->code = pj_strtoul(&token);
pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &status_line->reason); - pj_scan_get_newline( scanner ); -} - -/* Parse ending of header. */ -static void parse_hdr_end( pj_scanner *scanner ) -{ - if (pj_scan_is_eof(scanner)) { - ; /* Do nothing. */ - } else if (*scanner->curptr == '&') { - pj_scan_get_char(scanner); - } else { - pj_scan_get_newline(scanner); - } -} - -/* Parse ending of header. */ -void pjsip_parse_end_hdr_imp( pj_scanner *scanner ) -{ - parse_hdr_end(scanner); -} - -/* Parse generic array header. */ -static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr, - pj_scanner *scanner) -{ - pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS, &hdr->values[0]); - hdr->count++; - - while (*scanner->curptr == ',') { - pj_scan_get_char(scanner); + &status_line->reason);
+ pj_scan_get_newline( scanner );
+}
+
+/* Parse ending of header. */
+static void parse_hdr_end( pj_scanner *scanner )
+{
+ if (pj_scan_is_eof(scanner)) {
+ ; /* Do nothing. */
+ } else if (*scanner->curptr == '&') {
+ pj_scan_get_char(scanner);
+ } else {
+ pj_scan_get_newline(scanner);
+ }
+}
+
+/* Parse ending of header. */
+void pjsip_parse_end_hdr_imp( pj_scanner *scanner )
+{
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic array header. */
+static void parse_generic_array_hdr( pjsip_generic_array_hdr *hdr,
+ pj_scanner *scanner)
+{
+ pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS, &hdr->values[0]);
+ hdr->count++;
+
+ while (*scanner->curptr == ',') {
+ pj_scan_get_char(scanner);
pj_scan_get_until( scanner, &pjsip_ARRAY_ELEMENTS,
- &hdr->values[hdr->count]); - hdr->count++; - } - parse_hdr_end(scanner); -} - -/* Parse generic string header. */ -static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr, - pj_scanner *scanner ) -{ - pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue); - parse_hdr_end(scanner); -} - -/* Parse generic integer header. */ -static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr, - pj_scanner *scanner ) -{ - pj_str_t tmp; - pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &tmp); - hdr->ivalue = pj_strtoul(&tmp); - parse_hdr_end(scanner); -} - - -/* Parse Accept header. */ -static pjsip_hdr* parse_hdr_accept(pjsip_parse_ctx *ctx) -{ - pjsip_accept_hdr *accept = pjsip_accept_hdr_create(ctx->pool); - parse_generic_array_hdr(accept, ctx->scanner); - return (pjsip_hdr*)accept; -} - -/* Parse Allow header. */ -static pjsip_hdr* parse_hdr_allow(pjsip_parse_ctx *ctx) -{ - pjsip_allow_hdr *allow = pjsip_allow_hdr_create(ctx->pool); - parse_generic_array_hdr(allow, ctx->scanner); - return (pjsip_hdr*)allow; -} - -/* Parse Call-ID header. */ -static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx) -{ - pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(ctx->pool); - pj_scan_get_until( ctx->scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id); + &hdr->values[hdr->count]);
+ hdr->count++;
+ }
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic string header. */
+static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
+ pj_scanner *scanner )
+{
+ pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->hvalue);
+ parse_hdr_end(scanner);
+}
+
+/* Parse generic integer header. */
+static void parse_generic_int_hdr( pjsip_generic_int_hdr *hdr,
+ pj_scanner *scanner )
+{
+ pj_str_t tmp;
+ pj_scan_get_until( scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &tmp);
+ hdr->ivalue = pj_strtoul(&tmp);
+ parse_hdr_end(scanner);
+}
+
+
+/* Parse Accept header. */
+static pjsip_hdr* parse_hdr_accept(pjsip_parse_ctx *ctx)
+{
+ pjsip_accept_hdr *accept = pjsip_accept_hdr_create(ctx->pool);
+ parse_generic_array_hdr(accept, ctx->scanner);
+ return (pjsip_hdr*)accept;
+}
+
+/* Parse Allow header. */
+static pjsip_hdr* parse_hdr_allow(pjsip_parse_ctx *ctx)
+{
+ pjsip_allow_hdr *allow = pjsip_allow_hdr_create(ctx->pool);
+ parse_generic_array_hdr(allow, ctx->scanner);
+ return (pjsip_hdr*)allow;
+}
+
+/* Parse Call-ID header. */
+static pjsip_hdr* parse_hdr_call_id(pjsip_parse_ctx *ctx)
+{
+ pjsip_cid_hdr *hdr = pjsip_cid_hdr_create(ctx->pool);
+ pj_scan_get_until( ctx->scanner, &pjsip_NEWLINE_OR_EOF_SPEC, &hdr->id);
parse_hdr_end(ctx->scanner);
if (ctx->rdata)
ctx->rdata->call_id = hdr->id;
- - return (pjsip_hdr*)hdr; -} - -/* Parse and interpret Contact param. */ -static void int_parse_contact_param( pjsip_contact_hdr *hdr, - pj_scanner *scanner, - pj_pool_t *pool) -{ - while ( *scanner->curptr == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) { - char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen); - if (!dot_pos) { - hdr->q1000 = pj_strtoul(&pvalue); - } else { - pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1); - pvalue.ptr = dot_pos + 1; - hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3); - } - } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) { - hdr->expires = pj_strtoul(&pvalue); - - } else { - concat_param(&hdr->other_param, pool, &pname, &pvalue); - } - } -} - -/* Parse Contact header. */ -static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx ) -{ +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse and interpret Contact param. */
+static void int_parse_contact_param( pjsip_contact_hdr *hdr,
+ pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ while ( *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+ if (!parser_stricmp(pname, pjsip_Q_STR) && pvalue.slen) {
+ char *dot_pos = memchr(pvalue.ptr, '.', pvalue.slen);
+ if (!dot_pos) {
+ hdr->q1000 = pj_strtoul(&pvalue);
+ } else {
+ pvalue.slen = (pvalue.ptr+pvalue.slen) - (dot_pos+1);
+ pvalue.ptr = dot_pos + 1;
+ hdr->q1000 = pj_strtoul_mindigit(&pvalue, 3);
+ }
+ } else if (!parser_stricmp(pname, pjsip_EXPIRES_STR) && pvalue.slen) {
+ hdr->expires = pj_strtoul(&pvalue);
+
+ } else {
+ concat_param(&hdr->other_param, pool, &pname, &pvalue);
+ }
+ }
+}
+
+/* Parse Contact header. */
+static pjsip_hdr* parse_hdr_contact( pjsip_parse_ctx *ctx )
+{
pjsip_contact_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner; - - do { - pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(ctx->pool); - if (first == NULL) - first = hdr; - else - pj_list_insert_before(first, hdr); - - if (*scanner->curptr == '*') { - pj_scan_get_char(scanner); - hdr->star = 1; - - } else { - hdr->star = 0; + pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_contact_hdr *hdr = pjsip_contact_hdr_create(ctx->pool);
+ if (first == NULL)
+ first = hdr;
+ else
+ pj_list_insert_before(first, hdr);
+
+ if (*scanner->curptr == '*') {
+ pj_scan_get_char(scanner);
+ hdr->star = 1;
+
+ } else {
+ hdr->star = 0;
hdr->uri = int_parse_uri_or_name_addr(scanner, ctx->pool,
- PJSIP_PARSE_URI_AS_NAMEADDR); - - int_parse_contact_param(hdr, scanner, ctx->pool); - } - - if (*scanner->curptr != ',') - break; - - pj_scan_get_char(scanner); - - } while (1); - + PJSIP_PARSE_URI_AS_NAMEADDR);
+
+ int_parse_contact_param(hdr, scanner, ctx->pool);
+ }
+
+ if (*scanner->curptr != ',')
+ break;
+
+ pj_scan_get_char(scanner);
+
+ } while (1);
+
parse_hdr_end(scanner);
- - return (pjsip_hdr*)first; -} - -/* Parse Content-Length header. */ -static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx ) -{ - pj_str_t digit; - pjsip_clen_hdr *hdr; - - hdr = pjsip_clen_hdr_create(ctx->pool); - pj_scan_get(ctx->scanner, &pjsip_DIGIT_SPEC, &digit); - hdr->len = pj_strtoul(&digit); +
+ return (pjsip_hdr*)first;
+}
+
+/* Parse Content-Length header. */
+static pjsip_hdr* parse_hdr_content_len( pjsip_parse_ctx *ctx )
+{
+ pj_str_t digit;
+ pjsip_clen_hdr *hdr;
+
+ hdr = pjsip_clen_hdr_create(ctx->pool);
+ pj_scan_get(ctx->scanner, &pjsip_DIGIT_SPEC, &digit);
+ hdr->len = pj_strtoul(&digit);
parse_hdr_end(ctx->scanner);
if (ctx->rdata)
ctx->rdata->clen = hdr;
- - return (pjsip_hdr*)hdr; -} - -/* Parse Content-Type header. */ -static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx ) -{ - pjsip_ctype_hdr *hdr; +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Content-Type header. */
+static pjsip_hdr* parse_hdr_content_type( pjsip_parse_ctx *ctx )
+{
+ pjsip_ctype_hdr *hdr;
pj_scanner *scanner = ctx->scanner;
- - hdr = pjsip_ctype_hdr_create(ctx->pool); - - /* Parse media type and subtype. */ - pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.type); - pj_scan_get_char(scanner); - pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.subtype); - - /* Parse media parameters */ - while (*scanner->curptr == ';') { - pj_str_t pname, pvalue; - int_parse_param(scanner, &pname, &pvalue); - concat_param(&hdr->media.param, ctx->pool, &pname, &pvalue); - } - +
+ hdr = pjsip_ctype_hdr_create(ctx->pool);
+
+ /* Parse media type and subtype. */
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.type);
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, &pjsip_TOKEN_SPEC, &hdr->media.subtype);
+
+ /* Parse media parameters */
+ while (*scanner->curptr == ';') {
+ pj_str_t pname, pvalue;
+ int_parse_param(scanner, &pname, &pvalue);
+ concat_param(&hdr->media.param, ctx->pool, &pname, &pvalue);
+ }
+
parse_hdr_end(ctx->scanner);
if (ctx->rdata)
ctx->rdata->ctype = hdr;
- - return (pjsip_hdr*)hdr; -} - -/* Parse CSeq header. */ -static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx ) -{ - pj_str_t cseq, method; - pjsip_cseq_hdr *hdr; - - hdr = pjsip_cseq_hdr_create(ctx->pool); - pj_scan_get( ctx->scanner, &pjsip_DIGIT_SPEC, &cseq); - hdr->cseq = pj_strtoul(&cseq); - - pj_scan_get( ctx->scanner, &pjsip_TOKEN_SPEC, &method); - pjsip_method_init_np(&hdr->method, &method); - +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse CSeq header. */
+static pjsip_hdr* parse_hdr_cseq( pjsip_parse_ctx *ctx )
+{
+ pj_str_t cseq, method;
+ pjsip_cseq_hdr *hdr;
+
+ hdr = pjsip_cseq_hdr_create(ctx->pool);
+ pj_scan_get( ctx->scanner, &pjsip_DIGIT_SPEC, &cseq);
+ hdr->cseq = pj_strtoul(&cseq);
+
+ pj_scan_get( ctx->scanner, &pjsip_TOKEN_SPEC, &method);
+ pjsip_method_init_np(&hdr->method, &method);
+
parse_hdr_end( ctx->scanner );
if (ctx->rdata)
ctx->rdata->cseq = hdr;
- - return (pjsip_hdr*)hdr; -} - -/* Parse Expires header. */ -static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx) -{ - pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool); - parse_generic_int_hdr(hdr, ctx->scanner); - return (pjsip_hdr*)hdr; -} - -/* Parse From: or To: header. */ -static void parse_hdr_fromto( pj_scanner *scanner, - pj_pool_t *pool, - pjsip_from_hdr *hdr) -{ - hdr->uri = int_parse_uri_or_name_addr(scanner, pool, - PJSIP_PARSE_URI_AS_NAMEADDR | - PJSIP_PARSE_URI_IN_FROM_TO_HDR); - - while ( *scanner->curptr == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - - if (!parser_stricmp(pname, pjsip_TAG_STR)) { - hdr->tag = pvalue; - - } else { - concat_param(&hdr->other_param, pool, &pname, &pvalue); - } - } - - parse_hdr_end(scanner); -} - -/* Parse From: header. */ -static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx ) -{ - pjsip_from_hdr *hdr = pjsip_from_hdr_create(ctx->pool); +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Expires header. */
+static pjsip_hdr* parse_hdr_expires(pjsip_parse_ctx *ctx)
+{
+ pjsip_expires_hdr *hdr = pjsip_expires_hdr_create(ctx->pool);
+ parse_generic_int_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse From: or To: header. */
+static void parse_hdr_fromto( pj_scanner *scanner,
+ pj_pool_t *pool,
+ pjsip_from_hdr *hdr)
+{
+ hdr->uri = int_parse_uri_or_name_addr(scanner, pool,
+ PJSIP_PARSE_URI_AS_NAMEADDR |
+ PJSIP_PARSE_URI_IN_FROM_TO_HDR);
+
+ while ( *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_TAG_STR)) {
+ hdr->tag = pvalue;
+
+ } else {
+ concat_param(&hdr->other_param, pool, &pname, &pvalue);
+ }
+ }
+
+ parse_hdr_end(scanner);
+}
+
+/* Parse From: header. */
+static pjsip_hdr* parse_hdr_from( pjsip_parse_ctx *ctx )
+{
+ pjsip_from_hdr *hdr = pjsip_from_hdr_create(ctx->pool);
parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
if (ctx->rdata)
ctx->rdata->from = hdr;
- - return (pjsip_hdr*)hdr; -} - -/* Parse Require: header. */ -static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx ) -{ - pjsip_require_hdr *hdr = pjsip_require_hdr_create(ctx->pool); +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Require: header. */
+static pjsip_hdr* parse_hdr_require( pjsip_parse_ctx *ctx )
+{
+ pjsip_require_hdr *hdr = pjsip_require_hdr_create(ctx->pool);
parse_generic_array_hdr(hdr, ctx->scanner);
if (ctx->rdata && ctx->rdata->require == NULL)
ctx->rdata->require = hdr;
- - return (pjsip_hdr*)hdr; -} - -/* Parse Retry-After: header. */ -static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx) -{ - pjsip_retry_after_hdr *hdr; - hdr = pjsip_retry_after_hdr_create(ctx->pool); - parse_generic_int_hdr(hdr, ctx->scanner); - return (pjsip_hdr*)hdr; -} - -/* Parse Supported: header. */ -static pjsip_hdr* parse_hdr_supported(pjsip_parse_ctx *ctx) -{ - pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(ctx->pool); - parse_generic_array_hdr(hdr, ctx->scanner); - return (pjsip_hdr*)hdr; -} - - -/* Parse To: header. */ -static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx ) -{ - pjsip_to_hdr *hdr = pjsip_to_hdr_create(ctx->pool); +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Retry-After: header. */
+static pjsip_hdr* parse_hdr_retry_after(pjsip_parse_ctx *ctx)
+{
+ pjsip_retry_after_hdr *hdr;
+ hdr = pjsip_retry_after_hdr_create(ctx->pool);
+ parse_generic_int_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Supported: header. */
+static pjsip_hdr* parse_hdr_supported(pjsip_parse_ctx *ctx)
+{
+ pjsip_supported_hdr *hdr = pjsip_supported_hdr_create(ctx->pool);
+ parse_generic_array_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+
+/* Parse To: header. */
+static pjsip_hdr* parse_hdr_to( pjsip_parse_ctx *ctx )
+{
+ pjsip_to_hdr *hdr = pjsip_to_hdr_create(ctx->pool);
parse_hdr_fromto(ctx->scanner, ctx->pool, hdr);
if (ctx->rdata)
ctx->rdata->to = hdr;
- - return (pjsip_hdr*)hdr; -} - -/* Parse Unsupported: header. */ -static pjsip_hdr* parse_hdr_unsupported(pjsip_parse_ctx *ctx) -{ - pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(ctx->pool); - parse_generic_array_hdr(hdr, ctx->scanner); - return (pjsip_hdr*)hdr; -} - -/* Parse and interpret Via parameters. */ -static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner, - pj_pool_t *pool) -{ - while ( *scanner->curptr == ';' ) { - pj_str_t pname, pvalue; - - int_parse_param( scanner, &pname, &pvalue); - - if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) { - hdr->branch_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) { - hdr->ttl_param = pj_strtoul(&pvalue); - - } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) { - hdr->maddr_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) { - hdr->recvd_param = pvalue; - - } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) { - if (pvalue.slen) - hdr->rport_param = pj_strtoul(&pvalue); - else - hdr->rport_param = 0; - } else { - concat_param( &hdr->other_param, pool, &pname, &pvalue); - } - } - -} - -/* 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); +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Unsupported: header. */
+static pjsip_hdr* parse_hdr_unsupported(pjsip_parse_ctx *ctx)
+{
+ pjsip_unsupported_hdr *hdr = pjsip_unsupported_hdr_create(ctx->pool);
+ parse_generic_array_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse and interpret Via parameters. */
+static void int_parse_via_param( pjsip_via_hdr *hdr, pj_scanner *scanner,
+ pj_pool_t *pool)
+{
+ while ( *scanner->curptr == ';' ) {
+ pj_str_t pname, pvalue;
+
+ int_parse_param( scanner, &pname, &pvalue);
+
+ if (!parser_stricmp(pname, pjsip_BRANCH_STR) && pvalue.slen) {
+ hdr->branch_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_TTL_STR) && pvalue.slen) {
+ hdr->ttl_param = pj_strtoul(&pvalue);
+
+ } else if (!parser_stricmp(pname, pjsip_MADDR_STR) && pvalue.slen) {
+ hdr->maddr_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_PNAME_STR) && pvalue.slen) {
+ hdr->recvd_param = pvalue;
+
+ } else if (!parser_stricmp(pname, pjsip_RPORT_STR)) {
+ if (pvalue.slen)
+ hdr->rport_param = pj_strtoul(&pvalue);
+ else
+ hdr->rport_param = 0;
+ } else {
+ concat_param( &hdr->other_param, pool, &pname, &pvalue);
+ }
+ }
+
+}
+
+/* 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);
parse_generic_int_hdr(hdr, ctx->scanner);
if (ctx->rdata)
ctx->rdata->max_fwd = hdr;
- - return (pjsip_hdr*)hdr; -} - -/* Parse Min-Expires header. */ -static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx) -{ - pjsip_min_expires_hdr *hdr; - hdr = pjsip_min_expires_hdr_create(ctx->pool); - parse_generic_int_hdr(hdr, ctx->scanner); - return (pjsip_hdr*)hdr; -} - - -/* Parse Route: or Record-Route: header. */ -static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool, - pjsip_routing_hdr *hdr ) -{ - pjsip_name_addr *temp=int_parse_name_addr(scanner, pool); - - pj_memcpy(&hdr->name_addr, temp, sizeof(*temp)); - if (*scanner->curptr == ';') { +
+ return (pjsip_hdr*)hdr;
+}
+
+/* Parse Min-Expires header. */
+static pjsip_hdr* parse_hdr_min_expires(pjsip_parse_ctx *ctx)
+{
+ pjsip_min_expires_hdr *hdr;
+ hdr = pjsip_min_expires_hdr_create(ctx->pool);
+ parse_generic_int_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+}
+
+
+/* Parse Route: or Record-Route: header. */
+static void parse_hdr_rr_route( pj_scanner *scanner, pj_pool_t *pool,
+ pjsip_routing_hdr *hdr )
+{
+ pjsip_name_addr *temp=int_parse_name_addr(scanner, pool);
+
+ pj_memcpy(&hdr->name_addr, temp, sizeof(*temp));
+ if (*scanner->curptr == ';') {
pj_scan_get_until(scanner, &pjsip_NEWLINE_OR_EOF_SPEC,
- &hdr->other_param); - } -} - -/* Parse Record-Route header. */ -static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx) -{ + &hdr->other_param);
+ }
+}
+
+/* Parse Record-Route header. */
+static pjsip_hdr* parse_hdr_rr( pjsip_parse_ctx *ctx)
+{
pjsip_rr_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner; - - do { - pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(ctx->pool); - if (!first) { - first = hdr; - } else { - pj_list_insert_before(first, hdr); - } - parse_hdr_rr_route(scanner, ctx->pool, hdr); - if (*scanner->curptr == ',') { - pj_scan_get_char(scanner); - } else { - break; - } - } while (1); + pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_rr_hdr *hdr = pjsip_rr_hdr_create(ctx->pool);
+ if (!first) {
+ first = hdr;
+ } else {
+ pj_list_insert_before(first, hdr);
+ }
+ parse_hdr_rr_route(scanner, ctx->pool, hdr);
+ if (*scanner->curptr == ',') {
+ pj_scan_get_char(scanner);
+ } else {
+ break;
+ }
+ } while (1);
parse_hdr_end(scanner);
if (ctx->rdata && ctx->rdata->record_route==NULL)
ctx->rdata->record_route = first;
- - return (pjsip_hdr*)first; -} - -/* Parse Route: header. */ -static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx ) -{ +
+ return (pjsip_hdr*)first;
+}
+
+/* Parse Route: header. */
+static pjsip_hdr* parse_hdr_route( pjsip_parse_ctx *ctx )
+{
pjsip_route_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner; - - do { - pjsip_route_hdr *hdr = pjsip_route_hdr_create(ctx->pool); - if (!first) { - first = hdr; - } else { - pj_list_insert_before(first, hdr); - } - parse_hdr_rr_route(scanner, ctx->pool, hdr); - if (*scanner->curptr == ',') { - pj_scan_get_char(scanner); - } else { - break; - } - } while (1); + pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_route_hdr *hdr = pjsip_route_hdr_create(ctx->pool);
+ if (!first) {
+ first = hdr;
+ } else {
+ pj_list_insert_before(first, hdr);
+ }
+ parse_hdr_rr_route(scanner, ctx->pool, hdr);
+ if (*scanner->curptr == ',') {
+ pj_scan_get_char(scanner);
+ } else {
+ break;
+ }
+ } while (1);
parse_hdr_end(scanner);
if (ctx->rdata && ctx->rdata->route==NULL)
ctx->rdata->route = first;
- - return (pjsip_hdr*)first; -} - -/* Parse Via: header. */ -static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx ) -{ +
+ return (pjsip_hdr*)first;
+}
+
+/* Parse Via: header. */
+static pjsip_hdr* parse_hdr_via( pjsip_parse_ctx *ctx )
+{
pjsip_via_hdr *first = NULL;
- pj_scanner *scanner = ctx->scanner; - - do { - pjsip_via_hdr *hdr = pjsip_via_hdr_create(ctx->pool); - if (!first) - first = hdr; - else - pj_list_insert_before(first, hdr); - - if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0) - PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); - - pj_scan_advance_n( scanner, 8, 1); - - pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hdr->transport); - pj_scan_get( scanner, &pjsip_HOST_SPEC, &hdr->sent_by.host); - - if (*scanner->curptr==':') { - pj_str_t digit; - pj_scan_get_char(scanner); - pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit); - hdr->sent_by.port = pj_strtoul(&digit); - } else { - hdr->sent_by.port = 5060; - } - - int_parse_via_param(hdr, scanner, ctx->pool); - - if (*scanner->curptr == '(') { - pj_scan_get_char(scanner); - pj_scan_get_until_ch( scanner, ')', &hdr->comment); - pj_scan_get_char( scanner ); - } - - if (*scanner->curptr != ',') - break; - - pj_scan_get_char(scanner); - - } while (1); - + pj_scanner *scanner = ctx->scanner;
+
+ do {
+ pjsip_via_hdr *hdr = pjsip_via_hdr_create(ctx->pool);
+ if (!first)
+ first = hdr;
+ else
+ pj_list_insert_before(first, hdr);
+
+ if (pj_scan_stricmp( scanner, PJSIP_VERSION "/", 8) != 0)
+ PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
+
+ pj_scan_advance_n( scanner, 8, 1);
+
+ pj_scan_get( scanner, &pjsip_TOKEN_SPEC, &hdr->transport);
+ pj_scan_get( scanner, &pjsip_HOST_SPEC, &hdr->sent_by.host);
+
+ if (*scanner->curptr==':') {
+ pj_str_t digit;
+ pj_scan_get_char(scanner);
+ pj_scan_get(scanner, &pjsip_DIGIT_SPEC, &digit);
+ hdr->sent_by.port = pj_strtoul(&digit);
+ } else {
+ hdr->sent_by.port = 5060;
+ }
+
+ int_parse_via_param(hdr, scanner, ctx->pool);
+
+ if (*scanner->curptr == '(') {
+ pj_scan_get_char(scanner);
+ pj_scan_get_until_ch( scanner, ')', &hdr->comment);
+ pj_scan_get_char( scanner );
+ }
+
+ if (*scanner->curptr != ',')
+ break;
+
+ pj_scan_get_char(scanner);
+
+ } while (1);
+
parse_hdr_end(scanner);
if (ctx->rdata && ctx->rdata->via == NULL)
ctx->rdata->via = first;
- - return (pjsip_hdr*)first; -} - -/* Parse generic header. */ -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); - parse_generic_string_hdr(hdr, ctx->scanner); - return (pjsip_hdr*)hdr; - -} - -/* Public function to parse a header value. */ -PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname, - char *buf, pj_size_t size, int *parsed_len ) -{ - pj_scanner scanner; +
+ return (pjsip_hdr*)first;
+}
+
+/* Parse generic header. */
+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);
+ parse_generic_string_hdr(hdr, ctx->scanner);
+ return (pjsip_hdr*)hdr;
+
+}
+
+/* Public function to parse a header value. */
+PJ_DEF(void*) pjsip_parse_hdr( pj_pool_t *pool, const pj_str_t *hname,
+ char *buf, pj_size_t size, int *parsed_len )
+{
+ pj_scanner scanner;
pjsip_hdr *hdr = NULL;
- pjsip_parse_ctx context; - PJ_USE_EXCEPTION; - - init_sip_parser(); - + pjsip_parse_ctx context;
+ PJ_USE_EXCEPTION;
+
+ init_sip_parser();
+
pj_scan_init(&scanner, buf, size, PJ_SCAN_AUTOSKIP_WS_HEADER,
- &on_syntax_error); + &on_syntax_error);
context.scanner = &scanner;
context.pool = pool;
context.rdata = NULL;
- - PJ_TRY { - pjsip_parse_hdr_func *handler = find_handler(hname); - if (handler) { - hdr = (*handler)(&context); - } else { - hdr = parse_hdr_generic_string(&context); - hdr->type = PJSIP_H_OTHER; - pj_strdup(pool, &hdr->name, hname); - hdr->sname = hdr->name; - } - - } - PJ_DEFAULT { - hdr = NULL; - } - PJ_END - - if (parsed_len) { - *parsed_len = (scanner.curptr - scanner.begin); - } - - pj_scan_fini(&scanner); - - return hdr; -} - +
+ PJ_TRY {
+ pjsip_parse_hdr_func *handler = find_handler(hname);
+ if (handler) {
+ hdr = (*handler)(&context);
+ } else {
+ hdr = parse_hdr_generic_string(&context);
+ hdr->type = PJSIP_H_OTHER;
+ pj_strdup(pool, &hdr->name, hname);
+ hdr->sname = hdr->name;
+ }
+
+ }
+ PJ_DEFAULT {
+ hdr = NULL;
+ }
+ PJ_END
+
+ if (parsed_len) {
+ *parsed_len = (scanner.curptr - scanner.begin);
+ }
+
+ pj_scan_fini(&scanner);
+
+ return hdr;
+}
+
diff --git a/pjsip/src/pjsip/sip_resolve.c b/pjsip/src/pjsip/sip_resolve.c index b199eaba..74d7e886 100644 --- a/pjsip/src/pjsip/sip_resolve.c +++ b/pjsip/src/pjsip/sip_resolve.c @@ -1,110 +1,132 @@ -/* $Id$ - */ - -#include <pjsip/sip_resolve.h> -#include <pjsip/sip_transport.h> -#include <pj/pool.h> -#include <pj/ctype.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <pjsip/sip_resolve.h>
+#include <pjsip/sip_transport.h>
+#include <pj/pool.h>
+#include <pj/ctype.h>
#include <pj/assert.h>
- -struct pjsip_resolver_t -{ - void *dummy; -}; - -PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool) -{ - pjsip_resolver_t *resolver; - resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver)); - return resolver; -} - -PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver) -{ - PJ_UNUSED_ARG(resolver); -} - -static int is_str_ip(const pj_str_t *host) -{ - const char *p = host->ptr; - const char *end = ((const char*)host->ptr) + host->slen; - - while (p != end) { - if (pj_isdigit(*p) || *p=='.') { - ++p; - } else { - return 0; - } - } - return 1; -} - -PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver, - pj_pool_t *pool, - pjsip_host_port *target, - void *token, - pjsip_resolver_callback *cb) -{ - struct pjsip_server_addresses svr_addr; - pj_status_t status; - int is_ip_addr; - pjsip_transport_type_e type = target->type; - - PJ_UNUSED_ARG(resolver); - PJ_UNUSED_ARG(pool); - - /* We only do synchronous resolving at this moment. */ - PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION) - - /* Is it IP address or hostname?. */ - is_ip_addr = is_str_ip(&target->host); - - /* Set the transport type if not explicitly specified. - * RFC 3263 section 4.1 specify rules to set up this. - */ - if (type == PJSIP_TRANSPORT_UNSPECIFIED) { - if (is_ip_addr || (target->port != 0)) { -#if PJ_HAS_TCP - if (target->flag & PJSIP_TRANSPORT_SECURE) - { - type = PJSIP_TRANSPORT_TLS; - } else if (target->flag & PJSIP_TRANSPORT_RELIABLE) - { - type = PJSIP_TRANSPORT_TCP; - } else -#endif - { - type = PJSIP_TRANSPORT_UDP; - } - } else { - /* No type or explicit port is specified, and the address is - * not IP address. - * In this case, full resolution must be performed. - * But we don't support it (yet). - */ - type = PJSIP_TRANSPORT_UDP; - } - - } - - /* Set the port number if not specified. */ - if (target->port == 0) { - target->port = pjsip_transport_get_default_port_for_type(type); - } - - /* Resolve hostname. */ - if (!is_ip_addr) { +
+struct pjsip_resolver_t
+{
+ void *dummy;
+};
+
+PJ_DEF(pjsip_resolver_t*) pjsip_resolver_create(pj_pool_t *pool)
+{
+ pjsip_resolver_t *resolver;
+ resolver = (pjsip_resolver_t*) pj_pool_calloc(pool, 1, sizeof(*resolver));
+ return resolver;
+}
+
+PJ_DEF(void) pjsip_resolver_destroy(pjsip_resolver_t *resolver)
+{
+ PJ_UNUSED_ARG(resolver);
+}
+
+static int is_str_ip(const pj_str_t *host)
+{
+ const char *p = host->ptr;
+ const char *end = ((const char*)host->ptr) + host->slen;
+
+ while (p != end) {
+ if (pj_isdigit(*p) || *p=='.') {
+ ++p;
+ } else {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+PJ_DEF(void) pjsip_resolve( pjsip_resolver_t *resolver,
+ pj_pool_t *pool,
+ pjsip_host_port *target,
+ void *token,
+ pjsip_resolver_callback *cb)
+{
+ struct pjsip_server_addresses svr_addr;
+ pj_status_t status;
+ int is_ip_addr;
+ pjsip_transport_type_e type = target->type;
+
+ PJ_UNUSED_ARG(resolver);
+ PJ_UNUSED_ARG(pool);
+
+ /* We only do synchronous resolving at this moment. */
+ PJ_TODO(SUPPORT_RFC3263_SERVER_RESOLUTION)
+
+ /* Is it IP address or hostname?. */
+ is_ip_addr = is_str_ip(&target->host);
+
+ /* Set the transport type if not explicitly specified.
+ * RFC 3263 section 4.1 specify rules to set up this.
+ */
+ if (type == PJSIP_TRANSPORT_UNSPECIFIED) {
+ if (is_ip_addr || (target->port != 0)) {
+#if PJ_HAS_TCP
+ if (target->flag & PJSIP_TRANSPORT_SECURE)
+ {
+ type = PJSIP_TRANSPORT_TLS;
+ } else if (target->flag & PJSIP_TRANSPORT_RELIABLE)
+ {
+ type = PJSIP_TRANSPORT_TCP;
+ } else
+#endif
+ {
+ type = PJSIP_TRANSPORT_UDP;
+ }
+ } else {
+ /* No type or explicit port is specified, and the address is
+ * not IP address.
+ * In this case, full resolution must be performed.
+ * But we don't support it (yet).
+ */
+ type = PJSIP_TRANSPORT_UDP;
+ }
+
+ }
+
+ /* Set the port number if not specified. */
+ if (target->port == 0) {
+ target->port = pjsip_transport_get_default_port_for_type(type);
+ }
+
+ /* Resolve hostname. */
+ if (!is_ip_addr) {
status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
- (pj_uint16_t)target->port); - } else { + (pj_uint16_t)target->port);
+ } else {
status = pj_sockaddr_in_init(&svr_addr.entry[0].addr, &target->host,
- (pj_uint16_t)target->port); - pj_assert(status == PJ_SUCCESS); - } - - /* Call the callback. */ - svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0; - svr_addr.entry[0].type = type; - (*cb)(status, token, &svr_addr); -} - + (pj_uint16_t)target->port);
+ pj_assert(status == PJ_SUCCESS);
+ }
+
+ /* Call the callback. */
+ svr_addr.count = (status == PJ_SUCCESS) ? 1 : 0;
+ svr_addr.entry[0].type = type;
+ (*cb)(status, token, &svr_addr);
+}
+
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index ebc0a803..9e9b4836 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -1,1966 +1,1988 @@ -/* $Id$ - */ -#include <pjsip/sip_transaction.h> -#include <pjsip/sip_transport.h> -#include <pjsip/sip_config.h> -#include <pjsip/sip_misc.h> -#include <pjsip/sip_event.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_errno.h> -#include <pj/log.h> -#include <pj/string.h> -#include <pj/os.h> -#include <pj/guid.h> -#include <pj/pool.h> -#include <pj/assert.h> - -/* Thread Local Storage ID for transaction lock (initialized by endpoint) */ -long pjsip_tsx_lock_tls_id; - -/* State names */ -static const char *state_str[] = -{ - "Null", - "Calling", - "Trying", - "Proceeding", - "Completed", - "Confirmed", - "Terminated", - "Destroyed", -}; - -/* Role names */ -static const char *role_name[] = -{ - "Client", - "Server" -}; - -/* Transaction lock. */ -typedef struct tsx_lock_data { - struct tsx_lock_data *prev; - pjsip_transaction *tsx; - int is_alive; -} tsx_lock_data; - - -/* Timer timeout value constants */ -static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000, - PJSIP_T1_TIMEOUT%1000 }; -static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000, - PJSIP_T4_TIMEOUT%1000 }; -static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000, - PJSIP_TD_TIMEOUT%1000 }; -static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, - (64*PJSIP_T1_TIMEOUT)%1000 }; - -/* Internal timer IDs */ -enum Transaction_Timer_Id -{ - TSX_TIMER_RETRANSMISSION, - TSX_TIMER_TIMEOUT, -}; - -/* Function Prototypes */ -static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_confirmed(pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_terminated(pjsip_transaction *tsx, - pjsip_event *event); -static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx, +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_config.h>
+#include <pjsip/sip_misc.h>
+#include <pjsip/sip_event.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_errno.h>
+#include <pj/log.h>
+#include <pj/string.h>
+#include <pj/os.h>
+#include <pj/guid.h>
+#include <pj/pool.h>
+#include <pj/assert.h>
+
+/* Thread Local Storage ID for transaction lock (initialized by endpoint) */
+long pjsip_tsx_lock_tls_id;
+
+/* State names */
+static const char *state_str[] =
+{
+ "Null",
+ "Calling",
+ "Trying",
+ "Proceeding",
+ "Completed",
+ "Confirmed",
+ "Terminated",
+ "Destroyed",
+};
+
+/* Role names */
+static const char *role_name[] =
+{
+ "Client",
+ "Server"
+};
+
+/* Transaction lock. */
+typedef struct tsx_lock_data {
+ struct tsx_lock_data *prev;
+ pjsip_transaction *tsx;
+ int is_alive;
+} tsx_lock_data;
+
+
+/* Timer timeout value constants */
+static const pj_time_val t1_timer_val = { PJSIP_T1_TIMEOUT/1000,
+ PJSIP_T1_TIMEOUT%1000 };
+static const pj_time_val t4_timer_val = { PJSIP_T4_TIMEOUT/1000,
+ PJSIP_T4_TIMEOUT%1000 };
+static const pj_time_val td_timer_val = { PJSIP_TD_TIMEOUT/1000,
+ PJSIP_TD_TIMEOUT%1000 };
+static const pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000,
+ (64*PJSIP_T1_TIMEOUT)%1000 };
+
+/* Internal timer IDs */
+enum Transaction_Timer_Id
+{
+ TSX_TIMER_RETRANSMISSION,
+ TSX_TIMER_TIMEOUT,
+};
+
+/* Function Prototypes */
+static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
pjsip_event *event);
- -static void tsx_timer_callback( pj_timer_heap_t *theap, - pj_timer_entry *entry); +static pj_status_t pjsip_tsx_on_state_proceeding_uac( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_confirmed(pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_terminated(pjsip_transaction *tsx,
+ pjsip_event *event);
+static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
+ pjsip_event *event);
+
+static void tsx_timer_callback( pj_timer_heap_t *theap,
+ pj_timer_entry *entry);
static int tsx_send_msg( pjsip_transaction *tsx,
- pjsip_tx_data *tdata); + pjsip_tx_data *tdata);
static void lock_tsx( pjsip_transaction *tsx, struct
- tsx_lock_data *lck ); -static pj_status_t unlock_tsx( pjsip_transaction *tsx, - struct tsx_lock_data *lck ); - -/* State handlers for UAC, indexed by state */ -static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, - pjsip_event *) = -{ - &pjsip_tsx_on_state_null, - &pjsip_tsx_on_state_calling, - &pjsip_tsx_on_state_trying, - &pjsip_tsx_on_state_proceeding_uac, - &pjsip_tsx_on_state_completed_uac, - &pjsip_tsx_on_state_confirmed, - &pjsip_tsx_on_state_terminated, - &pjsip_tsx_on_state_destroyed, -}; - -/* State handlers for UAS */ -static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *, - pjsip_event *) = -{ - &pjsip_tsx_on_state_null, - &pjsip_tsx_on_state_calling, - &pjsip_tsx_on_state_trying, - &pjsip_tsx_on_state_proceeding_uas, - &pjsip_tsx_on_state_completed_uas, - &pjsip_tsx_on_state_confirmed, - &pjsip_tsx_on_state_terminated, - &pjsip_tsx_on_state_destroyed, -}; - -/* - * Get transaction state name. - */ -PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state) -{ - return state_str[state]; -} - -/* - * Get the role name. - */ -PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role) -{ - return role_name[role]; -} - - -/* - * Create transaction key for RFC2543 compliant messages, which don't have - * unique branch parameter in the top most Via header. - * - * INVITE requests matches a transaction if the following attributes - * match the original request: - * - Request-URI - * - To tag - * - From tag - * - Call-ID - * - CSeq - * - top Via header - * - * CANCEL matching is done similarly as INVITE, except: - * - CSeq method will differ - * - To tag is not matched. - * - * ACK matching is done similarly, except that: - * - method of the CSeq will differ, - * - To tag is matched to the response sent by the server transaction. - * - * The transaction key is constructed from the common components of above - * components. Additional comparison is needed to fully match a transaction. - */ -static pj_status_t create_tsx_key_2543( pj_pool_t *pool, - pj_str_t *str, - pjsip_role_e role, - const pjsip_method *method, - const pjsip_rx_data *rdata ) -{ -#define SEPARATOR '$' - char *key, *p, *end; - int len; - pj_size_t len_required; - pjsip_uri *req_uri; - pj_str_t *host; - - PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL); - PJ_ASSERT_RETURN(rdata->msg, PJ_EINVAL); - PJ_ASSERT_RETURN(rdata->via, PJSIP_EMISSINGHDR); - PJ_ASSERT_RETURN(rdata->cseq, PJSIP_EMISSINGHDR); - PJ_ASSERT_RETURN(rdata->from, PJSIP_EMISSINGHDR); - - host = &rdata->via->sent_by.host; - req_uri = (pjsip_uri*)rdata->msg->line.req.uri; - - /* Calculate length required. */ - len_required = 9 + /* CSeq number */ - rdata->from->tag.slen + /* From tag. */ - rdata->call_id.slen + /* Call-ID */ - host->slen + /* Via host. */ - 9 + /* Via port. */ - 16; /* Separator+Allowance. */ - key = p = pj_pool_alloc(pool, len_required); - end = p + len_required; - - /* Add role. */ - *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); - *p++ = SEPARATOR; - - /* Add Request-URI */ - /* This is BUG! - * Response doesn't have Request-URI! - * - len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p ); - p += len; - *p++ = SEPARATOR; - */ - - /* Add method, except when method is INVITE or ACK. */ - if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { - pj_memcpy(p, method->name.ptr, method->name.slen); - p += method->name.slen; - *p++ = '$'; - } - - /* Add CSeq (only the number). */ - len = pj_utoa(rdata->cseq->cseq, p); - p += len; - *p++ = SEPARATOR; - - /* Add From tag. */ - len = rdata->from->tag.slen; - pj_memcpy( p, rdata->from->tag.ptr, len); - p += len; - *p++ = SEPARATOR; - - /* Add Call-ID. */ - len = rdata->call_id.slen; - pj_memcpy( p, rdata->call_id.ptr, len ); - p += len; - *p++ = SEPARATOR; - - /* Add top Via header. - * We don't really care whether the port contains the real port (because - * it can be omited if default port is used). Anyway this function is - * only used to match request retransmission, and we expect that the - * request retransmissions will contain the same port. - */ - pj_memcpy(p, host->ptr, host->slen); - p += host->slen; - *p++ = ':'; - - len = pj_utoa(rdata->via->sent_by.port, p); - p += len; - *p++ = SEPARATOR; - - *p++ = '\0'; - - /* Done. */ - str->ptr = key; - str->slen = p-key; - - return PJ_SUCCESS; -} - -/* - * Create transaction key for RFC3161 compliant system. - */ -static pj_status_t create_tsx_key_3261( pj_pool_t *pool, - pj_str_t *key, - pjsip_role_e role, - const pjsip_method *method, - const pj_str_t *branch) -{ - char *p; - - PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL); - - p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 ); - - /* Add role. */ - *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's'); - *p++ = SEPARATOR; - - /* Add method, except when method is INVITE or ACK. */ - if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) { - pj_memcpy(p, method->name.ptr, method->name.slen); - p += method->name.slen; - *p++ = '$'; - } - - /* Add branch ID. */ - pj_memcpy(p, branch->ptr, branch->slen); - p += branch->slen; - - /* Set length */ - key->slen = p - key->ptr; - - return PJ_SUCCESS; -} - -/* - * Create key from the incoming data, to be used to search the transaction - * in the transaction hash table. - */ -PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key, - pjsip_role_e role, - const pjsip_method *method, - const pjsip_rx_data *rdata) -{ - pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID, - PJSIP_RFC3261_BRANCH_LEN}; - - - /* Get the branch parameter in the top-most Via. - * If branch parameter is started with "z9hG4bK", then the message was - * generated by agent compliant with RFC3261. Otherwise, it will be - * handled as RFC2543. - */ - const pj_str_t *branch = &rdata->via->branch_param; - - if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) { - - /* Create transaction key. */ - return create_tsx_key_3261(pool, key, role, method, branch); - - } else { - /* Create the key for the message. This key will be matched up - * with the transaction key. For RFC2563 transactions, the - * transaction key was created by the same function, so it will - * match the message. - */ - return create_tsx_key_2543( pool, key, role, method, rdata ); - } -} - - -/* - * Create new transaction. - */ -pj_status_t pjsip_tsx_create( pj_pool_t *pool, - pjsip_endpoint *endpt, - pjsip_transaction **p_tsx) -{ - pjsip_transaction *tsx; - pj_status_t status; - - tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction)); - - tsx->pool = pool; - tsx->endpt = endpt; - tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION; - tsx->retransmit_timer._timer_id = -1; - tsx->retransmit_timer.user_data = tsx; - tsx->retransmit_timer.cb = &tsx_timer_callback; - tsx->timeout_timer.id = TSX_TIMER_TIMEOUT; - tsx->timeout_timer._timer_id = -1; - tsx->timeout_timer.user_data = tsx; - tsx->timeout_timer.cb = &tsx_timer_callback; - pj_sprintf(tsx->obj_name, "tsx%p", tsx); - status = pj_mutex_create_recursive(pool, "mtsx%p", &tsx->mutex); - if (status != PJ_SUCCESS) { - return status; - } - - *p_tsx = tsx; - return PJ_SUCCESS; -} - -/* - * Lock transaction and set the value of Thread Local Storage. - */ -static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck) -{ - struct tsx_lock_data *prev_data; - - pj_mutex_lock(tsx->mutex); - prev_data = (struct tsx_lock_data *) - pj_thread_local_get(pjsip_tsx_lock_tls_id); - lck->prev = prev_data; - lck->tsx = tsx; - lck->is_alive = 1; - pj_thread_local_set(pjsip_tsx_lock_tls_id, lck); -} - - -/* - * Unlock transaction. - * This will selectively unlock the mutex ONLY IF the transaction has not been - * destroyed. The function knows whether the transaction has been destroyed - * because when transaction is destroyed the is_alive flag for the transaction - * will be set to zero. - */ -static pj_status_t unlock_tsx( pjsip_transaction *tsx, - struct tsx_lock_data *lck) -{ - pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck); - pj_assert( lck->tsx == tsx ); - pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev); - if (lck->is_alive) - pj_mutex_unlock(tsx->mutex); - - return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED; -} - -/* - * Set transaction state, and inform TU about the transaction state change. - */ -static void tsx_set_state( pjsip_transaction *tsx, - pjsip_tsx_state_e state, - pjsip_event_id_e event_src_type, - void *event_src ) -{ - pjsip_event e; - - PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, cause = %s", - state_str[tsx->state], state_str[state], - pjsip_event_str(event_src_type))); - - /* Change state. */ - tsx->state = state; - - /* Update the state handlers. */ - if (tsx->role == PJSIP_ROLE_UAC) { - tsx->state_handler = tsx_state_handler_uac[state]; - } else { - tsx->state_handler = tsx_state_handler_uas[state]; - } - - /* Inform TU */ - PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src); - pjsip_endpt_send_tsx_event( tsx->endpt, &e ); - - /* When the transaction is terminated, release transport, and free the - * saved last transmitted message. - */ - if (state == PJSIP_TSX_STATE_TERMINATED) { - - /* Decrement transport reference counter. */ - if (tsx->transport && - tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) - { - pjsip_transport_dec_ref( tsx->transport ); - tsx->transport = NULL; - } - /* Free last transmitted message. */ - if (tsx->last_tx) { - pjsip_tx_data_dec_ref( tsx->last_tx ); - tsx->last_tx = NULL; - } - /* Cancel timeout timer. */ - if (tsx->timeout_timer._timer_id != -1) { - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); - tsx->timeout_timer._timer_id = -1; - } - /* Cancel retransmission timer. */ - if (tsx->retransmit_timer._timer_id != -1) { - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); - tsx->retransmit_timer._timer_id = -1; - } - - /* If transport is not pending, reschedule timeout timer to - * destroy this transaction. - */ - if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { - pj_time_val timeout = {0, 0}; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout); - } - - } else if (state == PJSIP_TSX_STATE_DESTROYED) { - - /* Clear TLS, so that mutex will not be unlocked */ - struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id); - while (lck) { - if (lck->tsx == tsx) { - lck->is_alive = 0; - } - lck = lck->prev; - } - } -} - -/* - * Look-up destination address and select which transport to be used to send - * the request message. The procedure used here follows the guidelines on - * sending the request in RFC3261 chapter 8.1.2. - * - * This function also modifies the message (request line and Route headers) - * accordingly. - */ -static pj_status_t tsx_process_route( pjsip_transaction *tsx, - pjsip_tx_data *tdata, - pjsip_host_port *send_addr ) -{ - const pjsip_uri *new_request_uri, *target_uri; - const pjsip_name_addr *topmost_route_uri; - pjsip_route_hdr *first_route_hdr, *last_route_hdr; - - pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG); - - /* Get the first "Route" header from the message. If the message doesn't - * have any "Route" headers but the endpoint has, then copy the "Route" - * headers from the endpoint first. - */ - last_route_hdr = first_route_hdr = - pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL); - if (first_route_hdr) { - topmost_route_uri = &first_route_hdr->name_addr; - while (last_route_hdr->next != (void*)&tdata->msg->hdr) { - pjsip_route_hdr *hdr; - hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, - last_route_hdr->next); - if (!hdr) - break; - last_route_hdr = hdr; - } - } else { - const pjsip_route_hdr *hdr_list; - hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt); - if (hdr_list->next != hdr_list) { - const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next; - first_route_hdr = NULL; - topmost_route_uri = &hdr->name_addr; - do { - last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr); - if (first_route_hdr == NULL) - first_route_hdr = last_route_hdr; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr); - hdr = hdr->next; - } while (hdr != hdr_list); - } else { - topmost_route_uri = NULL; - } - } - - /* If Route headers exist, and the first element indicates loose-route, - * the URI is taken from the Request-URI, and we keep all existing Route - * headers intact. - * If Route headers exist, and the first element DOESN'T indicate loose - * route, the URI is taken from the first Route header, and remove the - * first Route header from the message. - * Otherwise if there's no Route headers, the URI is taken from the - * Request-URI. - */ - if (topmost_route_uri) { - pj_bool_t has_lr_param; - - if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) || - PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri)) - { - const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri); - has_lr_param = url->lr_param; - } else { - has_lr_param = 0; - } - - if (has_lr_param) { - new_request_uri = tdata->msg->line.req.uri; - /* We shouldn't need to delete topmost Route if it has lr param. - * But seems like it breaks some proxy implementation, so we - * delete it anyway. - */ - /* - pj_list_erase(first_route_hdr); - if (first_route_hdr == last_route_hdr) - last_route_hdr = NULL; - */ - } else { - new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri); - pj_list_erase(first_route_hdr); - if (first_route_hdr == last_route_hdr) - last_route_hdr = NULL; - } - - target_uri = (pjsip_uri*)topmost_route_uri; - - } else { - target_uri = new_request_uri = tdata->msg->line.req.uri; - } - - /* The target URI must be a SIP/SIPS URL so we can resolve it's address. - * Otherwise we're in trouble (i.e. there's no host part in tel: URL). - */ - pj_memset(send_addr, 0, sizeof(*send_addr)); - - if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) { - pjsip_uri *uri = (pjsip_uri*) target_uri; - const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); - send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE); - pj_strdup(tdata->pool, &send_addr->host, &url->host); - send_addr->port = url->port; - send_addr->type = - pjsip_transport_get_type_from_name(&url->transport_param); - - } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) { - pjsip_uri *uri = (pjsip_uri*) target_uri; - const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri); - pj_strdup(tdata->pool, &send_addr->host, &url->host); - send_addr->port = url->port; - send_addr->type = - pjsip_transport_get_type_from_name(&url->transport_param); -#if PJ_HAS_TCP - if (send_addr->type == PJSIP_TRANSPORT_TCP || - send_addr->type == PJSIP_TRANSPORT_SCTP) - { - send_addr->flag |= PJSIP_TRANSPORT_RELIABLE; - } -#endif - } else { - pj_assert(!"Unsupported URI scheme!"); - return PJSIP_EINVALIDSCHEME; - } - - /* If target URI is different than request URI, replace - * request URI add put the original URI in the last Route header. - */ - if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) { - pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool); - route->name_addr.uri = tdata->msg->line.req.uri; - if (last_route_hdr) - pj_list_insert_after(last_route_hdr, route); - else - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route); - tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri; - } - - /* Success. */ - return PJ_SUCCESS; -} - - -/* - * Callback from the transport job. - * This callback is called when asychronous transport connect() operation - * has completed, with or without error. - */ -static void tsx_transport_callback(pjsip_transport_t *tr, - void *token, - pj_status_t status) -{ - char addr[PJ_MAX_HOSTNAME]; - pjsip_transaction *tsx = token; - struct tsx_lock_data lck; - - pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen); - addr[tsx->dest_name.host.slen] = '\0'; - - - if (status == PJ_SUCCESS) { - PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d", - pjsip_transport_get_type_name(tr), - addr, tsx->dest_name.port)); - } else { - PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d", - pjsip_transport_get_type_name(tr), - addr, tsx->dest_name.port, status)); - } - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - if (status != PJ_SUCCESS) { - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); - - /* Unlock transaction. */ - unlock_tsx(tsx, &lck); - return; - } - - /* See if transaction has already been terminated. - * If so, schedule to destroy the transaction. - */ - if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { - pj_time_val timeout = {0, 0}; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout); - - /* Unlock transaction. */ - unlock_tsx(tsx, &lck); - return; - } - - /* Add reference counter to the transport. */ - pjsip_transport_add_ref(tr); - - /* Mark transport as ready. */ - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->transport = tr; - - /* If there's a pending message to send, send it now. */ - if (tsx->has_unsent_msg) { - tsx_send_msg( tsx, tsx->last_tx ); - } - - /* Unlock transaction. */ - unlock_tsx(tsx, &lck); -} - -/* - * Callback from the resolver job. - */ -static void tsx_resolver_callback(pj_status_t status, - void *token, - const struct pjsip_server_addresses *addr) -{ - pjsip_transaction *tsx = token; - struct tsx_lock_data lck; - - PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status)); - - if (status != PJ_SUCCESS || addr->count == 0) { - lock_tsx(tsx, &lck); - tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR; - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); - unlock_tsx(tsx, &lck); - return; - } - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - /* Copy server addresses. */ - pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr)); - - /* Create/find the transport for the remote address. */ - PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d", - pj_inet_ntoa(addr->entry[0].addr.sin_addr), - pj_ntohs(addr->entry[0].addr.sin_port))); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING; - pjsip_endpt_get_transport(tsx->endpt, tsx->pool, - addr->entry[0].type, &addr->entry[0].addr, - tsx, - &tsx_transport_callback); - - /* Unlock transaction */ - unlock_tsx(tsx, &lck); - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the callback for the - * transport is called. - */ -} - -/* - * Initialize the transaction as UAC transaction. - */ -PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx, - pjsip_tx_data *tdata) -{ - pjsip_msg *msg; - pjsip_cseq_hdr *cseq; - pjsip_via_hdr *via; - pj_status_t status; - struct tsx_lock_data lck; - - PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata)); - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - /* Keep shortcut */ - msg = tdata->msg; - - /* Role is UAC. */ - tsx->role = PJSIP_ROLE_UAC; - - /* Save method. */ - pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); - - /* Generate Via header if it doesn't exist. */ - via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); - if (via == NULL) { - via = pjsip_via_hdr_create(tdata->pool); - pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via); - } - - if (via->branch_param.slen == 0) { - pj_str_t tmp; - via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN); - via->branch_param.slen = PJSIP_MAX_BRANCH_LEN; - pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID, - PJSIP_RFC3261_BRANCH_LEN); - - tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN; - pj_generate_unique_string( &tmp ); - - /* Save branch parameter. */ - tsx->branch = via->branch_param; - } else { - /* Copy branch parameter. */ - pj_strdup(tsx->pool, &tsx->branch, &via->branch_param); - } - - - /* Generate transaction key. */ - status = create_tsx_key_3261( tsx->pool, &tsx->transaction_key, - PJSIP_ROLE_UAC, &tsx->method, - &via->branch_param); - if (status != PJ_SUCCESS) { - unlock_tsx(tsx, &lck); - return status; - } - - PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, - tsx->transaction_key.ptr)); - - /* Save CSeq. */ - cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL); - if (!cseq) { - pj_assert(!"CSeq header not present in outgoing message!"); - unlock_tsx(tsx, &lck); - return PJSIP_EMISSINGHDR; - } - tsx->cseq = cseq->cseq; - - - /* Begin with State_Null. - * Manually set-up the state becase we don't want to call the callback. - */ - tsx->state = PJSIP_TSX_STATE_NULL; - tsx->state_handler = &pjsip_tsx_on_state_null; - - /* Get destination name from the message. */ - status = tsx_process_route(tsx, tdata, &tsx->dest_name); - if (status != PJ_SUCCESS) { - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); - unlock_tsx(tsx, &lck); - return status; - } - - /* Resolve destination. - * This will start asynchronous resolver job, and when it finishes, - * the callback will be called. - */ - PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", - tsx->dest_name.host.slen, - tsx->dest_name.host.ptr, - tsx->dest_name.port)); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, - tsx, &tsx_resolver_callback); - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the resolver callback is - * called. - */ - - /* Unlock transaction and return. - * If transaction has been destroyed WITHIN the current thread, the - * unlock_tsx() function will return -1. - */ - return unlock_tsx(tsx, &lck); -} - - -/* - * Initialize the transaction as UAS transaction. - */ -PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - pjsip_msg *msg = rdata->msg; - pj_str_t *branch; - pjsip_cseq_hdr *cseq; - pj_status_t status; - struct tsx_lock_data lck; - - PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata)); - - /* Lock transaction. */ - lock_tsx(tsx, &lck); - - /* Keep shortcut to message */ - msg = rdata->msg; - - /* Role is UAS */ - tsx->role = PJSIP_ROLE_UAS; - - /* Save method. */ - pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method); - - /* Get transaction key either from branch for RFC3261 message, or - * create transaction key. - */ - status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key, - PJSIP_ROLE_UAS, &tsx->method, rdata); - if (status != PJ_SUCCESS) { - unlock_tsx(tsx, &lck); - return status; - } - - /* Duplicate branch parameter for transaction. */ - branch = &rdata->via->branch_param; - pj_strdup(tsx->pool, &tsx->branch, branch); - - PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, - tsx->transaction_key.ptr)); - - /* Save CSeq */ - cseq = rdata->cseq; - tsx->cseq = cseq->cseq; - - /* Begin with state NULL - * Manually set-up the state becase we don't want to call the callback. - */ - tsx->state = PJSIP_TSX_STATE_NULL; - tsx->state_handler = &pjsip_tsx_on_state_null; - - /* Get the transport to send the response. - * According to section 18.2.2 of RFC3261, if the transport is reliable - * then the response must be sent using that transport. - */ - /* In addition, RFC 3581 says, if Via has "rport" parameter specified, - * then return the response using the same transport. - */ - if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->transport) || - rdata->via->rport_param >= 0) - { - tsx->transport = rdata->transport; - pjsip_transport_add_ref(tsx->transport); - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - - tsx->current_addr = 0; - tsx->remote_addr.count = 1; - tsx->remote_addr.entry[0].type = - pjsip_transport_get_type(tsx->transport); - pj_memcpy(&tsx->remote_addr.entry[0].addr, - &rdata->addr, rdata->addr_len); - - } else { - pj_status_t status; - - status = pjsip_get_response_addr(tsx->pool, rdata->transport, - rdata->via, &tsx->dest_name); - if (status != PJ_SUCCESS) { - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL; - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); - unlock_tsx(tsx, &lck); - return status; - } - - /* Resolve destination. - * This will start asynchronous resolver job, and when it finishes, - * the callback will be called. - */ - PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", - tsx->dest_name.host.slen, - tsx->dest_name.host.ptr, - tsx->dest_name.port)); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, - tsx, &tsx_resolver_callback); - } - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the resolver callback is - * called. - */ - - /* Unlock transaction and return. - * If transaction has been destroyed WITHIN the current thread, the - * unlock_tsx() function will return -1. - */ - return unlock_tsx(tsx, &lck); -} - -/* - * Callback when timer expires. - */ -static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry) -{ - pjsip_event event; - pjsip_transaction *tsx = entry->user_data; - struct tsx_lock_data lck; - - PJ_UNUSED_ARG(theap); - - PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)", - (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout"))); - - - if (entry->id == TSX_TIMER_RETRANSMISSION) { - PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer); - } else { - PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer); - } - - /* Dispatch event to transaction. */ - lock_tsx(tsx, &lck); - (*tsx->state_handler)(tsx, &event); - unlock_tsx(tsx, &lck); -} - -/* - * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for - * non-2xx/INVITE is automatically sent by the transaction. - * This operation is only valid if the transaction is configured to handle ACK - * (tsx->handle_ack is non-zero). If this attribute is not set, then the - * transaction will comply with RFC-3261, i.e. it will set itself to - * TERMINATED state when it receives 2xx/INVITE. - */ -PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata) -{ - pjsip_msg *msg; - pjsip_host_port dest_addr; - pjsip_via_hdr *via; - struct tsx_lock_data lck; - pj_status_t status = PJ_SUCCESS; - - /* Lock tsx. */ - lock_tsx(tsx, &lck); - - pj_assert(tsx->handle_ack != 0); - - msg = tdata->msg; - - /* Generate branch parameter if it doesn't exist. */ - via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL); - if (via == NULL) { - via = pjsip_via_hdr_create(tdata->pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) via); - } - - if (via->branch_param.slen == 0) { - via->branch_param = tsx->branch; - } else { - pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 ); - } - - /* Get destination name from the message. */ - status = tsx_process_route(tsx, tdata, &dest_addr); - if (status != 0){ - goto on_error; - } - - /* Compare message's destination name with transaction's destination name. - * If NOT equal, then we'll have to resolve the destination. - */ - if (dest_addr.type == tsx->dest_name.type && - dest_addr.flag == tsx->dest_name.flag && - dest_addr.port == tsx->dest_name.port && - pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0) - { - /* Equal destination. We can use current transport. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - unlock_tsx(tsx, &lck); - return; - - } - - /* New destination; we'll have to resolve host and create new transport. */ - pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr)); - pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host); - - PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d", - tsx->dest_name.host.slen, - tsx->dest_name.host.ptr, - tsx->dest_name.port)); - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_transport_dec_ref(tsx->transport); - tsx->transport = NULL; - - /* Put the message in queue. */ - pjsip_tsx_on_tx_msg(tsx, tdata); - - /* This is a bug! - * We shouldn't change transaction's state before actually sending the - * message. Otherwise transaction will terminate before message is sent, - * and timeout timer will be scheduled. - */ - PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK) - - /* - * This will start asynchronous resolver job, and when it finishes, - * the callback will be called. - */ - - tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING; - pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name, - tsx, &tsx_resolver_callback); - - unlock_tsx(tsx, &lck); - - /* There should be nothing to do after this point. - * Execution for the transaction will resume when the resolver callback is - * called. - */ - return; - -on_error: - /* Failure condition. - * Send TERMINATED event. - */ - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); - - unlock_tsx(tsx, &lck); -} - - -/* - * This function is called by TU to send a message. - */ -PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx, - pjsip_tx_data *tdata ) -{ - pjsip_event event; - struct tsx_lock_data lck; - pj_status_t status; - - PJ_LOG(5,(tsx->obj_name, "Request to transmit msg on state %s (tdata=%p)", - state_str[tsx->state], tdata)); - - PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata); - - /* Dispatch to transaction. */ - lock_tsx(tsx, &lck); - status = (*tsx->state_handler)(tsx, &event); - unlock_tsx(tsx, &lck); -} - -/* - * This function is called by endpoint when incoming message for the - * transaction is received. - */ -PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - pjsip_event event; - struct tsx_lock_data lck; - pj_status_t status; - - PJ_LOG(5,(tsx->obj_name, "Incoming msg on state %s (rdata=%p)", - state_str[tsx->state], rdata)); - - PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata); - - /* Dispatch to transaction. */ - lock_tsx(tsx, &lck); - status = (*tsx->state_handler)(tsx, &event); - unlock_tsx(tsx, &lck); -} - -/* - * Forcely terminate transaction. - */ -PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code ) -{ - struct tsx_lock_data lck; - - lock_tsx(tsx, &lck); - tsx->status_code = code; - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_USER, NULL); - - unlock_tsx(tsx, &lck); -} - -/* - * Send message to the transport. - * If transport is not yet available, then do nothing. The message will be - * transmitted when transport connection completion callback is called. - */ -static pj_status_t tsx_send_msg( pjsip_transaction *tsx, - pjsip_tx_data *tdata) -{ - pj_status_t status = PJ_SUCCESS; - - PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata)); - - if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) { - pj_ssize_t sent; - pjsip_event before_tx_event; - - pj_assert(tsx->transport != NULL); - - /* Make sure Via transport info is filled up properly for - * requests. - */ - if (tdata->msg->type == PJSIP_REQUEST_MSG) { - const pj_sockaddr_in *addr_name; - pjsip_via_hdr *via = (pjsip_via_hdr*) - pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL); - - /* For request message, set "rport" parameter by default. */ - if (tdata->msg->type == PJSIP_REQUEST_MSG) - via->rport_param = 0; - - /* Don't update Via sent-by on retransmission. */ - if (via->sent_by.host.slen == 0) { - addr_name = pjsip_transport_get_addr_name(tsx->transport); - pj_strdup2(tdata->pool, &via->transport, - pjsip_transport_get_type_name(tsx->transport)); - pj_strdup2(tdata->pool, &via->sent_by.host, - pj_inet_ntoa(addr_name->sin_addr)); - via->sent_by.port = pj_ntohs(addr_name->sin_port); - } - } - - /* Notify everybody we're about to send message. */ - PJSIP_EVENT_INIT_PRE_TX_MSG(before_tx_event, tsx, tdata, - tsx->retransmit_count); - pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event ); - - tsx->has_unsent_msg = 0; - status = pjsip_transport_send_msg( - tsx->transport, tdata, - &tsx->remote_addr.entry[tsx->current_addr].addr, - &sent); - if (status != PJ_SUCCESS) { - goto on_error; - } - } else { - tsx->has_unsent_msg = 1; - } - - return 0; - -on_error: - tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR; - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TRANSPORT_ERROR, (void*)status); - return status; -} - -/* - * Retransmit last message sent. - */ -static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx, - int should_restart_timer) -{ - pj_status_t status; - - PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)", - tsx->last_tx, tsx->retransmit_count, should_restart_timer)); - - pj_assert(tsx->last_tx != NULL); - - ++tsx->retransmit_count; - - status = tsx_send_msg( tsx, tsx->last_tx); - if (status != PJ_SUCCESS) { - return status; - } - - /* Restart timer T1. */ - if (should_restart_timer) { - pj_time_val timeout; - int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT; - - if (tsx->method.id!=PJSIP_INVITE_METHOD && msec_time>PJSIP_T2_TIMEOUT) - msec_time = PJSIP_T2_TIMEOUT; - - timeout.sec = msec_time / 1000; - timeout.msec = msec_time % 1000; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, - &timeout); - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in state Null. - */ -static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx, - pjsip_event *event ) -{ - pj_status_t status; - - pj_assert( tsx->state == PJSIP_TSX_STATE_NULL); - pj_assert( tsx->last_tx == NULL ); - pj_assert( tsx->has_unsent_msg == 0); - - if (tsx->role == PJSIP_ROLE_UAS) { - - /* Set state to Trying. */ - pj_assert(event->type == PJSIP_EVENT_RX_MSG); - tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); - - } else { - pjsip_tx_data *tdata = event->body.tx_msg.tdata; - - /* Save the message for retransmission. */ - tsx->last_tx = tdata; - pjsip_tx_data_add_ref(tdata); - - /* Send the message. */ - status = tsx_send_msg( tsx, tdata); - if (status != PJ_SUCCESS) { - return status; - } - - /* Start Timer B (or called timer F for non-INVITE) for transaction - * timeout. - */ - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout_timer_val); - - /* Start Timer A (or timer E) for retransmission only if unreliable - * transport is being used. - */ - if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL && - PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) - { - pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer, - &t1_timer_val); - tsx->retransmit_count = 0; - } - - /* Move state. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING, - PJSIP_EVENT_TX_MSG, tdata); - } - - return PJ_SUCCESS; -} - -/* - * State Calling is for UAC after it sends request but before any responses - * is received. - */ -static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx, - pjsip_event *event ) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING); - pj_assert(tsx->role == PJSIP_ROLE_UAC); - - if (event->type == PJSIP_EVENT_TIMER && - event->body.timer.entry == &tsx->retransmit_timer) - { - pj_status_t status; - - /* Retransmit the request. */ - status = pjsip_tsx_retransmit( tsx, 1 ); - if (status != PJ_SUCCESS) { - return status; - } - - } else if (event->type == PJSIP_EVENT_TIMER && - event->body.timer.entry == &tsx->timeout_timer) - { - - /* Cancel retransmission timer. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); - } - - /* Set status code */ - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - - /* Inform TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer); - - /* Transaction is destroyed */ - return PJSIP_ETSXDESTROYED; - - } else if (event->type == PJSIP_EVENT_RX_MSG) { - int code; - - /* Cancel retransmission timer A. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer); - - /* Cancel timer B (transaction timeout) */ - pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer); - - /* Discard retransmission message if it is not INVITE. - * The INVITE tdata is needed in case we have to generate ACK for - * the final response. - */ - /* Keep last_tx for authorization. */ - code = event->body.rx_msg.rdata->msg->line.status.code; - if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) { - pjsip_tx_data_dec_ref(tsx->last_tx); - tsx->last_tx = NULL; - } - - /* Processing is similar to state Proceeding. */ - pjsip_tsx_on_state_proceeding_uac( tsx, event); - - } else { - pj_assert(0); - return PJ_EBUG; - } - - return PJ_SUCCESS; -} - -/* - * State Trying is for UAS after it received request but before any responses - * is sent. - * Note: this is different than RFC3261, which can use Trying state for - * non-INVITE client transaction (bug in RFC?). - */ -static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx, - pjsip_event *event) -{ - pj_status_t status; - - pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING); - - /* This state is only for UAS */ - pj_assert(tsx->role == PJSIP_ROLE_UAS); - - /* Better be transmission of response message. - * If we've got request retransmission, this means that the TU hasn't - * transmitted any responses within 500 ms, which is not allowed. If - * this happens, just ignore the event (we couldn't retransmit last - * response because we haven't sent any!). - */ - //pj_assert(event->type == PJSIP_EVENT_TX_MSG); - if (event->type != PJSIP_EVENT_TX_MSG) { - return PJ_SUCCESS; - } - - /* The rest of the processing of the event is exactly the same as in - * "Proceeding" state. - */ - status = pjsip_tsx_on_state_proceeding_uas( tsx, event); - - /* Inform the TU of the state transision if state is still State_Trying */ - if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) { - tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, - PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata); - } - - return status; -} - -/* - * Handler for events in Proceeding for UAS - * This state happens after the TU sends provisional response. - */ -static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx, - pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || - tsx->state == PJSIP_TSX_STATE_TRYING); - - /* This state is only for UAS. */ - pj_assert(tsx->role == PJSIP_ROLE_UAS); - - /* Receive request retransmission. */ - if (event->type == PJSIP_EVENT_RX_MSG) { - - pj_status_t status; - - /* Send last response. */ - status = pjsip_tsx_retransmit( tsx, 0 ); - if (status != PJ_SUCCESS) { - return status; - } - - } else if (event->type == PJSIP_EVENT_TX_MSG ) { - pjsip_tx_data *tdata = event->body.tx_msg.tdata; - pj_status_t status; - - /* The TU sends response message to the request. Save this message so - * that we can retransmit the last response in case we receive request - * retransmission. - */ - pjsip_msg *msg = tdata->msg; - - /* This can only be a response message. */ - pj_assert(msg->type == PJSIP_RESPONSE_MSG); - - /* Status code must be higher than last sent. */ - pj_assert(msg->line.status.code >= tsx->status_code); - - /* Update last status */ - tsx->status_code = msg->line.status.code; - - /* Discard the saved last response (it will be updated later as - * necessary). - */ - if (tsx->last_tx && tsx->last_tx != tdata) { - pjsip_tx_data_dec_ref( tsx->last_tx ); - tsx->last_tx = NULL; - } - - /* Send the message. */ - status = tsx_send_msg(tsx, tdata); - if (status != PJ_SUCCESS) { - return status; - } - - // Update To tag header for RFC2543 transaction. - // TODO: - - /* Update transaction state */ - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { - - if (tsx->last_tx != tdata) { - tsx->last_tx = tdata; - pjsip_tx_data_add_ref( tdata ); - } - tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, - PJSIP_EVENT_TX_MSG, tdata ); - - } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) { - - if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) { - - /* 2xx class message is not saved, because retransmission - * is handled by TU. - */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TX_MSG, tdata ); - - /* Transaction is destroyed. */ - return PJSIP_ETSXDESTROYED; - - } else { - pj_time_val timeout; - - if (tsx->method.id == PJSIP_INVITE_METHOD) { - tsx->retransmit_count = 0; - pjsip_endpt_schedule_timer( tsx->endpt, - &tsx->retransmit_timer, - &t1_timer_val); - } - - /* Save last response sent for retransmission when request - * retransmission is received. - */ - if (tsx->last_tx != tdata) { - tsx->last_tx = tdata; - pjsip_tx_data_add_ref(tdata); - } - - /* Start timer J at 64*T1 for unreliable transport or zero for - * reliable transport. - */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { - timeout = timeout_timer_val; - } else { - timeout.sec = timeout.msec = 0; - } - - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout); - - /* Set state to "Completed" */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_TX_MSG, tdata ); - } - - } else if (tsx->status_code >= 300) { - - /* 3xx-6xx class message causes transaction to move to - * "Completed" state. - */ - if (tsx->last_tx != tdata) { - tsx->last_tx = tdata; - pjsip_tx_data_add_ref( tdata ); - } - - /* Start timer H for transaction termination */ - pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer, - &timeout_timer_val); - - /* For INVITE, if unreliable transport is used, retransmission - * timer G will be scheduled (retransmission). - */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) { - pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, - NULL); - if (cseq->method.id == PJSIP_INVITE_METHOD) { - tsx->retransmit_count = 0; - pjsip_endpt_schedule_timer(tsx->endpt, - &tsx->retransmit_timer, - &t1_timer_val); - } - } - - /* Inform TU */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_TX_MSG, tdata ); - - } else { - pj_assert(0); - } - - - } else if (event->type == PJSIP_EVENT_TIMER && - event->body.timer.entry == &tsx->retransmit_timer) { - /* Retransmission timer elapsed. */ - pj_status_t status; - - /* Must have last response to retransmit. */ - pj_assert(tsx->last_tx != NULL); - - /* Retransmit the last response. */ - status = pjsip_tsx_retransmit( tsx, 1 ); - if (status != PJ_SUCCESS) { - return status; - } - - } else if (event->type == PJSIP_EVENT_TIMER && - event->body.timer.entry == &tsx->timeout_timer) { - - /* Timeout timer. should not happen? */ - pj_assert(0); - - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer); - - return PJ_EBUG; - - } else { - pj_assert(0); - return PJ_EBUG; - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in Proceeding for UAC - * This state happens after provisional response(s) has been received from - * UAS. - */ -static pj_status_t pjsip_tsx_on_state_proceeding_uac(pjsip_transaction *tsx, - pjsip_event *event) -{ - - pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING || - tsx->state == PJSIP_TSX_STATE_CALLING); - - if (event->type != PJSIP_EVENT_TIMER) { - /* Must be incoming response, because we should not retransmit - * request once response has been received. - */ - pj_assert(event->type == PJSIP_EVENT_RX_MSG); - if (event->type != PJSIP_EVENT_RX_MSG) { - return PJ_EINVALIDOP; - } - - tsx->status_code = event->body.rx_msg.rdata->msg->line.status.code; - } else { - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - } - - if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) { - - /* Inform the message to TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); - - } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { - - /* Stop timeout timer B/F. */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); - - /* For INVITE, the state moves to Terminated state (because ACK is - * handled in TU). For non-INVITE, state moves to Completed. - */ - if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) { - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); - return PJSIP_ETSXDESTROYED; - - } else { - pj_time_val timeout; - - /* For unreliable transport, start timer D (for INVITE) or - * timer K for non-INVITE. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - timeout = td_timer_val; - } else { - timeout = t4_timer_val; - } - } else { - timeout.sec = timeout.msec = 0; - } - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &timeout); - - /* Move state to Completed, inform TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); - } - - } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { - pj_time_val timeout; - pj_status_t status; - - /* Stop timer B. */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); - - /* Generate and send ACK for INVITE. */ - if (tsx->method.id == PJSIP_INVITE_METHOD) { - pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, - event->body.rx_msg.rdata ); - status = tsx_send_msg( tsx, tsx->last_tx); - if (status != PJ_SUCCESS) { - return status; - } - } - - /* Start Timer D with TD/T4 timer if unreliable transport is used. */ - if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - timeout = td_timer_val; - } else { - timeout = t4_timer_val; - } - } else { - timeout.sec = timeout.msec = 0; - } - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); - - /* Inform TU. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); - - } else { - // Shouldn't happen because there's no timer for this state. - pj_assert(0); - return PJ_EBUG; - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in Completed state for UAS - */ -static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx, - pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); - - if (event->type == PJSIP_EVENT_RX_MSG) { - pjsip_msg *msg = event->body.rx_msg.rdata->msg; - pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL ); - - /* On receive request retransmission, retransmit last response. */ - if (cseq->method.id != PJSIP_ACK_METHOD) { - pj_status_t status; - - status = pjsip_tsx_retransmit( tsx, 0 ); - if (status != PJ_SUCCESS) { - return status; - } - - } else { - /* Process incoming ACK request. */ - - /* Cease retransmission. */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer ); - - /* Start timer I in T4 interval (transaction termination). */ - pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, - &t4_timer_val); - - /* Move state to "Confirmed" */ - tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED, - PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); - } - - } else if (event->type == PJSIP_EVENT_TIMER) { - - if (event->body.timer.entry == &tsx->retransmit_timer) { - /* Retransmit message. */ - pj_status_t status; - - status = pjsip_tsx_retransmit( tsx, 1 ); - if (status != PJ_SUCCESS) { - return status; - } - - } else { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - - /* For INVITE, this means that ACK was never received. - * Set state to Terminated, and inform TU. - */ - - tsx->status_code = PJSIP_SC_TSX_TIMEOUT; - - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer ); - - return PJSIP_ETSXDESTROYED; - - } else { - /* Transaction terminated, it can now be deleted. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer ); - return PJSIP_ETSXDESTROYED; - } - } - - } else { - /* Ignore request to transmit. */ - pj_assert(event->body.tx_msg.tdata == tsx->last_tx); - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in Completed state for UAC transaction. - */ -static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx, - pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED); - - if (event->type == PJSIP_EVENT_TIMER) { - /* Must be the timeout timer. */ - pj_assert(event->body.timer.entry == &tsx->timeout_timer); - - /* Move to Terminated state. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, event->body.timer.entry ); - - /* Transaction has been destroyed. */ - return PJSIP_ETSXDESTROYED; - - } else if (event->type == PJSIP_EVENT_RX_MSG) { - if (tsx->method.id == PJSIP_INVITE_METHOD) { - /* On received of final response retransmission, retransmit the ACK. - * TU doesn't need to be informed. - */ - pjsip_msg *msg = event->body.rx_msg.rdata->msg; - pj_assert(msg->type == PJSIP_RESPONSE_MSG); - if (msg->type==PJSIP_RESPONSE_MSG && - msg->line.status.code >= 200) - { - pj_status_t status; - - status = pjsip_tsx_retransmit( tsx, 0 ); - if (status != PJ_SUCCESS) { - return status; - } - } else { - /* Very late retransmission of privisional response. */ - pj_assert( msg->type == PJSIP_RESPONSE_MSG ); - } - } else { - /* Just drop the response. */ - } - } else if (tsx->method.id == PJSIP_INVITE_METHOD && - event->type == PJSIP_EVENT_TX_MSG && - event->body.tx_msg.tdata->msg->line.req.method.id==PJSIP_ACK_METHOD) { - - pj_status_t status; - - /* Set last transmitted message. */ - if (tsx->last_tx != event->body.tx_msg.tdata) { - pjsip_tx_data_dec_ref( tsx->last_tx ); - tsx->last_tx = event->body.tx_msg.tdata; - pjsip_tx_data_add_ref( tsx->last_tx ); - } - - /* No state changed, but notify app. - * Must notify now, so app has chance to put SDP in outgoing ACK msg. - */ - tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, - PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata ); - - /* Send msg */ - status = tsx_send_msg(tsx, event->body.tx_msg.tdata); - if (status != PJ_SUCCESS) - return status; - - } else { - pj_assert(0); - return PJ_EBUG; - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in state Confirmed. - */ -static pj_status_t pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx, - pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED); - - /* This state is only for UAS for INVITE. */ - pj_assert(tsx->role == PJSIP_ROLE_UAS); - pj_assert(tsx->method.id == PJSIP_INVITE_METHOD); - - /* Absorb any ACK received. */ - if (event->type == PJSIP_EVENT_RX_MSG) { - - pjsip_method_e method_id = - event->body.rx_msg.rdata->msg->line.req.method.id; - - /* Must be a request message. */ - pj_assert(event->body.rx_msg.rdata->msg->type == PJSIP_REQUEST_MSG); - - /* Must be an ACK request or a late INVITE retransmission. */ - pj_assert(method_id == PJSIP_ACK_METHOD || - method_id == PJSIP_INVITE_METHOD); - - /* Just so that compiler won't complain about unused vars when - * building release code. - */ - PJ_UNUSED_ARG(method_id); - - } else if (event->type == PJSIP_EVENT_TIMER) { - /* Must be from timeout_timer_. */ - pj_assert(event->body.timer.entry == &tsx->timeout_timer); - - /* Move to Terminated state. */ - tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED, - PJSIP_EVENT_TIMER, &tsx->timeout_timer ); - - /* Transaction has been destroyed. */ - return PJSIP_ETSXDESTROYED; - - } else { - pj_assert(0); - return PJ_EBUG; - } - - return PJ_SUCCESS; -} - -/* - * Handler for events in state Terminated. - */ -static pj_status_t pjsip_tsx_on_state_terminated( pjsip_transaction *tsx, - pjsip_event *event) -{ - pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED); - - PJ_UNUSED_ARG(event); - - /* Destroy this transaction */ - tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED, - event->type, event->body.user.user1 ); - - return PJ_SUCCESS; -} - - -static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx, - pjsip_event *event) -{ - PJ_UNUSED_ARG(tsx); - PJ_UNUSED_ARG(event); - return PJ_SUCCESS; -} - + tsx_lock_data *lck );
+static pj_status_t unlock_tsx( pjsip_transaction *tsx,
+ struct tsx_lock_data *lck );
+
+/* State handlers for UAC, indexed by state */
+static int (*tsx_state_handler_uac[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
+ pjsip_event *) =
+{
+ &pjsip_tsx_on_state_null,
+ &pjsip_tsx_on_state_calling,
+ &pjsip_tsx_on_state_trying,
+ &pjsip_tsx_on_state_proceeding_uac,
+ &pjsip_tsx_on_state_completed_uac,
+ &pjsip_tsx_on_state_confirmed,
+ &pjsip_tsx_on_state_terminated,
+ &pjsip_tsx_on_state_destroyed,
+};
+
+/* State handlers for UAS */
+static int (*tsx_state_handler_uas[PJSIP_TSX_STATE_MAX])(pjsip_transaction *,
+ pjsip_event *) =
+{
+ &pjsip_tsx_on_state_null,
+ &pjsip_tsx_on_state_calling,
+ &pjsip_tsx_on_state_trying,
+ &pjsip_tsx_on_state_proceeding_uas,
+ &pjsip_tsx_on_state_completed_uas,
+ &pjsip_tsx_on_state_confirmed,
+ &pjsip_tsx_on_state_terminated,
+ &pjsip_tsx_on_state_destroyed,
+};
+
+/*
+ * Get transaction state name.
+ */
+PJ_DEF(const char *) pjsip_tsx_state_str(pjsip_tsx_state_e state)
+{
+ return state_str[state];
+}
+
+/*
+ * Get the role name.
+ */
+PJ_DEF(const char *) pjsip_role_name(pjsip_role_e role)
+{
+ return role_name[role];
+}
+
+
+/*
+ * Create transaction key for RFC2543 compliant messages, which don't have
+ * unique branch parameter in the top most Via header.
+ *
+ * INVITE requests matches a transaction if the following attributes
+ * match the original request:
+ * - Request-URI
+ * - To tag
+ * - From tag
+ * - Call-ID
+ * - CSeq
+ * - top Via header
+ *
+ * CANCEL matching is done similarly as INVITE, except:
+ * - CSeq method will differ
+ * - To tag is not matched.
+ *
+ * ACK matching is done similarly, except that:
+ * - method of the CSeq will differ,
+ * - To tag is matched to the response sent by the server transaction.
+ *
+ * The transaction key is constructed from the common components of above
+ * components. Additional comparison is needed to fully match a transaction.
+ */
+static pj_status_t create_tsx_key_2543( pj_pool_t *pool,
+ pj_str_t *str,
+ pjsip_role_e role,
+ const pjsip_method *method,
+ const pjsip_rx_data *rdata )
+{
+#define SEPARATOR '$'
+ char *key, *p, *end;
+ int len;
+ pj_size_t len_required;
+ pjsip_uri *req_uri;
+ pj_str_t *host;
+
+ PJ_ASSERT_RETURN(pool && str && method && rdata, PJ_EINVAL);
+ PJ_ASSERT_RETURN(rdata->msg, PJ_EINVAL);
+ PJ_ASSERT_RETURN(rdata->via, PJSIP_EMISSINGHDR);
+ PJ_ASSERT_RETURN(rdata->cseq, PJSIP_EMISSINGHDR);
+ PJ_ASSERT_RETURN(rdata->from, PJSIP_EMISSINGHDR);
+
+ host = &rdata->via->sent_by.host;
+ req_uri = (pjsip_uri*)rdata->msg->line.req.uri;
+
+ /* Calculate length required. */
+ len_required = 9 + /* CSeq number */
+ rdata->from->tag.slen + /* From tag. */
+ rdata->call_id.slen + /* Call-ID */
+ host->slen + /* Via host. */
+ 9 + /* Via port. */
+ 16; /* Separator+Allowance. */
+ key = p = pj_pool_alloc(pool, len_required);
+ end = p + len_required;
+
+ /* Add role. */
+ *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+ *p++ = SEPARATOR;
+
+ /* Add Request-URI */
+ /* This is BUG!
+ * Response doesn't have Request-URI!
+ *
+ len = req_uri->vptr->print( PJSIP_URI_IN_REQ_URI, req_uri, p, end-p );
+ p += len;
+ *p++ = SEPARATOR;
+ */
+
+ /* Add method, except when method is INVITE or ACK. */
+ if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+ pj_memcpy(p, method->name.ptr, method->name.slen);
+ p += method->name.slen;
+ *p++ = '$';
+ }
+
+ /* Add CSeq (only the number). */
+ len = pj_utoa(rdata->cseq->cseq, p);
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add From tag. */
+ len = rdata->from->tag.slen;
+ pj_memcpy( p, rdata->from->tag.ptr, len);
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add Call-ID. */
+ len = rdata->call_id.slen;
+ pj_memcpy( p, rdata->call_id.ptr, len );
+ p += len;
+ *p++ = SEPARATOR;
+
+ /* Add top Via header.
+ * We don't really care whether the port contains the real port (because
+ * it can be omited if default port is used). Anyway this function is
+ * only used to match request retransmission, and we expect that the
+ * request retransmissions will contain the same port.
+ */
+ pj_memcpy(p, host->ptr, host->slen);
+ p += host->slen;
+ *p++ = ':';
+
+ len = pj_utoa(rdata->via->sent_by.port, p);
+ p += len;
+ *p++ = SEPARATOR;
+
+ *p++ = '\0';
+
+ /* Done. */
+ str->ptr = key;
+ str->slen = p-key;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create transaction key for RFC3161 compliant system.
+ */
+static pj_status_t create_tsx_key_3261( pj_pool_t *pool,
+ pj_str_t *key,
+ pjsip_role_e role,
+ const pjsip_method *method,
+ const pj_str_t *branch)
+{
+ char *p;
+
+ PJ_ASSERT_RETURN(pool && key && method && branch, PJ_EINVAL);
+
+ p = key->ptr = pj_pool_alloc(pool, branch->slen + method->name.slen + 4 );
+
+ /* Add role. */
+ *p++ = (char)(role==PJSIP_ROLE_UAC ? 'c' : 's');
+ *p++ = SEPARATOR;
+
+ /* Add method, except when method is INVITE or ACK. */
+ if (method->id != PJSIP_INVITE_METHOD && method->id != PJSIP_ACK_METHOD) {
+ pj_memcpy(p, method->name.ptr, method->name.slen);
+ p += method->name.slen;
+ *p++ = '$';
+ }
+
+ /* Add branch ID. */
+ pj_memcpy(p, branch->ptr, branch->slen);
+ p += branch->slen;
+
+ /* Set length */
+ key->slen = p - key->ptr;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create key from the incoming data, to be used to search the transaction
+ * in the transaction hash table.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_create_key( pj_pool_t *pool, pj_str_t *key,
+ pjsip_role_e role,
+ const pjsip_method *method,
+ const pjsip_rx_data *rdata)
+{
+ pj_str_t rfc3261_branch = {PJSIP_RFC3261_BRANCH_ID,
+ PJSIP_RFC3261_BRANCH_LEN};
+
+
+ /* Get the branch parameter in the top-most Via.
+ * If branch parameter is started with "z9hG4bK", then the message was
+ * generated by agent compliant with RFC3261. Otherwise, it will be
+ * handled as RFC2543.
+ */
+ const pj_str_t *branch = &rdata->via->branch_param;
+
+ if (pj_strncmp(branch,&rfc3261_branch,PJSIP_RFC3261_BRANCH_LEN)==0) {
+
+ /* Create transaction key. */
+ return create_tsx_key_3261(pool, key, role, method, branch);
+
+ } else {
+ /* Create the key for the message. This key will be matched up
+ * with the transaction key. For RFC2563 transactions, the
+ * transaction key was created by the same function, so it will
+ * match the message.
+ */
+ return create_tsx_key_2543( pool, key, role, method, rdata );
+ }
+}
+
+
+/*
+ * Create new transaction.
+ */
+pj_status_t pjsip_tsx_create( pj_pool_t *pool,
+ pjsip_endpoint *endpt,
+ pjsip_transaction **p_tsx)
+{
+ pjsip_transaction *tsx;
+ pj_status_t status;
+
+ tsx = pj_pool_calloc(pool, 1, sizeof(pjsip_transaction));
+
+ tsx->pool = pool;
+ tsx->endpt = endpt;
+ tsx->retransmit_timer.id = TSX_TIMER_RETRANSMISSION;
+ tsx->retransmit_timer._timer_id = -1;
+ tsx->retransmit_timer.user_data = tsx;
+ tsx->retransmit_timer.cb = &tsx_timer_callback;
+ tsx->timeout_timer.id = TSX_TIMER_TIMEOUT;
+ tsx->timeout_timer._timer_id = -1;
+ tsx->timeout_timer.user_data = tsx;
+ tsx->timeout_timer.cb = &tsx_timer_callback;
+ pj_sprintf(tsx->obj_name, "tsx%p", tsx);
+ status = pj_mutex_create_recursive(pool, "mtsx%p", &tsx->mutex);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ *p_tsx = tsx;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Lock transaction and set the value of Thread Local Storage.
+ */
+static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck)
+{
+ struct tsx_lock_data *prev_data;
+
+ pj_mutex_lock(tsx->mutex);
+ prev_data = (struct tsx_lock_data *)
+ pj_thread_local_get(pjsip_tsx_lock_tls_id);
+ lck->prev = prev_data;
+ lck->tsx = tsx;
+ lck->is_alive = 1;
+ pj_thread_local_set(pjsip_tsx_lock_tls_id, lck);
+}
+
+
+/*
+ * Unlock transaction.
+ * This will selectively unlock the mutex ONLY IF the transaction has not been
+ * destroyed. The function knows whether the transaction has been destroyed
+ * because when transaction is destroyed the is_alive flag for the transaction
+ * will be set to zero.
+ */
+static pj_status_t unlock_tsx( pjsip_transaction *tsx,
+ struct tsx_lock_data *lck)
+{
+ pj_assert( (void*)pj_thread_local_get(pjsip_tsx_lock_tls_id) == lck);
+ pj_assert( lck->tsx == tsx );
+ pj_thread_local_set(pjsip_tsx_lock_tls_id, lck->prev);
+ if (lck->is_alive)
+ pj_mutex_unlock(tsx->mutex);
+
+ return lck->is_alive ? PJ_SUCCESS : PJSIP_ETSXDESTROYED;
+}
+
+/*
+ * Set transaction state, and inform TU about the transaction state change.
+ */
+static void tsx_set_state( pjsip_transaction *tsx,
+ pjsip_tsx_state_e state,
+ pjsip_event_id_e event_src_type,
+ void *event_src )
+{
+ pjsip_event e;
+
+ PJ_LOG(4, (tsx->obj_name, "STATE %s-->%s, cause = %s",
+ state_str[tsx->state], state_str[state],
+ pjsip_event_str(event_src_type)));
+
+ /* Change state. */
+ tsx->state = state;
+
+ /* Update the state handlers. */
+ if (tsx->role == PJSIP_ROLE_UAC) {
+ tsx->state_handler = tsx_state_handler_uac[state];
+ } else {
+ tsx->state_handler = tsx_state_handler_uas[state];
+ }
+
+ /* Inform TU */
+ PJSIP_EVENT_INIT_TSX_STATE(e, tsx, event_src_type, event_src);
+ pjsip_endpt_send_tsx_event( tsx->endpt, &e );
+
+ /* When the transaction is terminated, release transport, and free the
+ * saved last transmitted message.
+ */
+ if (state == PJSIP_TSX_STATE_TERMINATED) {
+
+ /* Decrement transport reference counter. */
+ if (tsx->transport &&
+ tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL)
+ {
+ pjsip_transport_dec_ref( tsx->transport );
+ tsx->transport = NULL;
+ }
+ /* Free last transmitted message. */
+ if (tsx->last_tx) {
+ pjsip_tx_data_dec_ref( tsx->last_tx );
+ tsx->last_tx = NULL;
+ }
+ /* Cancel timeout timer. */
+ if (tsx->timeout_timer._timer_id != -1) {
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
+ tsx->timeout_timer._timer_id = -1;
+ }
+ /* Cancel retransmission timer. */
+ if (tsx->retransmit_timer._timer_id != -1) {
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+ tsx->retransmit_timer._timer_id = -1;
+ }
+
+ /* If transport is not pending, reschedule timeout timer to
+ * destroy this transaction.
+ */
+ if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
+ pj_time_val timeout = {0, 0};
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+ }
+
+ } else if (state == PJSIP_TSX_STATE_DESTROYED) {
+
+ /* Clear TLS, so that mutex will not be unlocked */
+ struct tsx_lock_data *lck = pj_thread_local_get(pjsip_tsx_lock_tls_id);
+ while (lck) {
+ if (lck->tsx == tsx) {
+ lck->is_alive = 0;
+ }
+ lck = lck->prev;
+ }
+ }
+}
+
+/*
+ * Look-up destination address and select which transport to be used to send
+ * the request message. The procedure used here follows the guidelines on
+ * sending the request in RFC3261 chapter 8.1.2.
+ *
+ * This function also modifies the message (request line and Route headers)
+ * accordingly.
+ */
+static pj_status_t tsx_process_route( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata,
+ pjsip_host_port *send_addr )
+{
+ const pjsip_uri *new_request_uri, *target_uri;
+ const pjsip_name_addr *topmost_route_uri;
+ pjsip_route_hdr *first_route_hdr, *last_route_hdr;
+
+ pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);
+
+ /* Get the first "Route" header from the message. If the message doesn't
+ * have any "Route" headers but the endpoint has, then copy the "Route"
+ * headers from the endpoint first.
+ */
+ last_route_hdr = first_route_hdr =
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE, NULL);
+ if (first_route_hdr) {
+ topmost_route_uri = &first_route_hdr->name_addr;
+ while (last_route_hdr->next != (void*)&tdata->msg->hdr) {
+ pjsip_route_hdr *hdr;
+ hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ROUTE,
+ last_route_hdr->next);
+ if (!hdr)
+ break;
+ last_route_hdr = hdr;
+ }
+ } else {
+ const pjsip_route_hdr *hdr_list;
+ hdr_list = (pjsip_route_hdr*)pjsip_endpt_get_routing(tsx->endpt);
+ if (hdr_list->next != hdr_list) {
+ const pjsip_route_hdr *hdr = (pjsip_route_hdr*)hdr_list->next;
+ first_route_hdr = NULL;
+ topmost_route_uri = &hdr->name_addr;
+ do {
+ last_route_hdr = pjsip_hdr_shallow_clone(tdata->pool, hdr);
+ if (first_route_hdr == NULL)
+ first_route_hdr = last_route_hdr;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)last_route_hdr);
+ hdr = hdr->next;
+ } while (hdr != hdr_list);
+ } else {
+ topmost_route_uri = NULL;
+ }
+ }
+
+ /* If Route headers exist, and the first element indicates loose-route,
+ * the URI is taken from the Request-URI, and we keep all existing Route
+ * headers intact.
+ * If Route headers exist, and the first element DOESN'T indicate loose
+ * route, the URI is taken from the first Route header, and remove the
+ * first Route header from the message.
+ * Otherwise if there's no Route headers, the URI is taken from the
+ * Request-URI.
+ */
+ if (topmost_route_uri) {
+ pj_bool_t has_lr_param;
+
+ if (PJSIP_URI_SCHEME_IS_SIP(topmost_route_uri) ||
+ PJSIP_URI_SCHEME_IS_SIPS(topmost_route_uri))
+ {
+ const pjsip_url *url = pjsip_uri_get_uri((void*)topmost_route_uri);
+ has_lr_param = url->lr_param;
+ } else {
+ has_lr_param = 0;
+ }
+
+ if (has_lr_param) {
+ new_request_uri = tdata->msg->line.req.uri;
+ /* We shouldn't need to delete topmost Route if it has lr param.
+ * But seems like it breaks some proxy implementation, so we
+ * delete it anyway.
+ */
+ /*
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ */
+ } else {
+ new_request_uri = pjsip_uri_get_uri((void*)topmost_route_uri);
+ pj_list_erase(first_route_hdr);
+ if (first_route_hdr == last_route_hdr)
+ last_route_hdr = NULL;
+ }
+
+ target_uri = (pjsip_uri*)topmost_route_uri;
+
+ } else {
+ target_uri = new_request_uri = tdata->msg->line.req.uri;
+ }
+
+ /* The target URI must be a SIP/SIPS URL so we can resolve it's address.
+ * Otherwise we're in trouble (i.e. there's no host part in tel: URL).
+ */
+ pj_memset(send_addr, 0, sizeof(*send_addr));
+
+ if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ send_addr->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE);
+ pj_strdup(tdata->pool, &send_addr->host, &url->host);
+ send_addr->port = url->port;
+ send_addr->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+
+ } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) {
+ pjsip_uri *uri = (pjsip_uri*) target_uri;
+ const pjsip_url *url = (const pjsip_url*)pjsip_uri_get_uri(uri);
+ pj_strdup(tdata->pool, &send_addr->host, &url->host);
+ send_addr->port = url->port;
+ send_addr->type =
+ pjsip_transport_get_type_from_name(&url->transport_param);
+#if PJ_HAS_TCP
+ if (send_addr->type == PJSIP_TRANSPORT_TCP ||
+ send_addr->type == PJSIP_TRANSPORT_SCTP)
+ {
+ send_addr->flag |= PJSIP_TRANSPORT_RELIABLE;
+ }
+#endif
+ } else {
+ pj_assert(!"Unsupported URI scheme!");
+ return PJSIP_EINVALIDSCHEME;
+ }
+
+ /* If target URI is different than request URI, replace
+ * request URI add put the original URI in the last Route header.
+ */
+ if (new_request_uri && new_request_uri!=tdata->msg->line.req.uri) {
+ pjsip_route_hdr *route = pjsip_route_hdr_create(tdata->pool);
+ route->name_addr.uri = tdata->msg->line.req.uri;
+ if (last_route_hdr)
+ pj_list_insert_after(last_route_hdr, route);
+ else
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)route);
+ tdata->msg->line.req.uri = (pjsip_uri*)new_request_uri;
+ }
+
+ /* Success. */
+ return PJ_SUCCESS;
+}
+
+
+/*
+ * Callback from the transport job.
+ * This callback is called when asychronous transport connect() operation
+ * has completed, with or without error.
+ */
+static void tsx_transport_callback(pjsip_transport_t *tr,
+ void *token,
+ pj_status_t status)
+{
+ char addr[PJ_MAX_HOSTNAME];
+ pjsip_transaction *tsx = token;
+ struct tsx_lock_data lck;
+
+ pj_memcpy(addr, tsx->dest_name.host.ptr, tsx->dest_name.host.slen);
+ addr[tsx->dest_name.host.slen] = '\0';
+
+
+ if (status == PJ_SUCCESS) {
+ PJ_LOG(4, (tsx->obj_name, "%s connected to %s:%d",
+ pjsip_transport_get_type_name(tr),
+ addr, tsx->dest_name.port));
+ } else {
+ PJ_LOG(4, (tsx->obj_name, "%s unable to connect to %s:%d, status=%d",
+ pjsip_transport_get_type_name(tr),
+ addr, tsx->dest_name.port, status));
+ }
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ if (status != PJ_SUCCESS) {
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+
+ /* Unlock transaction. */
+ unlock_tsx(tsx, &lck);
+ return;
+ }
+
+ /* See if transaction has already been terminated.
+ * If so, schedule to destroy the transaction.
+ */
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+ pj_time_val timeout = {0, 0};
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+
+ /* Unlock transaction. */
+ unlock_tsx(tsx, &lck);
+ return;
+ }
+
+ /* Add reference counter to the transport. */
+ pjsip_transport_add_ref(tr);
+
+ /* Mark transport as ready. */
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->transport = tr;
+
+ /* If there's a pending message to send, send it now. */
+ if (tsx->has_unsent_msg) {
+ tsx_send_msg( tsx, tsx->last_tx );
+ }
+
+ /* Unlock transaction. */
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Callback from the resolver job.
+ */
+static void tsx_resolver_callback(pj_status_t status,
+ void *token,
+ const struct pjsip_server_addresses *addr)
+{
+ pjsip_transaction *tsx = token;
+ struct tsx_lock_data lck;
+
+ PJ_LOG(4, (tsx->obj_name, "resolver job complete, status=%d", status));
+
+ if (status != PJ_SUCCESS || addr->count == 0) {
+ lock_tsx(tsx, &lck);
+ tsx->status_code = PJSIP_SC_TSX_RESOLVE_ERROR;
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ unlock_tsx(tsx, &lck);
+ return;
+ }
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ /* Copy server addresses. */
+ pj_memcpy(&tsx->remote_addr, addr, sizeof(*addr));
+
+ /* Create/find the transport for the remote address. */
+ PJ_LOG(5,(tsx->obj_name, "tsx getting transport for %s:%d",
+ pj_inet_ntoa(addr->entry[0].addr.sin_addr),
+ pj_ntohs(addr->entry[0].addr.sin_port)));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_CONNECTING;
+ pjsip_endpt_get_transport(tsx->endpt, tsx->pool,
+ addr->entry[0].type, &addr->entry[0].addr,
+ tsx,
+ &tsx_transport_callback);
+
+ /* Unlock transaction */
+ unlock_tsx(tsx, &lck);
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the callback for the
+ * transport is called.
+ */
+}
+
+/*
+ * Initialize the transaction as UAC transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_init_uac( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ pjsip_msg *msg;
+ pjsip_cseq_hdr *cseq;
+ pjsip_via_hdr *via;
+ pj_status_t status;
+ struct tsx_lock_data lck;
+
+ PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAC (tdata=%p)", tdata));
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ /* Keep shortcut */
+ msg = tdata->msg;
+
+ /* Role is UAC. */
+ tsx->role = PJSIP_ROLE_UAC;
+
+ /* Save method. */
+ pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+ /* Generate Via header if it doesn't exist. */
+ via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
+ if (via == NULL) {
+ via = pjsip_via_hdr_create(tdata->pool);
+ pjsip_msg_insert_first_hdr(msg, (pjsip_hdr*) via);
+ }
+
+ if (via->branch_param.slen == 0) {
+ pj_str_t tmp;
+ via->branch_param.ptr = pj_pool_alloc(tsx->pool, PJSIP_MAX_BRANCH_LEN);
+ via->branch_param.slen = PJSIP_MAX_BRANCH_LEN;
+ pj_memcpy(via->branch_param.ptr, PJSIP_RFC3261_BRANCH_ID,
+ PJSIP_RFC3261_BRANCH_LEN);
+
+ tmp.ptr = via->branch_param.ptr + PJSIP_RFC3261_BRANCH_LEN;
+ pj_generate_unique_string( &tmp );
+
+ /* Save branch parameter. */
+ tsx->branch = via->branch_param;
+ } else {
+ /* Copy branch parameter. */
+ pj_strdup(tsx->pool, &tsx->branch, &via->branch_param);
+ }
+
+
+ /* Generate transaction key. */
+ status = create_tsx_key_3261( tsx->pool, &tsx->transaction_key,
+ PJSIP_ROLE_UAC, &tsx->method,
+ &via->branch_param);
+ if (status != PJ_SUCCESS) {
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+ tsx->transaction_key.ptr));
+
+ /* Save CSeq. */
+ cseq = pjsip_msg_find_hdr(msg, PJSIP_H_CSEQ, NULL);
+ if (!cseq) {
+ pj_assert(!"CSeq header not present in outgoing message!");
+ unlock_tsx(tsx, &lck);
+ return PJSIP_EMISSINGHDR;
+ }
+ tsx->cseq = cseq->cseq;
+
+
+ /* Begin with State_Null.
+ * Manually set-up the state becase we don't want to call the callback.
+ */
+ tsx->state = PJSIP_TSX_STATE_NULL;
+ tsx->state_handler = &pjsip_tsx_on_state_null;
+
+ /* Get destination name from the message. */
+ status = tsx_process_route(tsx, tdata, &tsx->dest_name);
+ if (status != PJ_SUCCESS) {
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ /* Resolve destination.
+ * This will start asynchronous resolver job, and when it finishes,
+ * the callback will be called.
+ */
+ PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+ tsx->dest_name.host.slen,
+ tsx->dest_name.host.ptr,
+ tsx->dest_name.port));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
+ tsx, &tsx_resolver_callback);
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the resolver callback is
+ * called.
+ */
+
+ /* Unlock transaction and return.
+ * If transaction has been destroyed WITHIN the current thread, the
+ * unlock_tsx() function will return -1.
+ */
+ return unlock_tsx(tsx, &lck);
+}
+
+
+/*
+ * Initialize the transaction as UAS transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_tsx_init_uas( pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ pjsip_msg *msg = rdata->msg;
+ pj_str_t *branch;
+ pjsip_cseq_hdr *cseq;
+ pj_status_t status;
+ struct tsx_lock_data lck;
+
+ PJ_LOG(4,(tsx->obj_name, "initializing tsx as UAS (rdata=%p)", rdata));
+
+ /* Lock transaction. */
+ lock_tsx(tsx, &lck);
+
+ /* Keep shortcut to message */
+ msg = rdata->msg;
+
+ /* Role is UAS */
+ tsx->role = PJSIP_ROLE_UAS;
+
+ /* Save method. */
+ pjsip_method_copy( tsx->pool, &tsx->method, &msg->line.req.method);
+
+ /* Get transaction key either from branch for RFC3261 message, or
+ * create transaction key.
+ */
+ status = pjsip_tsx_create_key(tsx->pool, &tsx->transaction_key,
+ PJSIP_ROLE_UAS, &tsx->method, rdata);
+ if (status != PJ_SUCCESS) {
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ /* Duplicate branch parameter for transaction. */
+ branch = &rdata->via->branch_param;
+ pj_strdup(tsx->pool, &tsx->branch, branch);
+
+ PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen,
+ tsx->transaction_key.ptr));
+
+ /* Save CSeq */
+ cseq = rdata->cseq;
+ tsx->cseq = cseq->cseq;
+
+ /* Begin with state NULL
+ * Manually set-up the state becase we don't want to call the callback.
+ */
+ tsx->state = PJSIP_TSX_STATE_NULL;
+ tsx->state_handler = &pjsip_tsx_on_state_null;
+
+ /* Get the transport to send the response.
+ * According to section 18.2.2 of RFC3261, if the transport is reliable
+ * then the response must be sent using that transport.
+ */
+ /* In addition, RFC 3581 says, if Via has "rport" parameter specified,
+ * then return the response using the same transport.
+ */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(rdata->transport) ||
+ rdata->via->rport_param >= 0)
+ {
+ tsx->transport = rdata->transport;
+ pjsip_transport_add_ref(tsx->transport);
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+
+ tsx->current_addr = 0;
+ tsx->remote_addr.count = 1;
+ tsx->remote_addr.entry[0].type =
+ pjsip_transport_get_type(tsx->transport);
+ pj_memcpy(&tsx->remote_addr.entry[0].addr,
+ &rdata->addr, rdata->addr_len);
+
+ } else {
+ pj_status_t status;
+
+ status = pjsip_get_response_addr(tsx->pool, rdata->transport,
+ rdata->via, &tsx->dest_name);
+ if (status != PJ_SUCCESS) {
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_FINAL;
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+ tsx_set_state(tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ unlock_tsx(tsx, &lck);
+ return status;
+ }
+
+ /* Resolve destination.
+ * This will start asynchronous resolver job, and when it finishes,
+ * the callback will be called.
+ */
+ PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+ tsx->dest_name.host.slen,
+ tsx->dest_name.host.ptr,
+ tsx->dest_name.port));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
+ tsx, &tsx_resolver_callback);
+ }
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the resolver callback is
+ * called.
+ */
+
+ /* Unlock transaction and return.
+ * If transaction has been destroyed WITHIN the current thread, the
+ * unlock_tsx() function will return -1.
+ */
+ return unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Callback when timer expires.
+ */
+static void tsx_timer_callback( pj_timer_heap_t *theap, pj_timer_entry *entry)
+{
+ pjsip_event event;
+ pjsip_transaction *tsx = entry->user_data;
+ struct tsx_lock_data lck;
+
+ PJ_UNUSED_ARG(theap);
+
+ PJ_LOG(5,(tsx->obj_name, "got timer event (%s timer)",
+ (entry->id==TSX_TIMER_RETRANSMISSION ? "Retransmit" : "Timeout")));
+
+
+ if (entry->id == TSX_TIMER_RETRANSMISSION) {
+ PJSIP_EVENT_INIT_TIMER(event, &tsx->retransmit_timer);
+ } else {
+ PJSIP_EVENT_INIT_TIMER(event, &tsx->timeout_timer);
+ }
+
+ /* Dispatch event to transaction. */
+ lock_tsx(tsx, &lck);
+ (*tsx->state_handler)(tsx, &event);
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Transmit ACK message for 2xx/INVITE with this transaction. The ACK for
+ * non-2xx/INVITE is automatically sent by the transaction.
+ * This operation is only valid if the transaction is configured to handle ACK
+ * (tsx->handle_ack is non-zero). If this attribute is not set, then the
+ * transaction will comply with RFC-3261, i.e. it will set itself to
+ * TERMINATED state when it receives 2xx/INVITE.
+ */
+PJ_DEF(void) pjsip_tsx_on_tx_ack( pjsip_transaction *tsx, pjsip_tx_data *tdata)
+{
+ pjsip_msg *msg;
+ pjsip_host_port dest_addr;
+ pjsip_via_hdr *via;
+ struct tsx_lock_data lck;
+ pj_status_t status = PJ_SUCCESS;
+
+ /* Lock tsx. */
+ lock_tsx(tsx, &lck);
+
+ pj_assert(tsx->handle_ack != 0);
+
+ msg = tdata->msg;
+
+ /* Generate branch parameter if it doesn't exist. */
+ via = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, NULL);
+ if (via == NULL) {
+ via = pjsip_via_hdr_create(tdata->pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*) via);
+ }
+
+ if (via->branch_param.slen == 0) {
+ via->branch_param = tsx->branch;
+ } else {
+ pj_assert( pj_strcmp(&via->branch_param, &tsx->branch) == 0 );
+ }
+
+ /* Get destination name from the message. */
+ status = tsx_process_route(tsx, tdata, &dest_addr);
+ if (status != 0){
+ goto on_error;
+ }
+
+ /* Compare message's destination name with transaction's destination name.
+ * If NOT equal, then we'll have to resolve the destination.
+ */
+ if (dest_addr.type == tsx->dest_name.type &&
+ dest_addr.flag == tsx->dest_name.flag &&
+ dest_addr.port == tsx->dest_name.port &&
+ pj_stricmp(&dest_addr.host, &tsx->dest_name.host) == 0)
+ {
+ /* Equal destination. We can use current transport. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+ unlock_tsx(tsx, &lck);
+ return;
+
+ }
+
+ /* New destination; we'll have to resolve host and create new transport. */
+ pj_memcpy(&tsx->dest_name, &dest_addr, sizeof(dest_addr));
+ pj_strdup(tsx->pool, &tsx->dest_name.host, &dest_addr.host);
+
+ PJ_LOG(5,(tsx->obj_name, "tsx resolving destination %.*s:%d",
+ tsx->dest_name.host.slen,
+ tsx->dest_name.host.ptr,
+ tsx->dest_name.port));
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_transport_dec_ref(tsx->transport);
+ tsx->transport = NULL;
+
+ /* Put the message in queue. */
+ pjsip_tsx_on_tx_msg(tsx, tdata);
+
+ /* This is a bug!
+ * We shouldn't change transaction's state before actually sending the
+ * message. Otherwise transaction will terminate before message is sent,
+ * and timeout timer will be scheduled.
+ */
+ PJ_TODO(TSX_DONT_CHANGE_STATE_BEFORE_SENDING_ACK)
+
+ /*
+ * This will start asynchronous resolver job, and when it finishes,
+ * the callback will be called.
+ */
+
+ tsx->transport_state = PJSIP_TSX_TRANSPORT_STATE_RESOLVING;
+ pjsip_endpt_resolve( tsx->endpt, tsx->pool, &tsx->dest_name,
+ tsx, &tsx_resolver_callback);
+
+ unlock_tsx(tsx, &lck);
+
+ /* There should be nothing to do after this point.
+ * Execution for the transaction will resume when the resolver callback is
+ * called.
+ */
+ return;
+
+on_error:
+ /* Failure condition.
+ * Send TERMINATED event.
+ */
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+
+ unlock_tsx(tsx, &lck);
+}
+
+
+/*
+ * This function is called by TU to send a message.
+ */
+PJ_DEF(void) pjsip_tsx_on_tx_msg( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata )
+{
+ pjsip_event event;
+ struct tsx_lock_data lck;
+ pj_status_t status;
+
+ PJ_LOG(5,(tsx->obj_name, "Request to transmit msg on state %s (tdata=%p)",
+ state_str[tsx->state], tdata));
+
+ PJSIP_EVENT_INIT_TX_MSG(event, tsx, tdata);
+
+ /* Dispatch to transaction. */
+ lock_tsx(tsx, &lck);
+ status = (*tsx->state_handler)(tsx, &event);
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * This function is called by endpoint when incoming message for the
+ * transaction is received.
+ */
+PJ_DEF(void) pjsip_tsx_on_rx_msg( pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ pjsip_event event;
+ struct tsx_lock_data lck;
+ pj_status_t status;
+
+ PJ_LOG(5,(tsx->obj_name, "Incoming msg on state %s (rdata=%p)",
+ state_str[tsx->state], rdata));
+
+ PJSIP_EVENT_INIT_RX_MSG(event, tsx, rdata);
+
+ /* Dispatch to transaction. */
+ lock_tsx(tsx, &lck);
+ status = (*tsx->state_handler)(tsx, &event);
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Forcely terminate transaction.
+ */
+PJ_DEF(void) pjsip_tsx_terminate( pjsip_transaction *tsx, int code )
+{
+ struct tsx_lock_data lck;
+
+ lock_tsx(tsx, &lck);
+ tsx->status_code = code;
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_USER, NULL);
+
+ unlock_tsx(tsx, &lck);
+}
+
+/*
+ * Send message to the transport.
+ * If transport is not yet available, then do nothing. The message will be
+ * transmitted when transport connection completion callback is called.
+ */
+static pj_status_t tsx_send_msg( pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ pj_status_t status = PJ_SUCCESS;
+
+ PJ_LOG(5,(tsx->obj_name, "sending msg (tdata=%p)", tdata));
+
+ if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL) {
+ pj_ssize_t sent;
+ pjsip_event before_tx_event;
+
+ pj_assert(tsx->transport != NULL);
+
+ /* Make sure Via transport info is filled up properly for
+ * requests.
+ */
+ if (tdata->msg->type == PJSIP_REQUEST_MSG) {
+ const pj_sockaddr_in *addr_name;
+ pjsip_via_hdr *via = (pjsip_via_hdr*)
+ pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
+
+ /* For request message, set "rport" parameter by default. */
+ if (tdata->msg->type == PJSIP_REQUEST_MSG)
+ via->rport_param = 0;
+
+ /* Don't update Via sent-by on retransmission. */
+ if (via->sent_by.host.slen == 0) {
+ addr_name = pjsip_transport_get_addr_name(tsx->transport);
+ pj_strdup2(tdata->pool, &via->transport,
+ pjsip_transport_get_type_name(tsx->transport));
+ pj_strdup2(tdata->pool, &via->sent_by.host,
+ pj_inet_ntoa(addr_name->sin_addr));
+ via->sent_by.port = pj_ntohs(addr_name->sin_port);
+ }
+ }
+
+ /* Notify everybody we're about to send message. */
+ PJSIP_EVENT_INIT_PRE_TX_MSG(before_tx_event, tsx, tdata,
+ tsx->retransmit_count);
+ pjsip_endpt_send_tsx_event( tsx->endpt, &before_tx_event );
+
+ tsx->has_unsent_msg = 0;
+ status = pjsip_transport_send_msg(
+ tsx->transport, tdata,
+ &tsx->remote_addr.entry[tsx->current_addr].addr,
+ &sent);
+ if (status != PJ_SUCCESS) {
+ goto on_error;
+ }
+ } else {
+ tsx->has_unsent_msg = 1;
+ }
+
+ return 0;
+
+on_error:
+ tsx->status_code = PJSIP_SC_TSX_TRANSPORT_ERROR;
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TRANSPORT_ERROR, (void*)status);
+ return status;
+}
+
+/*
+ * Retransmit last message sent.
+ */
+static pj_status_t pjsip_tsx_retransmit( pjsip_transaction *tsx,
+ int should_restart_timer)
+{
+ pj_status_t status;
+
+ PJ_LOG(4,(tsx->obj_name, "retransmiting (tdata=%p, count=%d, restart?=%d)",
+ tsx->last_tx, tsx->retransmit_count, should_restart_timer));
+
+ pj_assert(tsx->last_tx != NULL);
+
+ ++tsx->retransmit_count;
+
+ status = tsx_send_msg( tsx, tsx->last_tx);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ /* Restart timer T1. */
+ if (should_restart_timer) {
+ pj_time_val timeout;
+ int msec_time = (1 << (tsx->retransmit_count)) * PJSIP_T1_TIMEOUT;
+
+ if (tsx->method.id!=PJSIP_INVITE_METHOD && msec_time>PJSIP_T2_TIMEOUT)
+ msec_time = PJSIP_T2_TIMEOUT;
+
+ timeout.sec = msec_time / 1000;
+ timeout.msec = msec_time % 1000;
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer,
+ &timeout);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in state Null.
+ */
+static pj_status_t pjsip_tsx_on_state_null( pjsip_transaction *tsx,
+ pjsip_event *event )
+{
+ pj_status_t status;
+
+ pj_assert( tsx->state == PJSIP_TSX_STATE_NULL);
+ pj_assert( tsx->last_tx == NULL );
+ pj_assert( tsx->has_unsent_msg == 0);
+
+ if (tsx->role == PJSIP_ROLE_UAS) {
+
+ /* Set state to Trying. */
+ pj_assert(event->type == PJSIP_EVENT_RX_MSG);
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TRYING,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+ } else {
+ pjsip_tx_data *tdata = event->body.tx_msg.tdata;
+
+ /* Save the message for retransmission. */
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref(tdata);
+
+ /* Send the message. */
+ status = tsx_send_msg( tsx, tdata);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ /* Start Timer B (or called timer F for non-INVITE) for transaction
+ * timeout.
+ */
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout_timer_val);
+
+ /* Start Timer A (or timer E) for retransmission only if unreliable
+ * transport is being used.
+ */
+ if (tsx->transport_state == PJSIP_TSX_TRANSPORT_STATE_FINAL &&
+ PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
+ {
+ pjsip_endpt_schedule_timer(tsx->endpt, &tsx->retransmit_timer,
+ &t1_timer_val);
+ tsx->retransmit_count = 0;
+ }
+
+ /* Move state. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_CALLING,
+ PJSIP_EVENT_TX_MSG, tdata);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * State Calling is for UAC after it sends request but before any responses
+ * is received.
+ */
+static pj_status_t pjsip_tsx_on_state_calling( pjsip_transaction *tsx,
+ pjsip_event *event )
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_CALLING);
+ pj_assert(tsx->role == PJSIP_ROLE_UAC);
+
+ if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->retransmit_timer)
+ {
+ pj_status_t status;
+
+ /* Retransmit the request. */
+ status = pjsip_tsx_retransmit( tsx, 1 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->timeout_timer)
+ {
+
+ /* Cancel retransmission timer. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+ }
+
+ /* Set status code */
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+ /* Inform TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+
+ /* Transaction is destroyed */
+ return PJSIP_ETSXDESTROYED;
+
+ } else if (event->type == PJSIP_EVENT_RX_MSG) {
+ int code;
+
+ /* Cancel retransmission timer A. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->retransmit_timer);
+
+ /* Cancel timer B (transaction timeout) */
+ pjsip_endpt_cancel_timer(tsx->endpt, &tsx->timeout_timer);
+
+ /* Discard retransmission message if it is not INVITE.
+ * The INVITE tdata is needed in case we have to generate ACK for
+ * the final response.
+ */
+ /* Keep last_tx for authorization. */
+ code = event->body.rx_msg.rdata->msg->line.status.code;
+ if (tsx->method.id != PJSIP_INVITE_METHOD && code!=401 && code!=407) {
+ pjsip_tx_data_dec_ref(tsx->last_tx);
+ tsx->last_tx = NULL;
+ }
+
+ /* Processing is similar to state Proceeding. */
+ pjsip_tsx_on_state_proceeding_uac( tsx, event);
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * State Trying is for UAS after it received request but before any responses
+ * is sent.
+ * Note: this is different than RFC3261, which can use Trying state for
+ * non-INVITE client transaction (bug in RFC?).
+ */
+static pj_status_t pjsip_tsx_on_state_trying( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_status_t status;
+
+ pj_assert(tsx->state == PJSIP_TSX_STATE_TRYING);
+
+ /* This state is only for UAS */
+ pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+ /* Better be transmission of response message.
+ * If we've got request retransmission, this means that the TU hasn't
+ * transmitted any responses within 500 ms, which is not allowed. If
+ * this happens, just ignore the event (we couldn't retransmit last
+ * response because we haven't sent any!).
+ */
+ //pj_assert(event->type == PJSIP_EVENT_TX_MSG);
+ if (event->type != PJSIP_EVENT_TX_MSG) {
+ return PJ_SUCCESS;
+ }
+
+ /* The rest of the processing of the event is exactly the same as in
+ * "Proceeding" state.
+ */
+ status = pjsip_tsx_on_state_proceeding_uas( tsx, event);
+
+ /* Inform the TU of the state transision if state is still State_Trying */
+ if (status==PJ_SUCCESS && tsx->state == PJSIP_TSX_STATE_TRYING) {
+ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
+ PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata);
+ }
+
+ return status;
+}
+
+/*
+ * Handler for events in Proceeding for UAS
+ * This state happens after the TU sends provisional response.
+ */
+static pj_status_t pjsip_tsx_on_state_proceeding_uas( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
+ tsx->state == PJSIP_TSX_STATE_TRYING);
+
+ /* This state is only for UAS. */
+ pj_assert(tsx->role == PJSIP_ROLE_UAS);
+
+ /* Receive request retransmission. */
+ if (event->type == PJSIP_EVENT_RX_MSG) {
+
+ pj_status_t status;
+
+ /* Send last response. */
+ status = pjsip_tsx_retransmit( tsx, 0 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TX_MSG ) {
+ pjsip_tx_data *tdata = event->body.tx_msg.tdata;
+ pj_status_t status;
+
+ /* The TU sends response message to the request. Save this message so
+ * that we can retransmit the last response in case we receive request
+ * retransmission.
+ */
+ pjsip_msg *msg = tdata->msg;
+
+ /* This can only be a response message. */
+ pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+
+ /* Status code must be higher than last sent. */
+ pj_assert(msg->line.status.code >= tsx->status_code);
+
+ /* Update last status */
+ tsx->status_code = msg->line.status.code;
+
+ /* Discard the saved last response (it will be updated later as
+ * necessary).
+ */
+ if (tsx->last_tx && tsx->last_tx != tdata) {
+ pjsip_tx_data_dec_ref( tsx->last_tx );
+ tsx->last_tx = NULL;
+ }
+
+ /* Send the message. */
+ status = tsx_send_msg(tsx, tdata);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ // Update To tag header for RFC2543 transaction.
+ // TODO:
+
+ /* Update transaction state */
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+ if (tsx->last_tx != tdata) {
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref( tdata );
+ }
+ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
+ PJSIP_EVENT_TX_MSG, tdata );
+
+ } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 200)) {
+
+ if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack==0) {
+
+ /* 2xx class message is not saved, because retransmission
+ * is handled by TU.
+ */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TX_MSG, tdata );
+
+ /* Transaction is destroyed. */
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ pj_time_val timeout;
+
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ tsx->retransmit_count = 0;
+ pjsip_endpt_schedule_timer( tsx->endpt,
+ &tsx->retransmit_timer,
+ &t1_timer_val);
+ }
+
+ /* Save last response sent for retransmission when request
+ * retransmission is received.
+ */
+ if (tsx->last_tx != tdata) {
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref(tdata);
+ }
+
+ /* Start timer J at 64*T1 for unreliable transport or zero for
+ * reliable transport.
+ */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ timeout = timeout_timer_val;
+ } else {
+ timeout.sec = timeout.msec = 0;
+ }
+
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+
+ /* Set state to "Completed" */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_TX_MSG, tdata );
+ }
+
+ } else if (tsx->status_code >= 300) {
+
+ /* 3xx-6xx class message causes transaction to move to
+ * "Completed" state.
+ */
+ if (tsx->last_tx != tdata) {
+ tsx->last_tx = tdata;
+ pjsip_tx_data_add_ref( tdata );
+ }
+
+ /* Start timer H for transaction termination */
+ pjsip_endpt_schedule_timer(tsx->endpt,&tsx->timeout_timer,
+ &timeout_timer_val);
+
+ /* For INVITE, if unreliable transport is used, retransmission
+ * timer G will be scheduled (retransmission).
+ */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ,
+ NULL);
+ if (cseq->method.id == PJSIP_INVITE_METHOD) {
+ tsx->retransmit_count = 0;
+ pjsip_endpt_schedule_timer(tsx->endpt,
+ &tsx->retransmit_timer,
+ &t1_timer_val);
+ }
+ }
+
+ /* Inform TU */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_TX_MSG, tdata );
+
+ } else {
+ pj_assert(0);
+ }
+
+
+ } else if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->retransmit_timer) {
+ /* Retransmission timer elapsed. */
+ pj_status_t status;
+
+ /* Must have last response to retransmit. */
+ pj_assert(tsx->last_tx != NULL);
+
+ /* Retransmit the last response. */
+ status = pjsip_tsx_retransmit( tsx, 1 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else if (event->type == PJSIP_EVENT_TIMER &&
+ event->body.timer.entry == &tsx->timeout_timer) {
+
+ /* Timeout timer. should not happen? */
+ pj_assert(0);
+
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer);
+
+ return PJ_EBUG;
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in Proceeding for UAC
+ * This state happens after provisional response(s) has been received from
+ * UAS.
+ */
+static pj_status_t pjsip_tsx_on_state_proceeding_uac(pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+
+ pj_assert(tsx->state == PJSIP_TSX_STATE_PROCEEDING ||
+ tsx->state == PJSIP_TSX_STATE_CALLING);
+
+ if (event->type != PJSIP_EVENT_TIMER) {
+ /* Must be incoming response, because we should not retransmit
+ * request once response has been received.
+ */
+ pj_assert(event->type == PJSIP_EVENT_RX_MSG);
+ if (event->type != PJSIP_EVENT_RX_MSG) {
+ return PJ_EINVALIDOP;
+ }
+
+ tsx->status_code = event->body.rx_msg.rdata->msg->line.status.code;
+ } else {
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+ }
+
+ if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code, 100)) {
+
+ /* Inform the message to TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_PROCEEDING,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+ } else if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
+
+ /* Stop timeout timer B/F. */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+
+ /* For INVITE, the state moves to Terminated state (because ACK is
+ * handled in TU). For non-INVITE, state moves to Completed.
+ */
+ if (tsx->method.id == PJSIP_INVITE_METHOD && tsx->handle_ack == 0) {
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ pj_time_val timeout;
+
+ /* For unreliable transport, start timer D (for INVITE) or
+ * timer K for non-INVITE. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ timeout = td_timer_val;
+ } else {
+ timeout = t4_timer_val;
+ }
+ } else {
+ timeout.sec = timeout.msec = 0;
+ }
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &timeout);
+
+ /* Move state to Completed, inform TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+ }
+
+ } else if (tsx->status_code >= 300 && tsx->status_code <= 699) {
+ pj_time_val timeout;
+ pj_status_t status;
+
+ /* Stop timer B. */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+
+ /* Generate and send ACK for INVITE. */
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx,
+ event->body.rx_msg.rdata );
+ status = tsx_send_msg( tsx, tsx->last_tx);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ }
+
+ /* Start Timer D with TD/T4 timer if unreliable transport is used. */
+ if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ timeout = td_timer_val;
+ } else {
+ timeout = t4_timer_val;
+ }
+ } else {
+ timeout.sec = timeout.msec = 0;
+ }
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout);
+
+ /* Inform TU. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+
+ } else {
+ // Shouldn't happen because there's no timer for this state.
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in Completed state for UAS
+ */
+static pj_status_t pjsip_tsx_on_state_completed_uas( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+ if (event->type == PJSIP_EVENT_RX_MSG) {
+ pjsip_msg *msg = event->body.rx_msg.rdata->msg;
+ pjsip_cseq_hdr *cseq = pjsip_msg_find_hdr( msg, PJSIP_H_CSEQ, NULL );
+
+ /* On receive request retransmission, retransmit last response. */
+ if (cseq->method.id != PJSIP_ACK_METHOD) {
+ pj_status_t status;
+
+ status = pjsip_tsx_retransmit( tsx, 0 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else {
+ /* Process incoming ACK request. */
+
+ /* Cease retransmission. */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->retransmit_timer );
+
+ /* Start timer I in T4 interval (transaction termination). */
+ pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer );
+ pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer,
+ &t4_timer_val);
+
+ /* Move state to "Confirmed" */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_CONFIRMED,
+ PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata );
+ }
+
+ } else if (event->type == PJSIP_EVENT_TIMER) {
+
+ if (event->body.timer.entry == &tsx->retransmit_timer) {
+ /* Retransmit message. */
+ pj_status_t status;
+
+ status = pjsip_tsx_retransmit( tsx, 1 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ } else {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+
+ /* For INVITE, this means that ACK was never received.
+ * Set state to Terminated, and inform TU.
+ */
+
+ tsx->status_code = PJSIP_SC_TSX_TIMEOUT;
+
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ /* Transaction terminated, it can now be deleted. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+ return PJSIP_ETSXDESTROYED;
+ }
+ }
+
+ } else {
+ /* Ignore request to transmit. */
+ pj_assert(event->body.tx_msg.tdata == tsx->last_tx);
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in Completed state for UAC transaction.
+ */
+static pj_status_t pjsip_tsx_on_state_completed_uac( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_COMPLETED);
+
+ if (event->type == PJSIP_EVENT_TIMER) {
+ /* Must be the timeout timer. */
+ pj_assert(event->body.timer.entry == &tsx->timeout_timer);
+
+ /* Move to Terminated state. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, event->body.timer.entry );
+
+ /* Transaction has been destroyed. */
+ return PJSIP_ETSXDESTROYED;
+
+ } else if (event->type == PJSIP_EVENT_RX_MSG) {
+ if (tsx->method.id == PJSIP_INVITE_METHOD) {
+ /* On received of final response retransmission, retransmit the ACK.
+ * TU doesn't need to be informed.
+ */
+ pjsip_msg *msg = event->body.rx_msg.rdata->msg;
+ pj_assert(msg->type == PJSIP_RESPONSE_MSG);
+ if (msg->type==PJSIP_RESPONSE_MSG &&
+ msg->line.status.code >= 200)
+ {
+ pj_status_t status;
+
+ status = pjsip_tsx_retransmit( tsx, 0 );
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ } else {
+ /* Very late retransmission of privisional response. */
+ pj_assert( msg->type == PJSIP_RESPONSE_MSG );
+ }
+ } else {
+ /* Just drop the response. */
+ }
+ } else if (tsx->method.id == PJSIP_INVITE_METHOD &&
+ event->type == PJSIP_EVENT_TX_MSG &&
+ event->body.tx_msg.tdata->msg->line.req.method.id==PJSIP_ACK_METHOD) {
+
+ pj_status_t status;
+
+ /* Set last transmitted message. */
+ if (tsx->last_tx != event->body.tx_msg.tdata) {
+ pjsip_tx_data_dec_ref( tsx->last_tx );
+ tsx->last_tx = event->body.tx_msg.tdata;
+ pjsip_tx_data_add_ref( tsx->last_tx );
+ }
+
+ /* No state changed, but notify app.
+ * Must notify now, so app has chance to put SDP in outgoing ACK msg.
+ */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED,
+ PJSIP_EVENT_TX_MSG, event->body.tx_msg.tdata );
+
+ /* Send msg */
+ status = tsx_send_msg(tsx, event->body.tx_msg.tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in state Confirmed.
+ */
+static pj_status_t pjsip_tsx_on_state_confirmed( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_CONFIRMED);
+
+ /* This state is only for UAS for INVITE. */
+ pj_assert(tsx->role == PJSIP_ROLE_UAS);
+ pj_assert(tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /* Absorb any ACK received. */
+ if (event->type == PJSIP_EVENT_RX_MSG) {
+
+ pjsip_method_e method_id =
+ event->body.rx_msg.rdata->msg->line.req.method.id;
+
+ /* Must be a request message. */
+ pj_assert(event->body.rx_msg.rdata->msg->type == PJSIP_REQUEST_MSG);
+
+ /* Must be an ACK request or a late INVITE retransmission. */
+ pj_assert(method_id == PJSIP_ACK_METHOD ||
+ method_id == PJSIP_INVITE_METHOD);
+
+ /* Just so that compiler won't complain about unused vars when
+ * building release code.
+ */
+ PJ_UNUSED_ARG(method_id);
+
+ } else if (event->type == PJSIP_EVENT_TIMER) {
+ /* Must be from timeout_timer_. */
+ pj_assert(event->body.timer.entry == &tsx->timeout_timer);
+
+ /* Move to Terminated state. */
+ tsx_set_state( tsx, PJSIP_TSX_STATE_TERMINATED,
+ PJSIP_EVENT_TIMER, &tsx->timeout_timer );
+
+ /* Transaction has been destroyed. */
+ return PJSIP_ETSXDESTROYED;
+
+ } else {
+ pj_assert(0);
+ return PJ_EBUG;
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Handler for events in state Terminated.
+ */
+static pj_status_t pjsip_tsx_on_state_terminated( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pj_assert(tsx->state == PJSIP_TSX_STATE_TERMINATED);
+
+ PJ_UNUSED_ARG(event);
+
+ /* Destroy this transaction */
+ tsx_set_state(tsx, PJSIP_TSX_STATE_DESTROYED,
+ event->type, event->body.user.user1 );
+
+ return PJ_SUCCESS;
+}
+
+
+static pj_status_t pjsip_tsx_on_state_destroyed(pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ PJ_UNUSED_ARG(tsx);
+ PJ_UNUSED_ARG(event);
+ return PJ_SUCCESS;
+}
+
diff --git a/pjsip/src/pjsip/sip_transport.c b/pjsip/src/pjsip/sip_transport.c index 09eb6cfb..477ddf20 100644 --- a/pjsip/src/pjsip/sip_transport.c +++ b/pjsip/src/pjsip/sip_transport.c @@ -1,1667 +1,1689 @@ -/* $Id$ - */ -#include <pjsip/sip_transport.h> -#include <pjsip/sip_endpoint.h> -#include <pjsip/sip_parser.h> -#include <pjsip/sip_msg.h> -#include <pjsip/sip_private.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_transport.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_private.h>
#include <pjsip/sip_errno.h>
-#include <pj/os.h> -#include <pj/log.h> -#include <pj/ioqueue.h> -#include <pj/hash.h> -#include <pj/string.h> -#include <pj/pool.h> +#include <pj/os.h>
+#include <pj/log.h>
+#include <pj/ioqueue.h>
+#include <pj/hash.h>
+#include <pj/string.h>
+#include <pj/pool.h>
#include <pj/assert.h>
- -#define MGR_IDLE_CHECK_INTERVAL 30 -#define MGR_HASH_TABLE_SIZE PJSIP_MAX_DIALOG_COUNT -#define BACKLOG 5 -#define DEFAULT_SO_SNDBUF (8 * 1024 * 1024) -#define DEFAULT_SO_RCVBUF (8 * 1024 * 1024) - -#define LOG_TRANSPORT_MGR "trmgr" -#define THIS_FILE "sip_transport" - -static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr ); - - -/** - * New TCP socket for accept. - */ -typedef struct incoming_socket_rec -{ - pj_sock_t sock; - pj_sockaddr_in remote; - pj_sockaddr_in local; - int addrlen; -} incoming_socket_rec; - -/** - * SIP Transport. - */ -struct pjsip_transport_t -{ - /** Standard list members, for chaining the transport in the - * listener list. - */ - PJ_DECL_LIST_MEMBER(struct pjsip_transport_t); - - /** Transport's pool. */ - pj_pool_t *pool; - - /** Mutex */ - pj_mutex_t *tr_mutex; - - /** Transport name for logging purpose */ - char obj_name[PJ_MAX_OBJ_NAME]; - - /** Socket handle */ - pj_sock_t sock; - - /** Transport type. */ - pjsip_transport_type_e type; - - /** Flags to keep various states (see pjsip_transport_flags_e). */ - pj_uint32_t flag; - - /** I/O Queue key */ - pj_ioqueue_key_t *key; +
+#define MGR_IDLE_CHECK_INTERVAL 30
+#define MGR_HASH_TABLE_SIZE PJSIP_MAX_DIALOG_COUNT
+#define BACKLOG 5
+#define DEFAULT_SO_SNDBUF (8 * 1024 * 1024)
+#define DEFAULT_SO_RCVBUF (8 * 1024 * 1024)
+
+#define LOG_TRANSPORT_MGR "trmgr"
+#define THIS_FILE "sip_transport"
+
+static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr );
+
+
+/**
+ * New TCP socket for accept.
+ */
+typedef struct incoming_socket_rec
+{
+ pj_sock_t sock;
+ pj_sockaddr_in remote;
+ pj_sockaddr_in local;
+ int addrlen;
+} incoming_socket_rec;
+
+/**
+ * SIP Transport.
+ */
+struct pjsip_transport_t
+{
+ /** Standard list members, for chaining the transport in the
+ * listener list.
+ */
+ PJ_DECL_LIST_MEMBER(struct pjsip_transport_t);
+
+ /** Transport's pool. */
+ pj_pool_t *pool;
+
+ /** Mutex */
+ pj_mutex_t *tr_mutex;
+
+ /** Transport name for logging purpose */
+ char obj_name[PJ_MAX_OBJ_NAME];
+
+ /** Socket handle */
+ pj_sock_t sock;
+
+ /** Transport type. */
+ pjsip_transport_type_e type;
+
+ /** Flags to keep various states (see pjsip_transport_flags_e). */
+ pj_uint32_t flag;
+
+ /** I/O Queue key */
+ pj_ioqueue_key_t *key;
/** Accept key. */
pj_ioqueue_op_key_t accept_op;
- /** Receive data buffer */ - pjsip_rx_data *rdata; -
- /** Pointer to transport manager */ - pjsip_transport_mgr *mgr; - - /** Reference counter, to prevent this transport from being closed while - * it's being used. - */ - pj_atomic_t *ref_cnt; - - /** Local address. */ - pj_sockaddr_in local_addr; - - /** Address name (what to put in Via address field). */ - pj_sockaddr_in addr_name; - - /** Remote address (can be zero for UDP and for listeners). UDP listener - * bound to local loopback interface (127.0.0.1) has remote address set - * to 127.0.0.1 to prevent client from using it to send to remote hosts, - * because remote host then will receive 127.0.0.1 as the packet's - * source address. - */ - pj_sockaddr_in remote_addr; - - /** Struct to save incoming socket information. */ - incoming_socket_rec accept_data; - - /** When this transport should be closed. */ - pj_time_val close_time; - - /** List of callbacks to be called when client attempt to use this - * transport while it's not connected (i.e. still connecting). - */ - pj_list cb_list; -}; - - -/* - * Transport manager. - */ -struct pjsip_transport_mgr -{ - pj_hash_table_t *transport_table; - pj_mutex_t *mutex; - pjsip_endpoint *endpt; - pj_ioqueue_t *ioqueue; + /** Receive data buffer */
+ pjsip_rx_data *rdata;
+
+ /** Pointer to transport manager */
+ pjsip_transport_mgr *mgr;
+
+ /** Reference counter, to prevent this transport from being closed while
+ * it's being used.
+ */
+ pj_atomic_t *ref_cnt;
+
+ /** Local address. */
+ pj_sockaddr_in local_addr;
+
+ /** Address name (what to put in Via address field). */
+ pj_sockaddr_in addr_name;
+
+ /** Remote address (can be zero for UDP and for listeners). UDP listener
+ * bound to local loopback interface (127.0.0.1) has remote address set
+ * to 127.0.0.1 to prevent client from using it to send to remote hosts,
+ * because remote host then will receive 127.0.0.1 as the packet's
+ * source address.
+ */
+ pj_sockaddr_in remote_addr;
+
+ /** Struct to save incoming socket information. */
+ incoming_socket_rec accept_data;
+
+ /** When this transport should be closed. */
+ pj_time_val close_time;
+
+ /** List of callbacks to be called when client attempt to use this
+ * transport while it's not connected (i.e. still connecting).
+ */
+ pj_list cb_list;
+};
+
+
+/*
+ * Transport manager.
+ */
+struct pjsip_transport_mgr
+{
+ pj_hash_table_t *transport_table;
+ pj_mutex_t *mutex;
+ pjsip_endpoint *endpt;
+ pj_ioqueue_t *ioqueue;
pj_time_val next_idle_check;
pj_size_t send_buf_size;
- pj_size_t recv_buf_size; - void (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata); -}; - -/* - * Transport role. - */ -typedef enum transport_role_e -{ - TRANSPORT_ROLE_LISTENER, - TRANSPORT_ROLE_TRANSPORT, -} transport_role_e; - -/* - * Transport key for indexing in the hash table. - * WATCH OUT FOR ALIGNMENT PROBLEM HERE! - */ -typedef struct transport_key -{ - pj_uint8_t type; - pj_uint8_t zero; - pj_uint16_t port; - pj_uint32_t addr; -} transport_key; - -/* - * Transport callback. - */ -struct transport_callback -{ - PJ_DECL_LIST_MEMBER(struct transport_callback); - - /** User defined token to be passed to the callback. */ - void *token; - - /** The callback function. */ - void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status); - -}; - -/* - * Transport names. - */ -const struct -{ - pjsip_transport_type_e type; - pj_uint16_t port; - pj_str_t name; -} transport_names[] = -{ - { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}}, - { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}}, -#if PJ_HAS_TCP - { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}}, - { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}}, - { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}} -#endif -}; - + pj_size_t recv_buf_size;
+ void (*message_callback)(pjsip_endpoint*, pjsip_rx_data *rdata);
+};
+
+/*
+ * Transport role.
+ */
+typedef enum transport_role_e
+{
+ TRANSPORT_ROLE_LISTENER,
+ TRANSPORT_ROLE_TRANSPORT,
+} transport_role_e;
+
+/*
+ * Transport key for indexing in the hash table.
+ * WATCH OUT FOR ALIGNMENT PROBLEM HERE!
+ */
+typedef struct transport_key
+{
+ pj_uint8_t type;
+ pj_uint8_t zero;
+ pj_uint16_t port;
+ pj_uint32_t addr;
+} transport_key;
+
+/*
+ * Transport callback.
+ */
+struct transport_callback
+{
+ PJ_DECL_LIST_MEMBER(struct transport_callback);
+
+ /** User defined token to be passed to the callback. */
+ void *token;
+
+ /** The callback function. */
+ void (*cb)(pjsip_transport_t *tr, void *token, pj_status_t status);
+
+};
+
+/*
+ * Transport names.
+ */
+const struct
+{
+ pjsip_transport_type_e type;
+ pj_uint16_t port;
+ pj_str_t name;
+} transport_names[] =
+{
+ { PJSIP_TRANSPORT_UNSPECIFIED, 0, {NULL, 0}},
+ { PJSIP_TRANSPORT_UDP, 5060, {"UDP", 3}},
+#if PJ_HAS_TCP
+ { PJSIP_TRANSPORT_TCP, 5060, {"TCP", 3}},
+ { PJSIP_TRANSPORT_TLS, 5061, {"TLS", 3}},
+ { PJSIP_TRANSPORT_SCTP, 5060, {"SCTP", 4}}
+#endif
+};
+
static void on_ioqueue_read(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read); + pj_ssize_t bytes_read);
static void on_ioqueue_write(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent); + pj_ssize_t bytes_sent);
static void on_ioqueue_accept(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_sock_t newsock,
- int status); + int status);
static void on_ioqueue_connect(pj_ioqueue_key_t *key,
- int status); - -static pj_ioqueue_callback ioqueue_transport_callback = -{ - &on_ioqueue_read, - &on_ioqueue_write, - &on_ioqueue_accept, - &on_ioqueue_connect -}; - -static void init_key_from_transport(transport_key *key, - const pjsip_transport_t *tr) -{ - /* This is to detect alignment problems. */ - pj_assert(sizeof(transport_key) == 8); - - key->type = (pj_uint8_t)tr->type; - key->zero = 0; - key->addr = pj_sockaddr_in_get_addr(&tr->remote_addr).s_addr; - key->port = pj_sockaddr_in_get_port(&tr->remote_addr); - /* - if (key->port == 0) { - key->port = pj_sockaddr_in_get_port(&tr->local_addr); - } - */ -} - -#if PJ_HAS_TCP -static void init_tcp_key(transport_key *key, pjsip_transport_type_e type, - const pj_sockaddr_in *addr) -{ - /* This is to detect alignment problems. */ - pj_assert(sizeof(transport_key) == 8); - - key->type = (pj_uint8_t)type; - key->zero = 0; - key->addr = pj_sockaddr_in_get_addr(addr).s_addr; - key->port = pj_sockaddr_in_get_port(addr); -} -#endif - -static void init_udp_key(transport_key *key, pjsip_transport_type_e type, - const pj_sockaddr_in *addr) -{ - PJ_UNUSED_ARG(addr); - - /* This is to detect alignment problems. */ - pj_assert(sizeof(transport_key) == 8); - - pj_memset(key, 0, sizeof(*key)); - key->type = (pj_uint8_t)type; - -#if 0 /* Not sure why we need to make 127.0.0.1 a special case */ - if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) { - /* This looks more complicated than it is because key->addr uses - * the host version of the address (i.e. converted with ntohl()). - */ - pj_str_t localaddr = pj_str("127.0.0.1"); - pj_sockaddr_in addr; - pj_sockaddr_set_str_addr(&addr, &localaddr); - key->addr = pj_sockaddr_in_get_addr(&addr); - } -#endif -} - -/* - * Get type format name (for pool name). - */ -static const char *transport_get_name_format( int type ) -{ - switch (type) { - case PJSIP_TRANSPORT_UDP: - return " udp%p"; -#if PJ_HAS_TCP - case PJSIP_TRANSPORT_TCP: - return " tcp%p"; - case PJSIP_TRANSPORT_TLS: - return " tls%p"; - case PJSIP_TRANSPORT_SCTP: - return "sctp%p"; -#endif - } - pj_assert(0); - return 0; -} - -/* - * Get the default SIP port number for the specified type. - */ -PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type) -{ - return transport_names[type].port; -} - -/* - * Get transport name. - */ -static const char *get_type_name(int type) -{ - return transport_names[type].name.ptr; -} - -/* - * Get transport type from name. - */ -PJ_DEF(pjsip_transport_type_e) -pjsip_transport_get_type_from_name(const pj_str_t *name) -{ - unsigned i; - - for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) { - if (pj_stricmp(name, &transport_names[i].name) == 0) { - return transport_names[i].type; - } - } - return PJSIP_TRANSPORT_UNSPECIFIED; -} - -/* - * Create new transmit buffer. - */ + int status);
+
+static pj_ioqueue_callback ioqueue_transport_callback =
+{
+ &on_ioqueue_read,
+ &on_ioqueue_write,
+ &on_ioqueue_accept,
+ &on_ioqueue_connect
+};
+
+static void init_key_from_transport(transport_key *key,
+ const pjsip_transport_t *tr)
+{
+ /* This is to detect alignment problems. */
+ pj_assert(sizeof(transport_key) == 8);
+
+ key->type = (pj_uint8_t)tr->type;
+ key->zero = 0;
+ key->addr = pj_sockaddr_in_get_addr(&tr->remote_addr).s_addr;
+ key->port = pj_sockaddr_in_get_port(&tr->remote_addr);
+ /*
+ if (key->port == 0) {
+ key->port = pj_sockaddr_in_get_port(&tr->local_addr);
+ }
+ */
+}
+
+#if PJ_HAS_TCP
+static void init_tcp_key(transport_key *key, pjsip_transport_type_e type,
+ const pj_sockaddr_in *addr)
+{
+ /* This is to detect alignment problems. */
+ pj_assert(sizeof(transport_key) == 8);
+
+ key->type = (pj_uint8_t)type;
+ key->zero = 0;
+ key->addr = pj_sockaddr_in_get_addr(addr).s_addr;
+ key->port = pj_sockaddr_in_get_port(addr);
+}
+#endif
+
+static void init_udp_key(transport_key *key, pjsip_transport_type_e type,
+ const pj_sockaddr_in *addr)
+{
+ PJ_UNUSED_ARG(addr);
+
+ /* This is to detect alignment problems. */
+ pj_assert(sizeof(transport_key) == 8);
+
+ pj_memset(key, 0, sizeof(*key));
+ key->type = (pj_uint8_t)type;
+
+#if 0 /* Not sure why we need to make 127.0.0.1 a special case */
+ if (addr->sin_addr.s_addr == inet_addr("127.0.0.1")) {
+ /* This looks more complicated than it is because key->addr uses
+ * the host version of the address (i.e. converted with ntohl()).
+ */
+ pj_str_t localaddr = pj_str("127.0.0.1");
+ pj_sockaddr_in addr;
+ pj_sockaddr_set_str_addr(&addr, &localaddr);
+ key->addr = pj_sockaddr_in_get_addr(&addr);
+ }
+#endif
+}
+
+/*
+ * Get type format name (for pool name).
+ */
+static const char *transport_get_name_format( int type )
+{
+ switch (type) {
+ case PJSIP_TRANSPORT_UDP:
+ return " udp%p";
+#if PJ_HAS_TCP
+ case PJSIP_TRANSPORT_TCP:
+ return " tcp%p";
+ case PJSIP_TRANSPORT_TLS:
+ return " tls%p";
+ case PJSIP_TRANSPORT_SCTP:
+ return "sctp%p";
+#endif
+ }
+ pj_assert(0);
+ return 0;
+}
+
+/*
+ * Get the default SIP port number for the specified type.
+ */
+PJ_DEF(int) pjsip_transport_get_default_port_for_type(pjsip_transport_type_e type)
+{
+ return transport_names[type].port;
+}
+
+/*
+ * Get transport name.
+ */
+static const char *get_type_name(int type)
+{
+ return transport_names[type].name.ptr;
+}
+
+/*
+ * Get transport type from name.
+ */
+PJ_DEF(pjsip_transport_type_e)
+pjsip_transport_get_type_from_name(const pj_str_t *name)
+{
+ unsigned i;
+
+ for (i=0; i<PJ_ARRAY_SIZE(transport_names); ++i) {
+ if (pj_stricmp(name, &transport_names[i].name) == 0) {
+ return transport_names[i].type;
+ }
+ }
+ return PJSIP_TRANSPORT_UNSPECIFIED;
+}
+
+/*
+ * Create new transmit buffer.
+ */
pj_status_t pjsip_tx_data_create( pjsip_transport_mgr *mgr,
- pjsip_tx_data **p_tdata ) -{ - pj_pool_t *pool; - pjsip_tx_data *tdata; + pjsip_tx_data **p_tdata )
+{
+ pj_pool_t *pool;
+ pjsip_tx_data *tdata;
pj_status_t status;
- - PJ_LOG(5, ("", "pjsip_tx_data_create")); +
+ PJ_LOG(5, ("", "pjsip_tx_data_create"));
PJ_ASSERT_RETURN(mgr && p_tdata, PJ_EINVAL);
- - pool = pjsip_endpt_create_pool( mgr->endpt, "ptdt%p", - PJSIP_POOL_LEN_TDATA, - PJSIP_POOL_INC_TDATA ); - if (!pool) { - return PJ_ENOMEM; - } - tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data)); - tdata->pool = pool; - tdata->mgr = mgr; - pj_sprintf(tdata->obj_name,"txd%p", tdata); - - status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt); - if (status != PJ_SUCCESS) { - pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool ); - return status; - } +
+ pool = pjsip_endpt_create_pool( mgr->endpt, "ptdt%p",
+ PJSIP_POOL_LEN_TDATA,
+ PJSIP_POOL_INC_TDATA );
+ if (!pool) {
+ return PJ_ENOMEM;
+ }
+ tdata = pj_pool_calloc(pool, 1, sizeof(pjsip_tx_data));
+ tdata->pool = pool;
+ tdata->mgr = mgr;
+ pj_sprintf(tdata->obj_name,"txd%p", tdata);
+
+ status = pj_atomic_create(tdata->pool, 0, &tdata->ref_cnt);
+ if (status != PJ_SUCCESS) {
+ pjsip_endpt_destroy_pool( mgr->endpt, tdata->pool );
+ return status;
+ }
- *p_tdata = tdata; - return PJ_SUCCESS; -} - -/* - * Add reference to tx buffer. - */ -PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata ) -{ - pj_atomic_inc(tdata->ref_cnt); -} - -/* - * Decrease transport data reference, destroy it when the reference count - * reaches zero. - */ -PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata ) -{ - pj_assert( pj_atomic_get(tdata->ref_cnt) > 0); - if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) { - PJ_LOG(6,(tdata->obj_name, "destroying txdata")); - pj_atomic_destroy( tdata->ref_cnt ); - pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool ); - } -} - -/* - * Invalidate the content of the print buffer to force the message to be - * re-printed when sent. - */ -PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata ) -{ - tdata->buf.cur = tdata->buf.start; -} - -/* - * Get the transport type. - */ -PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr) -{ - return tr->type; -} - -/* - * Get transport type from transport flag. - */ -PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag) -{ -#if PJ_HAS_TCP - if (flag & PJSIP_TRANSPORT_SECURE) { - return PJSIP_TRANSPORT_TLS; - } else if (flag & PJSIP_TRANSPORT_RELIABLE) { - return PJSIP_TRANSPORT_TCP; - } else -#else - PJ_UNUSED_ARG(flag); -#endif - { - return PJSIP_TRANSPORT_UDP; - } -} - -/* - * Get the transport type name. - */ -PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr) -{ - return get_type_name(tr->type); -} - -/* - * Get the transport's object name. - */ -PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr ) -{ - return tr->obj_name; -} - -/* - * Get the transport's reference counter. - */ -PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr ) -{ - return pj_atomic_get(tr->ref_cnt); -} - -/* - * Get transport local address. - */ -PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr ) -{ - return &tr->local_addr; -} - -/* - * Get address name. - */ -PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr) -{ - return &tr->addr_name; -} - -/* - * Get transport remote address. - */ -PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr ) -{ - return &tr->remote_addr; -} - -/* - * Get transport flag. - */ -PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr ) -{ - return tr->flag; -} - -/* - * Add reference to the specified transport. - */ -PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr ) -{ - pj_atomic_inc(tr->ref_cnt); -} - -/* - * Decrease the reference time of the transport. - */ -PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr ) -{ - pj_assert(tr->ref_cnt > 0); - if (pj_atomic_dec_and_get(tr->ref_cnt) == 0) { - pj_gettimeofday(&tr->close_time); - tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT; - } -} - -/* - * Open the underlying transport. - */ -static pj_status_t create_socket( pjsip_transport_type_e type, + *p_tdata = tdata;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Add reference to tx buffer.
+ */
+PJ_DEF(void) pjsip_tx_data_add_ref( pjsip_tx_data *tdata )
+{
+ pj_atomic_inc(tdata->ref_cnt);
+}
+
+/*
+ * Decrease transport data reference, destroy it when the reference count
+ * reaches zero.
+ */
+PJ_DEF(void) pjsip_tx_data_dec_ref( pjsip_tx_data *tdata )
+{
+ pj_assert( pj_atomic_get(tdata->ref_cnt) > 0);
+ if (pj_atomic_dec_and_get(tdata->ref_cnt) <= 0) {
+ PJ_LOG(6,(tdata->obj_name, "destroying txdata"));
+ pj_atomic_destroy( tdata->ref_cnt );
+ pjsip_endpt_destroy_pool( tdata->mgr->endpt, tdata->pool );
+ }
+}
+
+/*
+ * Invalidate the content of the print buffer to force the message to be
+ * re-printed when sent.
+ */
+PJ_DEF(void) pjsip_tx_data_invalidate_msg( pjsip_tx_data *tdata )
+{
+ tdata->buf.cur = tdata->buf.start;
+}
+
+/*
+ * Get the transport type.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_transport_get_type( const pjsip_transport_t * tr)
+{
+ return tr->type;
+}
+
+/*
+ * Get transport type from transport flag.
+ */
+PJ_DEF(pjsip_transport_type_e) pjsip_get_transport_type_from_flag(unsigned flag)
+{
+#if PJ_HAS_TCP
+ if (flag & PJSIP_TRANSPORT_SECURE) {
+ return PJSIP_TRANSPORT_TLS;
+ } else if (flag & PJSIP_TRANSPORT_RELIABLE) {
+ return PJSIP_TRANSPORT_TCP;
+ } else
+#else
+ PJ_UNUSED_ARG(flag);
+#endif
+ {
+ return PJSIP_TRANSPORT_UDP;
+ }
+}
+
+/*
+ * Get the transport type name.
+ */
+PJ_DEF(const char *) pjsip_transport_get_type_name( const pjsip_transport_t * tr)
+{
+ return get_type_name(tr->type);
+}
+
+/*
+ * Get the transport's object name.
+ */
+PJ_DEF(const char*) pjsip_transport_get_obj_name( const pjsip_transport_t *tr )
+{
+ return tr->obj_name;
+}
+
+/*
+ * Get the transport's reference counter.
+ */
+PJ_DEF(int) pjsip_transport_get_ref_cnt( const pjsip_transport_t *tr )
+{
+ return pj_atomic_get(tr->ref_cnt);
+}
+
+/*
+ * Get transport local address.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_local_addr( pjsip_transport_t *tr )
+{
+ return &tr->local_addr;
+}
+
+/*
+ * Get address name.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_addr_name (pjsip_transport_t *tr)
+{
+ return &tr->addr_name;
+}
+
+/*
+ * Get transport remote address.
+ */
+PJ_DEF(const pj_sockaddr_in*) pjsip_transport_get_remote_addr( const pjsip_transport_t *tr )
+{
+ return &tr->remote_addr;
+}
+
+/*
+ * Get transport flag.
+ */
+PJ_DEF(unsigned) pjsip_transport_get_flag( const pjsip_transport_t * tr )
+{
+ return tr->flag;
+}
+
+/*
+ * Add reference to the specified transport.
+ */
+PJ_DEF(void) pjsip_transport_add_ref( pjsip_transport_t * tr )
+{
+ pj_atomic_inc(tr->ref_cnt);
+}
+
+/*
+ * Decrease the reference time of the transport.
+ */
+PJ_DEF(void) pjsip_transport_dec_ref( pjsip_transport_t *tr )
+{
+ pj_assert(tr->ref_cnt > 0);
+ if (pj_atomic_dec_and_get(tr->ref_cnt) == 0) {
+ pj_gettimeofday(&tr->close_time);
+ tr->close_time.sec += PJSIP_TRANSPORT_CLOSE_TIMEOUT;
+ }
+}
+
+/*
+ * Open the underlying transport.
+ */
+static pj_status_t create_socket( pjsip_transport_type_e type,
pj_sockaddr_in *local,
- pj_sock_t *p_sock) -{ - int sock_family; - int sock_type; - int sock_proto; + pj_sock_t *p_sock)
+{
+ int sock_family;
+ int sock_type;
+ int sock_proto;
int len;
- pj_status_t status; - pj_sock_t sock; - - /* Set socket parameters */ - if (type == PJSIP_TRANSPORT_UDP) { - sock_family = PJ_AF_INET; - sock_type = PJ_SOCK_DGRAM; - sock_proto = 0; - -#if PJ_HAS_TCP - } else if (type == PJSIP_TRANSPORT_TCP) { - sock_family = PJ_AF_INET; - sock_type = PJ_SOCK_STREAM; - sock_proto = 0; -#endif - } else { - return PJ_EINVAL; - } - - /* Create socket. */ - status = pj_sock_socket( sock_family, sock_type, sock_proto, &sock); - if (status != PJ_SUCCESS) - return status; - - /* Bind the socket to the requested address, or if no address is - * specified, let the operating system chooses the address. - */ - if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) { + pj_status_t status;
+ pj_sock_t sock;
+
+ /* Set socket parameters */
+ if (type == PJSIP_TRANSPORT_UDP) {
+ sock_family = PJ_AF_INET;
+ sock_type = PJ_SOCK_DGRAM;
+ sock_proto = 0;
+
+#if PJ_HAS_TCP
+ } else if (type == PJSIP_TRANSPORT_TCP) {
+ sock_family = PJ_AF_INET;
+ sock_type = PJ_SOCK_STREAM;
+ sock_proto = 0;
+#endif
+ } else {
+ return PJ_EINVAL;
+ }
+
+ /* Create socket. */
+ status = pj_sock_socket( sock_family, sock_type, sock_proto, &sock);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ /* Bind the socket to the requested address, or if no address is
+ * specified, let the operating system chooses the address.
+ */
+ if (/*local->sin_addr.s_addr != 0 &&*/ local->sin_port != 0) {
/* Bind to the requested address. */
- status = pj_sock_bind(sock, local, sizeof(*local)); - if (status != PJ_SUCCESS) { - pj_sock_close(sock); - return status; - } - } else if (type == PJSIP_TRANSPORT_UDP) { - /* Only for UDP sockets: bind to any address so that the operating - * system allocates the port for us. For TCP, let the OS implicitly - * bind the socket with connect() syscall (if we bind now, then we'll - * get 0.0.0.0 as local address). - */ - pj_memset(local, 0, sizeof(*local)); + status = pj_sock_bind(sock, local, sizeof(*local));
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+ } else if (type == PJSIP_TRANSPORT_UDP) {
+ /* Only for UDP sockets: bind to any address so that the operating
+ * system allocates the port for us. For TCP, let the OS implicitly
+ * bind the socket with connect() syscall (if we bind now, then we'll
+ * get 0.0.0.0 as local address).
+ */
+ pj_memset(local, 0, sizeof(*local));
local->sin_family = PJ_AF_INET;
- status = pj_sock_bind(sock, local, sizeof(*local)); - if (status != PJ_SUCCESS) { - pj_sock_close(sock); - return status; - } - - /* Get the local address. */ + status = pj_sock_bind(sock, local, sizeof(*local));
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+
+ /* Get the local address. */
len = sizeof(pj_sockaddr_in);
- status = pj_sock_getsockname(sock, local, &len); - if (status != PJ_SUCCESS) { - pj_sock_close(sock); - return status; - } - } -
- *p_sock = sock; - return PJ_SUCCESS; -} - -/* - * Close the transport. - */ -static void destroy_socket( pjsip_transport_t * tr) -{ - pj_assert( pj_atomic_get(tr->ref_cnt) == 0); - pj_sock_close(tr->sock); - tr->sock = -1; -} - -/* - * Create a new transport object. - */ -static pj_status_t create_transport( pjsip_transport_mgr *mgr, - pjsip_transport_type_e type, - pj_sock_t sock_hnd, - const pj_sockaddr_in *local_addr, + status = pj_sock_getsockname(sock, local, &len);
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock);
+ return status;
+ }
+ }
+
+ *p_sock = sock;
+ return PJ_SUCCESS;
+}
+
+/*
+ * Close the transport.
+ */
+static void destroy_socket( pjsip_transport_t * tr)
+{
+ pj_assert( pj_atomic_get(tr->ref_cnt) == 0);
+ pj_sock_close(tr->sock);
+ tr->sock = -1;
+}
+
+/*
+ * Create a new transport object.
+ */
+static pj_status_t create_transport( pjsip_transport_mgr *mgr,
+ pjsip_transport_type_e type,
+ pj_sock_t sock_hnd,
+ const pj_sockaddr_in *local_addr,
const pj_sockaddr_in *addr_name,
- pjsip_transport_t **p_transport ) -{ - pj_pool_t *tr_pool=NULL, *rdata_pool=NULL; + pjsip_transport_t **p_transport )
+{
+ pj_pool_t *tr_pool=NULL, *rdata_pool=NULL;
pjsip_transport_t *tr = NULL;
- pj_status_t status; - - /* Allocate pool for transport from endpoint. */ - tr_pool = pjsip_endpt_create_pool( mgr->endpt, - transport_get_name_format(type), - PJSIP_POOL_LEN_TRANSPORT, - PJSIP_POOL_INC_TRANSPORT ); + pj_status_t status;
+
+ /* Allocate pool for transport from endpoint. */
+ tr_pool = pjsip_endpt_create_pool( mgr->endpt,
+ transport_get_name_format(type),
+ PJSIP_POOL_LEN_TRANSPORT,
+ PJSIP_POOL_INC_TRANSPORT );
if (!tr_pool) {
- status = PJ_ENOMEM; - goto on_error; - } - - /* Allocate pool for rdata from endpoint. */ - rdata_pool = pjsip_endpt_create_pool( mgr->endpt, - "prdt%p", - PJSIP_POOL_LEN_RDATA, - PJSIP_POOL_INC_RDATA ); + status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ /* Allocate pool for rdata from endpoint. */
+ rdata_pool = pjsip_endpt_create_pool( mgr->endpt,
+ "prdt%p",
+ PJSIP_POOL_LEN_RDATA,
+ PJSIP_POOL_INC_RDATA );
if (!rdata_pool) {
- status = PJ_ENOMEM; - goto on_error; - } - - /* Allocate and initialize the transport. */ - tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr)); - tr->pool = tr_pool; - tr->type = type; - tr->mgr = mgr; - tr->sock = sock_hnd; - pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in)); - pj_list_init(&tr->cb_list); - pj_sprintf(tr->obj_name, transport_get_name_format(type), tr); - - if (type != PJSIP_TRANSPORT_UDP) { - tr->flag |= PJSIP_TRANSPORT_RELIABLE; - } - - /* Address name. */ - if (addr_name == NULL) { - addr_name = &tr->local_addr; - } - pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name)); - + status = PJ_ENOMEM;
+ goto on_error;
+ }
+
+ /* Allocate and initialize the transport. */
+ tr = pj_pool_calloc(tr_pool, 1, sizeof(*tr));
+ tr->pool = tr_pool;
+ tr->type = type;
+ tr->mgr = mgr;
+ tr->sock = sock_hnd;
+ pj_memcpy(&tr->local_addr, local_addr, sizeof(pj_sockaddr_in));
+ pj_list_init(&tr->cb_list);
+ pj_sprintf(tr->obj_name, transport_get_name_format(type), tr);
+
+ if (type != PJSIP_TRANSPORT_UDP) {
+ tr->flag |= PJSIP_TRANSPORT_RELIABLE;
+ }
+
+ /* Address name. */
+ if (addr_name == NULL) {
+ addr_name = &tr->local_addr;
+ }
+ pj_memcpy(&tr->addr_name, addr_name, sizeof(*addr_name));
+
/* Create atomic */
- status = pj_atomic_create(tr_pool, 0, &tr->ref_cnt); - if (status != PJ_SUCCESS) - goto on_error; - - /* Init rdata in the transport. */ - tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata)); - tr->rdata->pool = rdata_pool; - tr->rdata->len = 0; - tr->rdata->transport = tr; - - /* Init transport mutex. */ - status = pj_mutex_create_recursive(tr_pool, "mtr%p", &tr->tr_mutex); - if (status != PJ_SUCCESS) - goto on_error; - - /* Register to I/O Queue */ - status = pj_ioqueue_register_sock( tr_pool, mgr->ioqueue, - tr->sock, tr, + status = pj_atomic_create(tr_pool, 0, &tr->ref_cnt);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Init rdata in the transport. */
+ tr->rdata = pj_pool_alloc(rdata_pool, sizeof(*tr->rdata));
+ tr->rdata->pool = rdata_pool;
+ tr->rdata->len = 0;
+ tr->rdata->transport = tr;
+
+ /* Init transport mutex. */
+ status = pj_mutex_create_recursive(tr_pool, "mtr%p", &tr->tr_mutex);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Register to I/O Queue */
+ status = pj_ioqueue_register_sock( tr_pool, mgr->ioqueue,
+ tr->sock, tr,
&ioqueue_transport_callback,
- &tr->key); - if (status != PJ_SUCCESS) - goto on_error; -
- *p_transport = tr; - return PJ_SUCCESS; - -on_error: - if (tr && tr->tr_mutex) { - pj_mutex_destroy(tr->tr_mutex); - } - if (tr_pool) { - pjsip_endpt_destroy_pool(mgr->endpt, tr_pool); - } - if (rdata_pool) { - pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool); - } - return status; -} - -/* - * Destroy transport. - */ -static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr) -{ - transport_key hash_key; - - /* Remove from I/O queue. */ - pj_ioqueue_unregister( tr->key ); - - /* Remove from hash table */ - init_key_from_transport(&hash_key, tr); - pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL); - - /* Close transport. */ - destroy_socket(tr); - - /* Destroy the transport mutex. */ - pj_mutex_destroy(tr->tr_mutex); - - /* Destroy atomic */ - pj_atomic_destroy( tr->ref_cnt ); - - /* Release the pool associated with the rdata. */ - pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool ); - - /* Release the pool associated with the transport. */ - pjsip_endpt_destroy_pool(mgr->endpt, tr->pool ); -} - - + &tr->key);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ *p_transport = tr;
+ return PJ_SUCCESS;
+
+on_error:
+ if (tr && tr->tr_mutex) {
+ pj_mutex_destroy(tr->tr_mutex);
+ }
+ if (tr_pool) {
+ pjsip_endpt_destroy_pool(mgr->endpt, tr_pool);
+ }
+ if (rdata_pool) {
+ pjsip_endpt_destroy_pool(mgr->endpt, rdata_pool);
+ }
+ return status;
+}
+
+/*
+ * Destroy transport.
+ */
+static void destroy_transport( pjsip_transport_mgr *mgr, pjsip_transport_t *tr)
+{
+ transport_key hash_key;
+
+ /* Remove from I/O queue. */
+ pj_ioqueue_unregister( tr->key );
+
+ /* Remove from hash table */
+ init_key_from_transport(&hash_key, tr);
+ pj_hash_set(NULL, mgr->transport_table, &hash_key, sizeof(hash_key), NULL);
+
+ /* Close transport. */
+ destroy_socket(tr);
+
+ /* Destroy the transport mutex. */
+ pj_mutex_destroy(tr->tr_mutex);
+
+ /* Destroy atomic */
+ pj_atomic_destroy( tr->ref_cnt );
+
+ /* Release the pool associated with the rdata. */
+ pjsip_endpt_destroy_pool(mgr->endpt, tr->rdata->pool );
+
+ /* Release the pool associated with the transport. */
+ pjsip_endpt_destroy_pool(mgr->endpt, tr->pool );
+}
+
+
static pj_status_t transport_send_msg( pjsip_transport_t *tr,
- pjsip_tx_data *tdata, + pjsip_tx_data *tdata,
const pj_sockaddr_in *addr,
- pj_ssize_t *p_sent) -{ - const char *buf = tdata->buf.start; - pj_ssize_t size; - pj_status_t status; + pj_ssize_t *p_sent)
+{
+ const char *buf = tdata->buf.start;
+ pj_ssize_t size;
+ pj_status_t status;
/* Can only send if tdata is not being sent! */
if (pj_ioqueue_is_pending(tr->key, &tdata->op_key))
return PJSIP_EPENDINGTX;
- - /* Allocate buffer if necessary. */ - if (tdata->buf.start == NULL) { - tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN); - tdata->buf.cur = tdata->buf.start; - tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN; - } - - /* Print the message if it's not printed */ - if (tdata->buf.cur <= tdata->buf.start) { - size = pjsip_msg_print( tdata->msg, tdata->buf.start, - tdata->buf.end - tdata->buf.start); +
+ /* Allocate buffer if necessary. */
+ if (tdata->buf.start == NULL) {
+ tdata->buf.start = pj_pool_alloc( tdata->pool, PJSIP_MAX_PKT_LEN);
+ tdata->buf.cur = tdata->buf.start;
+ tdata->buf.end = tdata->buf.start + PJSIP_MAX_PKT_LEN;
+ }
+
+ /* Print the message if it's not printed */
+ if (tdata->buf.cur <= tdata->buf.start) {
+ size = pjsip_msg_print( tdata->msg, tdata->buf.start,
+ tdata->buf.end - tdata->buf.start);
if (size < 0) {
- return PJSIP_EMSGTOOLONG; + return PJSIP_EMSGTOOLONG;
}
- pj_assert(size != 0); - tdata->buf.cur += size; - tdata->buf.cur[size] = '\0'; - } - - /* Send the message. */ - buf = tdata->buf.start; - size = tdata->buf.cur - tdata->buf.start; - - if (tr->type == PJSIP_TRANSPORT_UDP) { - PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n" - "----------- begin msg ------------\n" - "%s" - "------------ end msg -------------", - pj_inet_ntoa(addr->sin_addr), - pj_sockaddr_in_get_port(addr), - size, buf)); -
- status = pj_ioqueue_sendto( tr->key, &tdata->op_key, - buf, &size, 0, addr, sizeof(*addr)); - } -#if PJ_HAS_TCP - else { - PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n" - "----------- begin msg ------------\n" - "%s" - "------------ end msg -------------", - size, buf)); - - status = pj_ioqueue_send(tr->key, &tdata->op_key, buf, &size, 0); - } -#else - else { + pj_assert(size != 0);
+ tdata->buf.cur += size;
+ tdata->buf.cur[size] = '\0';
+ }
+
+ /* Send the message. */
+ buf = tdata->buf.start;
+ size = tdata->buf.cur - tdata->buf.start;
+
+ if (tr->type == PJSIP_TRANSPORT_UDP) {
+ PJ_LOG(4,(tr->obj_name, "sendto %s:%d, %d bytes, data:\n"
+ "----------- begin msg ------------\n"
+ "%s"
+ "------------ end msg -------------",
+ pj_inet_ntoa(addr->sin_addr),
+ pj_sockaddr_in_get_port(addr),
+ size, buf));
+
+ status = pj_ioqueue_sendto( tr->key, &tdata->op_key,
+ buf, &size, 0, addr, sizeof(*addr));
+ }
+#if PJ_HAS_TCP
+ else {
+ PJ_LOG(4,(tr->obj_name, "sending %d bytes, data:\n"
+ "----------- begin msg ------------\n"
+ "%s"
+ "------------ end msg -------------",
+ size, buf));
+
+ status = pj_ioqueue_send(tr->key, &tdata->op_key, buf, &size, 0);
+ }
+#else
+ else {
pj_assert(!"Unsupported transport");
- status = PJSIP_EUNSUPTRANSPORT; - } -#endif -
- *p_sent = size; - return status; -} - -/* - * Send a SIP message using the specified transport, to the address specified - * in the outgoing data. - */ -PJ_DEF(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr, - pjsip_tx_data *tdata, + status = PJSIP_EUNSUPTRANSPORT;
+ }
+#endif
+
+ *p_sent = size;
+ return status;
+}
+
+/*
+ * Send a SIP message using the specified transport, to the address specified
+ * in the outgoing data.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_send_msg( pjsip_transport_t *tr,
+ pjsip_tx_data *tdata,
const pj_sockaddr_in *addr,
- pj_ssize_t *sent) -{ - PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name)); - - return transport_send_msg(tr, tdata, addr, sent ); -} - -/////////////////////////////////////////////////////////////////////////////// - -/* - * Create a new transport manager. - */ -PJ_DEF(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool, - pjsip_endpoint * endpt, + pj_ssize_t *sent)
+{
+ PJ_LOG(5, (tr->obj_name, "pjsip_transport_send_msg(tdata=%s)", tdata->obj_name));
+
+ return transport_send_msg(tr, tdata, addr, sent );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Create a new transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_mgr_create( pj_pool_t *pool,
+ pjsip_endpoint * endpt,
void (*cb)(pjsip_endpoint*,
pjsip_rx_data *),
- pjsip_transport_mgr **p_mgr) -{ - pjsip_transport_mgr *mgr; + pjsip_transport_mgr **p_mgr)
+{
+ pjsip_transport_mgr *mgr;
pj_status_t status;
- - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()")); - - mgr = pj_pool_alloc(pool, sizeof(*mgr)); - mgr->endpt = endpt; - mgr->message_callback = cb; +
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_create()"));
+
+ mgr = pj_pool_alloc(pool, sizeof(*mgr));
+ mgr->endpt = endpt;
+ mgr->message_callback = cb;
mgr->send_buf_size = DEFAULT_SO_SNDBUF;
mgr->recv_buf_size = DEFAULT_SO_RCVBUF;
- - mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE); - if (!mgr->transport_table) { - return PJ_ENOMEM; - } - status = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS, &mgr->ioqueue); - if (status != PJ_SUCCESS) { - return status; - } - status = pj_mutex_create_recursive(pool, "tmgr%p", &mgr->mutex); - if (status != PJ_SUCCESS) { - pj_ioqueue_destroy(mgr->ioqueue); - return status; - } - pj_gettimeofday(&mgr->next_idle_check); +
+ mgr->transport_table = pj_hash_create(pool, MGR_HASH_TABLE_SIZE);
+ if (!mgr->transport_table) {
+ return PJ_ENOMEM;
+ }
+ status = pj_ioqueue_create(pool, PJSIP_MAX_TRANSPORTS, &mgr->ioqueue);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+ status = pj_mutex_create_recursive(pool, "tmgr%p", &mgr->mutex);
+ if (status != PJ_SUCCESS) {
+ pj_ioqueue_destroy(mgr->ioqueue);
+ return status;
+ }
+ pj_gettimeofday(&mgr->next_idle_check);
mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
- *p_mgr = mgr; - return status; -} - -/* - * Destroy transport manager. - */ -PJ_DEF(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr ) -{ - pj_hash_iterator_t itr_val; - pj_hash_iterator_t *itr; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()")); - - pj_mutex_lock(mgr->mutex); - - itr = pjsip_transport_first(mgr, &itr_val); - while (itr != NULL) { - pj_hash_iterator_t *next; - pjsip_transport_t *transport; - - transport = pjsip_transport_this(mgr, itr); - - next = pjsip_transport_next(mgr, itr); - - pj_atomic_set(transport->ref_cnt, 0); - destroy_transport( mgr, transport); - - itr = next; - } - pj_ioqueue_destroy(mgr->ioqueue); - + *p_mgr = mgr;
+ return status;
+}
+
+/*
+ * Destroy transport manager.
+ */
+PJ_DEF(pj_status_t) pjsip_transport_mgr_destroy( pjsip_transport_mgr *mgr )
+{
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_destroy()"));
+
+ pj_mutex_lock(mgr->mutex);
+
+ itr = pjsip_transport_first(mgr, &itr_val);
+ while (itr != NULL) {
+ pj_hash_iterator_t *next;
+ pjsip_transport_t *transport;
+
+ transport = pjsip_transport_this(mgr, itr);
+
+ next = pjsip_transport_next(mgr, itr);
+
+ pj_atomic_set(transport->ref_cnt, 0);
+ destroy_transport( mgr, transport);
+
+ itr = next;
+ }
+ pj_ioqueue_destroy(mgr->ioqueue);
+
pj_mutex_unlock(mgr->mutex);
- return PJ_SUCCESS; -} - -/* - * Create listener - */ -static pj_status_t create_listener( pjsip_transport_mgr *mgr, - pjsip_transport_type_e type, - pj_sock_t sock_hnd, - pj_sockaddr_in *local_addr, - const pj_sockaddr_in *addr_name) -{ - pjsip_transport_t *tr; + return PJ_SUCCESS;
+}
+
+/*
+ * Create listener
+ */
+static pj_status_t create_listener( pjsip_transport_mgr *mgr,
+ pjsip_transport_type_e type,
+ pj_sock_t sock_hnd,
+ pj_sockaddr_in *local_addr,
+ const pj_sockaddr_in *addr_name)
+{
+ pjsip_transport_t *tr;
struct transport_key *hash_key;
- const pj_str_t loopback_addr = { "127.0.0.1", 9 }; - pj_status_t status; + const pj_str_t loopback_addr = { "127.0.0.1", 9 };
+ pj_status_t status;
- if (mgr->send_buf_size != 0) { + if (mgr->send_buf_size != 0) {
int opt_val = mgr->send_buf_size;
status = pj_sock_setsockopt( sock_hnd, PJ_SOL_SOCKET,
PJ_SO_SNDBUF,
&opt_val, sizeof(opt_val));
- - if (status != PJ_SUCCESS) { - return status; +
+ if (status != PJ_SUCCESS) {
+ return status;
}
- } + }
- if (mgr->recv_buf_size != 0) { + if (mgr->recv_buf_size != 0) {
int opt_val = mgr->recv_buf_size;
status = pj_sock_setsockopt( sock_hnd, PJ_SOL_SOCKET,
PJ_SO_RCVBUF,
- &opt_val, sizeof(opt_val)); - if (status != PJ_SUCCESS) { - return status; + &opt_val, sizeof(opt_val));
+ if (status != PJ_SUCCESS) {
+ return status;
}
- } - - status = create_transport(mgr, type, sock_hnd, local_addr, addr_name, &tr); - if (status != PJ_SUCCESS) { - pj_sock_close(sock_hnd); - return status; - } -#if PJ_HAS_TCP - if (type == PJSIP_TRANSPORT_TCP) { -
- status = pj_sock_listen(tr->sock, BACKLOG); - if (status != 0) { - destroy_transport(mgr, tr); - return status; - } + }
+
+ status = create_transport(mgr, type, sock_hnd, local_addr, addr_name, &tr);
+ if (status != PJ_SUCCESS) {
+ pj_sock_close(sock_hnd);
+ return status;
+ }
+#if PJ_HAS_TCP
+ if (type == PJSIP_TRANSPORT_TCP) {
+
+ status = pj_sock_listen(tr->sock, BACKLOG);
+ if (status != 0) {
+ destroy_transport(mgr, tr);
+ return status;
+ }
/* Discard immediate connections. */
do {
- tr->accept_data.addrlen = sizeof(tr->accept_data.local); - status = pj_ioqueue_accept(tr->key, &tr->accept_op, - &tr->accept_data.sock, - &tr->accept_data.local, - &tr->accept_data.remote, + tr->accept_data.addrlen = sizeof(tr->accept_data.local);
+ status = pj_ioqueue_accept(tr->key, &tr->accept_op,
+ &tr->accept_data.sock,
+ &tr->accept_data.local,
+ &tr->accept_data.remote,
&tr->accept_data.addrlen);
if (status==PJ_SUCCESS) {
pj_sock_close(tr->accept_data.sock);
- } else if (status != PJ_EPENDING) { - destroy_transport(mgr, tr); - return status; + } else if (status != PJ_EPENDING) {
+ destroy_transport(mgr, tr);
+ return status;
}
- } while (status==PJ_SUCCESS); - - } else -#endif + } while (status==PJ_SUCCESS);
+
+ } else
+#endif
if (type == PJSIP_TRANSPORT_UDP) {
pj_ssize_t bytes;
/* Discard immediate data. */
- do { + do {
tr->rdata->addr_len = sizeof(tr->rdata->addr);
- bytes = PJSIP_MAX_PKT_LEN; - status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key, - tr->rdata->packet, &bytes, 0, - &tr->rdata->addr, + bytes = PJSIP_MAX_PKT_LEN;
+ status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
+ tr->rdata->packet, &bytes, 0,
+ &tr->rdata->addr,
&tr->rdata->addr_len);
if (status == PJ_SUCCESS) {
- ; - } else if (status != PJ_EPENDING) { - destroy_transport(mgr, tr); - return status; + ;
+ } else if (status != PJ_EPENDING) {
+ destroy_transport(mgr, tr);
+ return status;
}
- } while (status == PJ_SUCCESS); - } - - pj_atomic_set(tr->ref_cnt, 1); - - /* Listeners normally have no remote address */ - pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr)); - - /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1. - * See further comments on struct pjsip_transport_t definition. + } while (status == PJ_SUCCESS);
+ }
+
+ pj_atomic_set(tr->ref_cnt, 1);
+
+ /* Listeners normally have no remote address */
+ pj_memset(&tr->remote_addr, 0, sizeof(tr->remote_addr));
+
+ /* Set remote address to 127.0.0.1 for UDP socket bound to 127.0.0.1.
+ * See further comments on struct pjsip_transport_t definition.
*/
if (type == PJSIP_TRANSPORT_UDP &&
local_addr->sin_addr.s_addr == pj_inet_addr(&loopback_addr).s_addr)
- { - pj_str_t localaddr = pj_str("127.0.0.1"); - pj_sockaddr_in_set_str_addr( &tr->remote_addr, &localaddr); - } - hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); - init_key_from_transport(hash_key, tr); - - pj_mutex_lock(mgr->mutex); + {
+ pj_str_t localaddr = pj_str("127.0.0.1");
+ pj_sockaddr_in_set_str_addr( &tr->remote_addr, &localaddr);
+ }
+ hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+ init_key_from_transport(hash_key, tr);
+
+ pj_mutex_lock(mgr->mutex);
pj_hash_set(tr->pool, mgr->transport_table,
- hash_key, sizeof(transport_key), tr); - pj_mutex_unlock(mgr->mutex); - - PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d", - get_type_name(tr->type), - pj_inet_ntoa(tr->local_addr.sin_addr), - pj_sockaddr_in_get_port(&tr->local_addr))); - PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d", - get_type_name(tr->type), - pj_inet_ntoa(tr->addr_name.sin_addr), - pj_sockaddr_in_get_port(&tr->addr_name))); - return PJ_SUCCESS; -} - -/* - * Create listener. - */ -PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr, - pjsip_transport_type_e type, - pj_sockaddr_in *local_addr, - const pj_sockaddr_in *addr_name) -{ - pj_sock_t sock_hnd; + hash_key, sizeof(transport_key), tr);
+ pj_mutex_unlock(mgr->mutex);
+
+ PJ_LOG(4,(tr->obj_name, "Listening at %s %s:%d",
+ get_type_name(tr->type),
+ pj_inet_ntoa(tr->local_addr.sin_addr),
+ pj_sockaddr_in_get_port(&tr->local_addr)));
+ PJ_LOG(4,(tr->obj_name, "Listener public address is at %s %s:%d",
+ get_type_name(tr->type),
+ pj_inet_ntoa(tr->addr_name.sin_addr),
+ pj_sockaddr_in_get_port(&tr->addr_name)));
+ return PJ_SUCCESS;
+}
+
+/*
+ * Create listener.
+ */
+PJ_DEF(pj_status_t) pjsip_create_listener( pjsip_transport_mgr *mgr,
+ pjsip_transport_type_e type,
+ pj_sockaddr_in *local_addr,
+ const pj_sockaddr_in *addr_name)
+{
+ pj_sock_t sock_hnd;
pj_status_t status;
- - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type)); - - status = create_socket(type, local_addr, &sock_hnd); - if (status != PJ_SUCCESS) { - return status; - } - - return create_listener(mgr, type, sock_hnd, local_addr, addr_name); -} - -/* - * Create UDP listener. - */ -PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr, - pj_sock_t sock, - const pj_sockaddr_in *addr_name) -{ +
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_create_listener(type=%d)", type));
+
+ status = create_socket(type, local_addr, &sock_hnd);
+ if (status != PJ_SUCCESS) {
+ return status;
+ }
+
+ return create_listener(mgr, type, sock_hnd, local_addr, addr_name);
+}
+
+/*
+ * Create UDP listener.
+ */
+PJ_DEF(pj_status_t) pjsip_create_udp_listener( pjsip_transport_mgr *mgr,
+ pj_sock_t sock,
+ const pj_sockaddr_in *addr_name)
+{
pj_sockaddr_in local_addr;
- pj_status_t status; - int addrlen = sizeof(local_addr); + pj_status_t status;
+ int addrlen = sizeof(local_addr);
+
+ status = pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen);
+ if (status != PJ_SUCCESS)
+ return status;
- status = pj_sock_getsockname(sock, (pj_sockaddr_t*)&local_addr, &addrlen); - if (status != PJ_SUCCESS) - return status; - return create_listener(mgr, PJSIP_TRANSPORT_UDP, sock,
- &local_addr, addr_name); -} - -/* - * Find transport to be used to send message to remote destination. If no - * suitable transport is found, a new one will be created. - */ -PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr, - pj_pool_t *pool, - pjsip_transport_type_e type, - const pj_sockaddr_in *remote, - void *token, - pjsip_transport_completion_callback *cb) -{ - transport_key search_key, *hash_key; - pjsip_transport_t *tr; - pj_sockaddr_in local; - pj_sock_t sock_hnd; - pj_status_t status; - struct transport_callback *cb_rec; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()")); - - /* Create the callback record. - */ - cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec)); - cb_rec->token = token; - cb_rec->cb = cb; - - /* Create key for hash table look-up. - * The key creation is different for TCP and UDP. - */ -#if PJ_HAS_TCP - if (type==PJSIP_TRANSPORT_TCP) { - init_tcp_key(&search_key, type, remote); - } else -#endif - if (type==PJSIP_TRANSPORT_UDP) { - init_udp_key(&search_key, type, remote); - } - - /* Start lock the manager. */ - pj_mutex_lock(mgr->mutex); - - /* Lookup the transport in the hash table. */ - tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key)); - - if (tr) { - /* Transport found. If the transport is still busy (i.e. connecting - * is in progress), then just register the callback. Otherwise - * report via the callback if callback is specified. - */ - pj_mutex_unlock(mgr->mutex); - pj_mutex_lock(tr->tr_mutex); - - if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) { - /* Transport is busy. Just register the callback. */ - pj_list_insert_before(&tr->cb_list, cb_rec); - - } else { - /* Transport is ready. Call callback now. - */ - (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS); - } - pj_mutex_unlock(tr->tr_mutex); - - return; - } - - - /* Transport not found. Create new one. */ - pj_memset(&local, 0, sizeof(local)); - local.sin_family = PJ_AF_INET; - status = create_socket(type, &local, &sock_hnd); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, status); - return; - } - status = create_transport(mgr, type, sock_hnd, &local, NULL, &tr); - if (status != PJ_SUCCESS) { - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, status); - return; - } - -#if PJ_HAS_TCP - if (type == PJSIP_TRANSPORT_TCP) { - pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in)); + &local_addr, addr_name);
+}
+
+/*
+ * Find transport to be used to send message to remote destination. If no
+ * suitable transport is found, a new one will be created.
+ */
+PJ_DEF(void) pjsip_transport_get( pjsip_transport_mgr *mgr,
+ pj_pool_t *pool,
+ pjsip_transport_type_e type,
+ const pj_sockaddr_in *remote,
+ void *token,
+ pjsip_transport_completion_callback *cb)
+{
+ transport_key search_key, *hash_key;
+ pjsip_transport_t *tr;
+ pj_sockaddr_in local;
+ pj_sock_t sock_hnd;
+ pj_status_t status;
+ struct transport_callback *cb_rec;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_get()"));
+
+ /* Create the callback record.
+ */
+ cb_rec = pj_pool_calloc(pool, 1, sizeof(*cb_rec));
+ cb_rec->token = token;
+ cb_rec->cb = cb;
+
+ /* Create key for hash table look-up.
+ * The key creation is different for TCP and UDP.
+ */
+#if PJ_HAS_TCP
+ if (type==PJSIP_TRANSPORT_TCP) {
+ init_tcp_key(&search_key, type, remote);
+ } else
+#endif
+ if (type==PJSIP_TRANSPORT_UDP) {
+ init_udp_key(&search_key, type, remote);
+ }
+
+ /* Start lock the manager. */
+ pj_mutex_lock(mgr->mutex);
+
+ /* Lookup the transport in the hash table. */
+ tr = pj_hash_get(mgr->transport_table, &search_key, sizeof(transport_key));
+
+ if (tr) {
+ /* Transport found. If the transport is still busy (i.e. connecting
+ * is in progress), then just register the callback. Otherwise
+ * report via the callback if callback is specified.
+ */
+ pj_mutex_unlock(mgr->mutex);
+ pj_mutex_lock(tr->tr_mutex);
+
+ if (tr->flag & PJSIP_TRANSPORT_IOQUEUE_BUSY) {
+ /* Transport is busy. Just register the callback. */
+ pj_list_insert_before(&tr->cb_list, cb_rec);
+
+ } else {
+ /* Transport is ready. Call callback now.
+ */
+ (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
+ }
+ pj_mutex_unlock(tr->tr_mutex);
+
+ return;
+ }
+
+
+ /* Transport not found. Create new one. */
+ pj_memset(&local, 0, sizeof(local));
+ local.sin_family = PJ_AF_INET;
+ status = create_socket(type, &local, &sock_hnd);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+ status = create_transport(mgr, type, sock_hnd, &local, NULL, &tr);
+ if (status != PJ_SUCCESS) {
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+
+#if PJ_HAS_TCP
+ if (type == PJSIP_TRANSPORT_TCP) {
+ pj_memcpy(&tr->remote_addr, remote, sizeof(pj_sockaddr_in));
status = pj_ioqueue_connect(tr->key, &tr->remote_addr,
- sizeof(pj_sockaddr_in)); - pj_assert(status != 0); + sizeof(pj_sockaddr_in));
+ pj_assert(status != 0);
if (status != PJ_EPENDING) {
- PJ_TODO(HANDLE_IMMEDIATE_CONNECT); - destroy_transport(mgr, tr); - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, status); - return; - } - } else -#endif - if (type == PJSIP_TRANSPORT_UDP) { - pj_ssize_t size; - - do { + PJ_TODO(HANDLE_IMMEDIATE_CONNECT);
+ destroy_transport(mgr, tr);
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+ } else
+#endif
+ if (type == PJSIP_TRANSPORT_UDP) {
+ pj_ssize_t size;
+
+ do {
tr->rdata->addr_len = sizeof(tr->rdata->addr);
- size = PJSIP_MAX_PKT_LEN; - status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key, - tr->rdata->packet, &size, 0, - &tr->rdata->addr, + size = PJSIP_MAX_PKT_LEN;
+ status = pj_ioqueue_recvfrom( tr->key, &tr->rdata->op_key,
+ tr->rdata->packet, &size, 0,
+ &tr->rdata->addr,
&tr->rdata->addr_len);
if (status == PJ_SUCCESS)
- ; - else if (status != PJ_EPENDING) { - destroy_transport(mgr, tr); - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, status); - return; - } - - /* Bug here. - * If data is immediately available, although not likely, it will - * be dropped because we don't expect to have data right after - * the socket is created, do we ?! - */ - PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA); - - } while (status == PJ_SUCCESS); - - //Bug: cb will never be called! - // Must force status to PJ_SUCCESS; - //status = PJ_IOQUEUE_PENDING; - - status = PJ_SUCCESS; - - } else { - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(NULL, cb_rec->token, PJSIP_EUNSUPTRANSPORT); - return; - } - - pj_assert(status==PJ_EPENDING || status==PJ_SUCCESS); - pj_mutex_lock(tr->tr_mutex); - hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); - pj_memcpy(hash_key, &search_key, sizeof(transport_key)); + ;
+ else if (status != PJ_EPENDING) {
+ destroy_transport(mgr, tr);
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, status);
+ return;
+ }
+
+ /* Bug here.
+ * If data is immediately available, although not likely, it will
+ * be dropped because we don't expect to have data right after
+ * the socket is created, do we ?!
+ */
+ PJ_TODO(FIXED_BUG_ON_IMMEDIATE_TRANSPORT_DATA);
+
+ } while (status == PJ_SUCCESS);
+
+ //Bug: cb will never be called!
+ // Must force status to PJ_SUCCESS;
+ //status = PJ_IOQUEUE_PENDING;
+
+ status = PJ_SUCCESS;
+
+ } else {
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(NULL, cb_rec->token, PJSIP_EUNSUPTRANSPORT);
+ return;
+ }
+
+ pj_assert(status==PJ_EPENDING || status==PJ_SUCCESS);
+ pj_mutex_lock(tr->tr_mutex);
+ hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+ pj_memcpy(hash_key, &search_key, sizeof(transport_key));
pj_hash_set(tr->pool, mgr->transport_table,
- hash_key, sizeof(transport_key), tr); - if (status == PJ_SUCCESS) { - pj_mutex_unlock(tr->tr_mutex); - pj_mutex_unlock(mgr->mutex); - (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS); - } else { - pj_list_insert_before(&tr->cb_list, cb_rec); - pj_mutex_unlock(tr->tr_mutex); - pj_mutex_unlock(mgr->mutex); - } - -} - -#if PJ_HAS_TCP -/* - * Handle completion of asynchronous accept() operation. - * This function is called by handle_events() function. - */ -static void handle_new_connection( pjsip_transport_mgr *mgr, - pjsip_transport_t *listener, - pj_status_t status ) -{ - pjsip_transport_t *tr; + hash_key, sizeof(transport_key), tr);
+ if (status == PJ_SUCCESS) {
+ pj_mutex_unlock(tr->tr_mutex);
+ pj_mutex_unlock(mgr->mutex);
+ (*cb_rec->cb)(tr, cb_rec->token, PJ_SUCCESS);
+ } else {
+ pj_list_insert_before(&tr->cb_list, cb_rec);
+ pj_mutex_unlock(tr->tr_mutex);
+ pj_mutex_unlock(mgr->mutex);
+ }
+
+}
+
+#if PJ_HAS_TCP
+/*
+ * Handle completion of asynchronous accept() operation.
+ * This function is called by handle_events() function.
+ */
+static void handle_new_connection( pjsip_transport_mgr *mgr,
+ pjsip_transport_t *listener,
+ pj_status_t status )
+{
+ pjsip_transport_t *tr;
transport_key *hash_key;
pj_ssize_t size;
- - pj_assert (listener->type == PJSIP_TRANSPORT_TCP); - - if (status != PJ_SUCCESS) { +
+ pj_assert (listener->type == PJSIP_TRANSPORT_TCP);
+
+ if (status != PJ_SUCCESS) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in accept() completion"));
- goto on_return; - } - - PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d", - pj_inet_ntoa(listener->accept_data.remote.sin_addr), - pj_sockaddr_in_get_port(&listener->accept_data.remote))); - - status = create_transport(mgr, listener->type, - listener->accept_data.sock, - &listener->accept_data.local, - NULL, &tr); - if (status != PJ_SUCCESS) { + goto on_return;
+ }
+
+ PJ_LOG(4,(listener->obj_name, "incoming tcp connection from %s:%d",
+ pj_inet_ntoa(listener->accept_data.remote.sin_addr),
+ pj_sockaddr_in_get_port(&listener->accept_data.remote)));
+
+ status = create_transport(mgr, listener->type,
+ listener->accept_data.sock,
+ &listener->accept_data.local,
+ NULL, &tr);
+ if (status != PJ_SUCCESS) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in creating new incoming TCP"));
- goto on_return; - } - - /* - tr->rdata->addr_len = sizeof(tr->rdata->addr); - status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key, - tr->rdata->packet, PJSIP_MAX_PKT_LEN, - &tr->rdata->addr, - &tr->rdata->addr_len); - */ - tr->rdata->addr = listener->accept_data.remote; - tr->rdata->addr_len = listener->accept_data.addrlen; -
- size = PJSIP_MAX_PKT_LEN; + goto on_return;
+ }
+
+ /*
+ tr->rdata->addr_len = sizeof(tr->rdata->addr);
+ status = pj_ioqueue_recvfrom( mgr->ioqueue, tr->key,
+ tr->rdata->packet, PJSIP_MAX_PKT_LEN,
+ &tr->rdata->addr,
+ &tr->rdata->addr_len);
+ */
+ tr->rdata->addr = listener->accept_data.remote;
+ tr->rdata->addr_len = listener->accept_data.addrlen;
+
+ size = PJSIP_MAX_PKT_LEN;
status = pj_ioqueue_recv(tr->key, &tr->rdata->op_key,
- tr->rdata->packet, &size, 0); - if (status != PJ_EPENDING) { + tr->rdata->packet, &size, 0);
+ if (status != PJ_EPENDING) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in receiving data"));
- PJ_TODO(IMMEDIATE_DATA); - destroy_transport(mgr, tr); - goto on_return; - } - + PJ_TODO(IMMEDIATE_DATA);
+ destroy_transport(mgr, tr);
+ goto on_return;
+ }
+
pj_memcpy(&tr->remote_addr, &listener->accept_data.remote,
- listener->accept_data.addrlen); - hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key)); - init_key_from_transport(hash_key, tr); - - pj_mutex_lock(mgr->mutex); + listener->accept_data.addrlen);
+ hash_key = pj_pool_alloc(tr->pool, sizeof(transport_key));
+ init_key_from_transport(hash_key, tr);
+
+ pj_mutex_lock(mgr->mutex);
pj_hash_set(tr->pool, mgr->transport_table, hash_key,
- sizeof(transport_key), tr); - pj_mutex_unlock(mgr->mutex); - -on_return: - /* Re-initiate asynchronous accept() */ - listener->accept_data.addrlen = sizeof(listener->accept_data.local); - status = pj_ioqueue_accept(listener->key, &listener->accept_op, - &listener->accept_data.sock, - &listener->accept_data.local, - &listener->accept_data.remote, - &listener->accept_data.addrlen); - if (status != PJ_EPENDING) { + sizeof(transport_key), tr);
+ pj_mutex_unlock(mgr->mutex);
+
+on_return:
+ /* Re-initiate asynchronous accept() */
+ listener->accept_data.addrlen = sizeof(listener->accept_data.local);
+ status = pj_ioqueue_accept(listener->key, &listener->accept_op,
+ &listener->accept_data.sock,
+ &listener->accept_data.local,
+ &listener->accept_data.remote,
+ &listener->accept_data.addrlen);
+ if (status != PJ_EPENDING) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, listener->obj_name, status,
"Error in receiving data"));
- PJ_TODO(IMMEDIATE_ACCEPT); - return; - } -} - -/* - * Handle completion of asynchronous connect() function. - * This function is called by the handle_events() function. - */ -static void handle_connect_completion( pjsip_transport_mgr *mgr, - pjsip_transport_t *tr, - pj_status_t status ) -{ - struct transport_callback new_list; - struct transport_callback *cb_rec; + PJ_TODO(IMMEDIATE_ACCEPT);
+ return;
+ }
+}
+
+/*
+ * Handle completion of asynchronous connect() function.
+ * This function is called by the handle_events() function.
+ */
+static void handle_connect_completion( pjsip_transport_mgr *mgr,
+ pjsip_transport_t *tr,
+ pj_status_t status )
+{
+ struct transport_callback new_list;
+ struct transport_callback *cb_rec;
pj_ssize_t recv_size;
- - PJ_UNUSED_ARG(mgr); - - /* On connect completion, we must call all registered callbacks in - * the transport. - */ - - /* Initialize new list. */ - pj_list_init(&new_list); - - /* Hold transport's mutex. We don't want other thread to register a - * callback while we're dealing with it. - */ - pj_mutex_lock(tr->tr_mutex); - - /* Copy callback list to new list so that we can call the callbacks - * without holding the mutex. - */ - pj_list_merge_last(&new_list, &tr->cb_list); - - /* Clear transport's busy flag. */ - tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY; - - /* If success, update local address. - * Local address is only available after connect() has returned. - */ - if (status == PJ_SUCCESS) { - int addrlen = sizeof(tr->local_addr); +
+ PJ_UNUSED_ARG(mgr);
+
+ /* On connect completion, we must call all registered callbacks in
+ * the transport.
+ */
+
+ /* Initialize new list. */
+ pj_list_init(&new_list);
+
+ /* Hold transport's mutex. We don't want other thread to register a
+ * callback while we're dealing with it.
+ */
+ pj_mutex_lock(tr->tr_mutex);
+
+ /* Copy callback list to new list so that we can call the callbacks
+ * without holding the mutex.
+ */
+ pj_list_merge_last(&new_list, &tr->cb_list);
+
+ /* Clear transport's busy flag. */
+ tr->flag &= ~PJSIP_TRANSPORT_IOQUEUE_BUSY;
+
+ /* If success, update local address.
+ * Local address is only available after connect() has returned.
+ */
+ if (status == PJ_SUCCESS) {
+ int addrlen = sizeof(tr->local_addr);
status = pj_sock_getsockname(tr->sock,
(pj_sockaddr_t*)&tr->local_addr,
- &addrlen); - if (status == PJ_SUCCESS) { - pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name)); - } - } - - /* Unlock mutex. */ - pj_mutex_unlock(tr->tr_mutex); - - /* Call all registered callbacks. */ - cb_rec = new_list.next; - while (cb_rec != &new_list) { - struct transport_callback *next; - next = cb_rec->next; - (*cb_rec->cb)(tr, cb_rec->token, status); - cb_rec = next; - } - - /* Success? */ - if (status != PJ_SUCCESS) { + &addrlen);
+ if (status == PJ_SUCCESS) {
+ pj_memcpy(&tr->addr_name, &tr->local_addr, sizeof(tr->addr_name));
+ }
+ }
+
+ /* Unlock mutex. */
+ pj_mutex_unlock(tr->tr_mutex);
+
+ /* Call all registered callbacks. */
+ cb_rec = new_list.next;
+ while (cb_rec != &new_list) {
+ struct transport_callback *next;
+ next = cb_rec->next;
+ (*cb_rec->cb)(tr, cb_rec->token, status);
+ cb_rec = next;
+ }
+
+ /* Success? */
+ if (status != PJ_SUCCESS) {
destroy_transport(mgr, tr);
- PJ_TODO(WTF); - return; - } - + PJ_TODO(WTF);
+ return;
+ }
+
/* Initiate read operation to socket. */
- recv_size = PJSIP_MAX_PKT_LEN; + recv_size = PJSIP_MAX_PKT_LEN;
status = pj_ioqueue_recv( tr->key, &tr->rdata->op_key, tr->rdata->packet,
- &recv_size, 0); - if (status != PJ_EPENDING) { + &recv_size, 0);
+ if (status != PJ_EPENDING) {
destroy_transport(mgr, tr);
- PJ_TODO(IMMEDIATE_DATA); - return; - } -} -#endif /* PJ_HAS_TCP */ - -/* - * Handle incoming data. - * This function is called when the transport manager receives 'notification' - * from the I/O Queue that the receive operation has completed. - * This function will then attempt to parse the message, and hands over the - * message to the endpoint. - */ -static void handle_received_data( pjsip_transport_mgr *mgr, - pjsip_transport_t *tr, - pj_ssize_t size ) -{ - pjsip_msg *msg; - pjsip_rx_data *rdata = tr->rdata; - pj_pool_t *rdata_pool; - pjsip_hdr *hdr; - pj_str_t s; - char *src_addr; - int src_port; - pj_size_t msg_fragment_size = 0; - - /* Check size. */ - if (size < 1) { - if (tr->type != PJSIP_TRANSPORT_UDP) { - /* zero bytes indicates transport has been closed for TCP. - * But alas, we can't destroy it now since transactions may still - * have reference to it. In that case, just do nothing, the - * transaction will receive error when it tries to send anything. - * But alas!! UAC transactions wont send anything!!. - * So this is a bug! - */ - if (pj_atomic_get(tr->ref_cnt)==0) { - PJ_LOG(4,(tr->obj_name, "connection closed")); - destroy_transport(mgr, tr); - } else { - PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED); - //PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED); - } - return; - } else { - /* On Windows machines, UDP recv() will return zero upon receiving - * ICMP port unreachable message. - */ - PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)")); - goto on_return; - } - } - - /* Save received time. */ - pj_gettimeofday(&rdata->timestamp); - - /* Update length. */ - rdata->len += size; - - /* Null terminate packet, this is the requirement of the parser. */ - rdata->packet[rdata->len] = '\0'; - - /* Get source address and port for logging purpose. */ - src_addr = pj_inet_ntoa(rdata->addr.sin_addr); - src_port = pj_sockaddr_in_get_port(&rdata->addr); - - /* Print the whole data to the log. */ - PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n" - "----------- begin msg ------------\n" - "%s" - "------------ end msg -------------", - rdata->len, src_addr, src_port, rdata->packet)); - - - /* Process all message fragments. */ - while (rdata->len > 0) { - - msg_fragment_size = rdata->len; -#if PJ_HAS_TCP - /* For TCP transport, check if the whole message has been received. */ - if (tr->type != PJSIP_TRANSPORT_UDP) { - pj_status_t msg_status; + PJ_TODO(IMMEDIATE_DATA);
+ return;
+ }
+}
+#endif /* PJ_HAS_TCP */
+
+/*
+ * Handle incoming data.
+ * This function is called when the transport manager receives 'notification'
+ * from the I/O Queue that the receive operation has completed.
+ * This function will then attempt to parse the message, and hands over the
+ * message to the endpoint.
+ */
+static void handle_received_data( pjsip_transport_mgr *mgr,
+ pjsip_transport_t *tr,
+ pj_ssize_t size )
+{
+ pjsip_msg *msg;
+ pjsip_rx_data *rdata = tr->rdata;
+ pj_pool_t *rdata_pool;
+ pjsip_hdr *hdr;
+ pj_str_t s;
+ char *src_addr;
+ int src_port;
+ pj_size_t msg_fragment_size = 0;
+
+ /* Check size. */
+ if (size < 1) {
+ if (tr->type != PJSIP_TRANSPORT_UDP) {
+ /* zero bytes indicates transport has been closed for TCP.
+ * But alas, we can't destroy it now since transactions may still
+ * have reference to it. In that case, just do nothing, the
+ * transaction will receive error when it tries to send anything.
+ * But alas!! UAC transactions wont send anything!!.
+ * So this is a bug!
+ */
+ if (pj_atomic_get(tr->ref_cnt)==0) {
+ PJ_LOG(4,(tr->obj_name, "connection closed"));
+ destroy_transport(mgr, tr);
+ } else {
+ PJ_TODO(HANDLE_TCP_TRANSPORT_CLOSED);
+ //PJ_TODO(SIGNAL_TRANSACTIONS_ON_TRANSPORT_CLOSED);
+ }
+ return;
+ } else {
+ /* On Windows machines, UDP recv() will return zero upon receiving
+ * ICMP port unreachable message.
+ */
+ PJ_LOG(4,(tr->obj_name, "Ignored zero length UDP packet (port unreachable?)"));
+ goto on_return;
+ }
+ }
+
+ /* Save received time. */
+ pj_gettimeofday(&rdata->timestamp);
+
+ /* Update length. */
+ rdata->len += size;
+
+ /* Null terminate packet, this is the requirement of the parser. */
+ rdata->packet[rdata->len] = '\0';
+
+ /* Get source address and port for logging purpose. */
+ src_addr = pj_inet_ntoa(rdata->addr.sin_addr);
+ src_port = pj_sockaddr_in_get_port(&rdata->addr);
+
+ /* Print the whole data to the log. */
+ PJ_LOG(4,(tr->obj_name, "%d bytes recvfrom %s:%d:\n"
+ "----------- begin msg ------------\n"
+ "%s"
+ "------------ end msg -------------",
+ rdata->len, src_addr, src_port, rdata->packet));
+
+
+ /* Process all message fragments. */
+ while (rdata->len > 0) {
+
+ msg_fragment_size = rdata->len;
+#if PJ_HAS_TCP
+ /* For TCP transport, check if the whole message has been received. */
+ if (tr->type != PJSIP_TRANSPORT_UDP) {
+ pj_status_t msg_status;
msg_status = pjsip_find_msg(rdata->packet, rdata->len, PJ_FALSE,
- &msg_fragment_size); - if (msg_status != PJ_SUCCESS) { + &msg_fragment_size);
+ if (msg_status != PJ_SUCCESS) {
if (rdata->len == PJSIP_MAX_PKT_LEN) {
PJSIP_ENDPT_LOG_ERROR((mgr->endpt, tr->obj_name,
PJSIP_EOVERFLOW,
- "Buffer discarded for %s:%d", - src_addr, src_port)); - goto on_return; - } else { - goto tcp_read_packet; - } - } - } -#endif - - /* Clear parser error report */ - pj_list_init(&rdata->parse_err); - - /* Parse the message. */ - PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size, - src_addr, src_port)); - - msg = pjsip_parse_rdata( rdata->packet, msg_fragment_size, rdata); - if (msg == NULL) { - PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size, - src_addr, src_port)); - goto finish_process_fragment; - } -
- /* Perform basic header checking. */ + "Buffer discarded for %s:%d",
+ src_addr, src_port));
+ goto on_return;
+ } else {
+ goto tcp_read_packet;
+ }
+ }
+ }
+#endif
+
+ /* Clear parser error report */
+ pj_list_init(&rdata->parse_err);
+
+ /* Parse the message. */
+ PJ_LOG(5,(tr->obj_name, "Parsing %d bytes from %s:%d", msg_fragment_size,
+ src_addr, src_port));
+
+ msg = pjsip_parse_rdata( rdata->packet, msg_fragment_size, rdata);
+ if (msg == NULL) {
+ PJ_LOG(3,(tr->obj_name, "Bad message (%d bytes from %s:%d)", msg_fragment_size,
+ src_addr, src_port));
+ goto finish_process_fragment;
+ }
+
+ /* Perform basic header checking. */
if (rdata->call_id.ptr == NULL || rdata->from == NULL ||
- rdata->to == NULL || rdata->via == NULL || rdata->cseq == NULL) - { - PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header", - src_addr, src_port)); - goto finish_process_fragment; - } - - /* If message is received from address that's different from the sent-by, - * MUST add received parameter to the via. - * In our case, we add Via receive param for EVERY received message, - * because it saves us from resolving the host HERE in case sent-by is in - * FQDN format. And it doesn't hurt either. - */ - s = pj_str(src_addr); - pj_strdup(rdata->pool, &rdata->via->recvd_param, &s); - - /* RFC 3581: - * If message contains "rport" param, put the received port there. - */ - if (rdata->via->rport_param == 0) { - rdata->via->rport_param = pj_sockaddr_in_get_port(&rdata->addr); - } - - /* Drop response message if it has more than one Via. - */ - if (msg->type == PJSIP_RESPONSE_MSG) { - hdr = (pjsip_hdr*)rdata->via->next; - if (hdr != &rdata->msg->hdr) { - hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr); - if (hdr) { - PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: " - "multiple Via in response message", - src_addr, src_port)); - goto finish_process_fragment; - } - } - } - - /* Call the transport manager's upstream message callback. - */ - (*mgr->message_callback)(mgr->endpt, rdata); - -finish_process_fragment: - rdata->len -= msg_fragment_size; - if (rdata->len > 0) { - pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len); - PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len)); - } - - } /* while (rdata->len > 0) */ - -on_return: - /* Reset the pool and rdata */ - rdata_pool = rdata->pool; - pj_pool_reset(rdata_pool); - rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) ); - rdata->len = 0; - rdata->transport = tr; - rdata->pool = rdata_pool; - tr->rdata = rdata; - - /* Read the next packet. */ - rdata->addr_len = sizeof(rdata->addr); + rdata->to == NULL || rdata->via == NULL || rdata->cseq == NULL)
+ {
+ PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: missing some header",
+ src_addr, src_port));
+ goto finish_process_fragment;
+ }
+
+ /* If message is received from address that's different from the sent-by,
+ * MUST add received parameter to the via.
+ * In our case, we add Via receive param for EVERY received message,
+ * because it saves us from resolving the host HERE in case sent-by is in
+ * FQDN format. And it doesn't hurt either.
+ */
+ s = pj_str(src_addr);
+ pj_strdup(rdata->pool, &rdata->via->recvd_param, &s);
+
+ /* RFC 3581:
+ * If message contains "rport" param, put the received port there.
+ */
+ if (rdata->via->rport_param == 0) {
+ rdata->via->rport_param = pj_sockaddr_in_get_port(&rdata->addr);
+ }
+
+ /* Drop response message if it has more than one Via.
+ */
+ if (msg->type == PJSIP_RESPONSE_MSG) {
+ hdr = (pjsip_hdr*)rdata->via->next;
+ if (hdr != &rdata->msg->hdr) {
+ hdr = pjsip_msg_find_hdr(msg, PJSIP_H_VIA, hdr);
+ if (hdr) {
+ PJ_LOG(3,(tr->obj_name, "Bad message from %s:%d: "
+ "multiple Via in response message",
+ src_addr, src_port));
+ goto finish_process_fragment;
+ }
+ }
+ }
+
+ /* Call the transport manager's upstream message callback.
+ */
+ (*mgr->message_callback)(mgr->endpt, rdata);
+
+finish_process_fragment:
+ rdata->len -= msg_fragment_size;
+ if (rdata->len > 0) {
+ pj_memmove(rdata->packet, rdata->packet+msg_fragment_size, rdata->len);
+ PJ_LOG(4,(tr->obj_name, "Processing next fragment, size=%d bytes", rdata->len));
+ }
+
+ } /* while (rdata->len > 0) */
+
+on_return:
+ /* Reset the pool and rdata */
+ rdata_pool = rdata->pool;
+ pj_pool_reset(rdata_pool);
+ rdata = pj_pool_alloc( rdata_pool, sizeof(*rdata) );
+ rdata->len = 0;
+ rdata->transport = tr;
+ rdata->pool = rdata_pool;
+ tr->rdata = rdata;
+
+ /* Read the next packet. */
+ rdata->addr_len = sizeof(rdata->addr);
if (tr->type == PJSIP_TRANSPORT_UDP) {
- pj_ssize_t size = PJSIP_MAX_PKT_LEN; - pj_ioqueue_recvfrom(tr->key, &tr->rdata->op_key, - tr->rdata->packet, &size, 0, + pj_ssize_t size = PJSIP_MAX_PKT_LEN;
+ pj_ioqueue_recvfrom(tr->key, &tr->rdata->op_key,
+ tr->rdata->packet, &size, 0,
&rdata->addr, &rdata->addr_len);
- PJ_TODO(HANDLE_IMMEDIATE_DATA); - } - -#if PJ_HAS_TCP - /* The next 'if' should have been 'else if', but we need to put the - label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning. - */ -tcp_read_packet: + PJ_TODO(HANDLE_IMMEDIATE_DATA);
+ }
+
+#if PJ_HAS_TCP
+ /* The next 'if' should have been 'else if', but we need to put the
+ label inside the '#if PJ_HAS_TCP' block to avoid 'unreferenced label' warning.
+ */
+tcp_read_packet:
if (tr->type == PJSIP_TRANSPORT_TCP) {
pj_ssize_t size = PJSIP_MAX_PKT_LEN - tr->rdata->len;
- pj_ioqueue_recv( tr->key, &tr->rdata->op_key, - tr->rdata->packet + tr->rdata->len, + pj_ioqueue_recv( tr->key, &tr->rdata->op_key,
+ tr->rdata->packet + tr->rdata->len,
&size, 0);
- PJ_TODO(HANDLE_IMMEDIATE_DATA_1); - } -#endif -} - -static void transport_mgr_on_idle( pjsip_transport_mgr *mgr ) -{ - pj_time_val now; - pj_hash_iterator_t itr_val; - pj_hash_iterator_t *itr; - - - /* Get time for comparing transport's close time. */ - pj_gettimeofday(&now); - if (now.sec < mgr->next_idle_check.sec) { - return; - } - - /* Acquire transport manager's lock. */ - pj_mutex_lock(mgr->mutex); - - /* Update next idle check. */ - mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL; - - /* Iterate all transports, and close transports that are not used for - some periods. - */ - itr = pjsip_transport_first(mgr, &itr_val); - while (itr != NULL) { - pj_hash_iterator_t *next; - pjsip_transport_t *transport; - - transport = pjsip_transport_this(mgr, itr); - - next = pjsip_transport_next(mgr, itr); - - if (pj_atomic_get(transport->ref_cnt)==0 && - PJ_TIME_VAL_LTE(transport->close_time, now)) - { - destroy_transport(mgr, transport); - } - - itr = next; - } - - /* Release transport manager's lock. */ - pj_mutex_unlock(mgr->mutex); -} - + PJ_TODO(HANDLE_IMMEDIATE_DATA_1);
+ }
+#endif
+}
+
+static void transport_mgr_on_idle( pjsip_transport_mgr *mgr )
+{
+ pj_time_val now;
+ pj_hash_iterator_t itr_val;
+ pj_hash_iterator_t *itr;
+
+
+ /* Get time for comparing transport's close time. */
+ pj_gettimeofday(&now);
+ if (now.sec < mgr->next_idle_check.sec) {
+ return;
+ }
+
+ /* Acquire transport manager's lock. */
+ pj_mutex_lock(mgr->mutex);
+
+ /* Update next idle check. */
+ mgr->next_idle_check.sec += MGR_IDLE_CHECK_INTERVAL;
+
+ /* Iterate all transports, and close transports that are not used for
+ some periods.
+ */
+ itr = pjsip_transport_first(mgr, &itr_val);
+ while (itr != NULL) {
+ pj_hash_iterator_t *next;
+ pjsip_transport_t *transport;
+
+ transport = pjsip_transport_this(mgr, itr);
+
+ next = pjsip_transport_next(mgr, itr);
+
+ if (pj_atomic_get(transport->ref_cnt)==0 &&
+ PJ_TIME_VAL_LTE(transport->close_time, now))
+ {
+ destroy_transport(mgr, transport);
+ }
+
+ itr = next;
+ }
+
+ /* Release transport manager's lock. */
+ pj_mutex_unlock(mgr->mutex);
+}
+
static void on_ioqueue_read(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_read) -{ - pjsip_transport_t *t; - t = pj_ioqueue_get_user_data(key); - - handle_received_data( t->mgr, t, bytes_read ); -} - + pj_ssize_t bytes_read)
+{
+ pjsip_transport_t *t;
+ t = pj_ioqueue_get_user_data(key);
+
+ handle_received_data( t->mgr, t, bytes_read );
+}
+
static void on_ioqueue_write(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
- pj_ssize_t bytes_sent) -{ - PJ_UNUSED_ARG(key); - PJ_UNUSED_ARG(bytes_sent); - - /* Completion of write operation. - * Do nothing. - */ -} - + pj_ssize_t bytes_sent)
+{
+ PJ_UNUSED_ARG(key);
+ PJ_UNUSED_ARG(bytes_sent);
+
+ /* Completion of write operation.
+ * Do nothing.
+ */
+}
+
static void on_ioqueue_accept(pj_ioqueue_key_t *key,
pj_ioqueue_op_key_t *op_key,
pj_sock_t newsock,
- int status) -{ -#if PJ_HAS_TCP - pjsip_transport_t *t; - t = pj_ioqueue_get_user_data(key); - - handle_new_connection( t->mgr, t, status ); -#else - PJ_UNUSED_ARG(key); - PJ_UNUSED_ARG(status); -#endif -} - -static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status) -{ -#if PJ_HAS_TCP - pjsip_transport_t *t; - t = pj_ioqueue_get_user_data(key); - - handle_connect_completion( t->mgr, t, status); -#else - PJ_UNUSED_ARG(key); - PJ_UNUSED_ARG(status); -#endif -} - - -/* - * Poll for events. - */ -PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr, - const pj_time_val *req_timeout ) -{ - int event_count; - int break_loop; - int result; - pj_time_val timeout; - - PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()")); - - event_count = 0; - break_loop = 0; - timeout = *req_timeout; - do { - result = pj_ioqueue_poll( mgr->ioqueue, &timeout); - if (result == 1) { - ++event_count; - - /* Break the loop. */ - //if (timeout.msec==0 && timeout.sec==0) { - break_loop = 1; - //} - - } else { - /* On idle, cleanup transport. */ - transport_mgr_on_idle(mgr); - - break_loop = 1; - } - timeout.sec = timeout.msec = 0; - } while (!break_loop); - - return event_count; -} - - -PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *it ) -{ - return pj_hash_first(mgr->transport_table, it); -} - -PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *itr ) -{ - return pj_hash_next(mgr->transport_table, itr); -} - -PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr, - pj_hash_iterator_t *itr ) -{ - return pj_hash_this(mgr->transport_table, itr); -} + int status)
+{
+#if PJ_HAS_TCP
+ pjsip_transport_t *t;
+ t = pj_ioqueue_get_user_data(key);
+
+ handle_new_connection( t->mgr, t, status );
+#else
+ PJ_UNUSED_ARG(key);
+ PJ_UNUSED_ARG(status);
+#endif
+}
+
+static void on_ioqueue_connect(pj_ioqueue_key_t *key, int status)
+{
+#if PJ_HAS_TCP
+ pjsip_transport_t *t;
+ t = pj_ioqueue_get_user_data(key);
+
+ handle_connect_completion( t->mgr, t, status);
+#else
+ PJ_UNUSED_ARG(key);
+ PJ_UNUSED_ARG(status);
+#endif
+}
+
+
+/*
+ * Poll for events.
+ */
+PJ_DEF(int) pjsip_transport_mgr_handle_events( pjsip_transport_mgr *mgr,
+ const pj_time_val *req_timeout )
+{
+ int event_count;
+ int break_loop;
+ int result;
+ pj_time_val timeout;
+
+ PJ_LOG(5, (LOG_TRANSPORT_MGR, "pjsip_transport_mgr_handle_events()"));
+
+ event_count = 0;
+ break_loop = 0;
+ timeout = *req_timeout;
+ do {
+ result = pj_ioqueue_poll( mgr->ioqueue, &timeout);
+ if (result == 1) {
+ ++event_count;
+
+ /* Break the loop. */
+ //if (timeout.msec==0 && timeout.sec==0) {
+ break_loop = 1;
+ //}
+
+ } else {
+ /* On idle, cleanup transport. */
+ transport_mgr_on_idle(mgr);
+
+ break_loop = 1;
+ }
+ timeout.sec = timeout.msec = 0;
+ } while (!break_loop);
+
+ return event_count;
+}
+
+
+PJ_DEF(pj_hash_iterator_t*) pjsip_transport_first( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *it )
+{
+ return pj_hash_first(mgr->transport_table, it);
+}
+
+PJ_DEF(pj_hash_iterator_t*) pjsip_transport_next( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *itr )
+{
+ return pj_hash_next(mgr->transport_table, itr);
+}
+
+PJ_DEF(pjsip_transport_t*) pjsip_transport_this( pjsip_transport_mgr *mgr,
+ pj_hash_iterator_t *itr )
+{
+ return pj_hash_this(mgr->transport_table, itr);
+}
diff --git a/pjsip/src/pjsip/sip_uri.c b/pjsip/src/pjsip/sip_uri.c index 8085f7fc..e6db8705 100644 --- a/pjsip/src/pjsip/sip_uri.c +++ b/pjsip/src/pjsip/sip_uri.c @@ -1,398 +1,420 @@ -/* $Id$ - */ -#include <pjsip/sip_uri.h> -#include <pjsip/sip_msg.h> -#include <pjsip/print_util.h> -#include <pj/string.h> -#include <pj/pool.h> +/* $Id$
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_uri.h>
+#include <pjsip/sip_msg.h>
+#include <pjsip/print_util.h>
+#include <pj/string.h>
+#include <pj/pool.h>
#include <pj/assert.h>
- -#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr) - -static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* ); -static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* ); -static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * ); -static void *pjsip_get_uri( pjsip_uri *uri ); -static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ); - -static pj_str_t sip_str = { "sip", 3 }; -static pj_str_t sips_str = { "sips", 4 }; - -#ifdef __GNUC__ -# define HAPPY_FLAG (void*) -#else -# define HAPPY_FLAG -#endif - -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 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_url *url, - char *buf, pj_size_t size); -static int pjsip_url_compare( pjsip_uri_context_e context, - const pjsip_url *url1, const pjsip_url *url2); -static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs); - -static pjsip_uri_vptr sip_url_vptr = -{ - HAPPY_FLAG &pjsip_url_get_scheme, - HAPPY_FLAG &pjsip_get_uri, - HAPPY_FLAG &pjsip_url_print, - HAPPY_FLAG &pjsip_url_compare, - HAPPY_FLAG &pjsip_url_clone -}; - -static pjsip_uri_vptr sips_url_vptr = -{ - HAPPY_FLAG &pjsips_url_get_scheme, - HAPPY_FLAG &pjsip_get_uri, - HAPPY_FLAG &pjsip_url_print, - HAPPY_FLAG &pjsip_url_compare, - HAPPY_FLAG &pjsip_url_clone -}; - -static pjsip_uri_vptr name_addr_vptr = -{ - HAPPY_FLAG &pjsip_name_addr_get_scheme, - HAPPY_FLAG &pjsip_name_addr_get_uri, - HAPPY_FLAG &pjsip_name_addr_print, - HAPPY_FLAG &pjsip_name_addr_compare, - HAPPY_FLAG &pjsip_name_addr_clone -}; - -static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url) -{ - PJ_UNUSED_ARG(url); - return &sip_str; -} - -static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url) -{ - PJ_UNUSED_ARG(url); - return &sips_str; -} - -static void *pjsip_get_uri( pjsip_uri *uri ) -{ - return uri; -} - -static void *pjsip_name_addr_get_uri( pjsip_name_addr *name ) -{ - return name->uri; -} - -PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure) -{ - pj_memset(url, 0, sizeof(*url)); - url->ttl_param = -1; - url->vptr = secure ? &sips_url_vptr : &sip_url_vptr; -} - -PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure ) -{ - pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); - pjsip_url_init(url, secure); - return url; -} - -static int pjsip_url_print( pjsip_uri_context_e context, - const pjsip_url *url, - char *buf, pj_size_t size) -{ - int printed; - pj_size_t size_required; - char *startbuf = buf; - const pj_str_t *scheme; - *buf = '\0'; - - /* Check the buffer length. */ - size_required = 6 + url->host.slen + 10 + - url->user.slen + url->passwd.slen + 2 + - url->user_param.slen + 6 + - url->method_param.slen + 8 + - url->transport_param.slen + 11 + - 9 + 5 + - url->maddr_param.slen + 7 + - 3 + - url->other_param.slen + - url->header_param.slen; - if (size < size_required) { - return -1; - } - - /* Print scheme ("sip:" or "sips:") */ - scheme = pjsip_uri_get_scheme(url); - copy_advance_no_check(buf, *scheme); - *buf++ = ':'; - - /* Print "user:password@", if any. */ - if (url->user.slen) { - copy_advance_no_check(buf, url->user); - if (url->passwd.slen) { - *buf++ = ':'; - copy_advance_no_check(buf, url->passwd); - } - - *buf++ = '@'; - } - - /* Print host. */ - pj_assert(url->host.slen != 0); - copy_advance_no_check(buf, url->host); - - /* Only print port if it is explicitly specified. - * Port is not allowed in To and From header. - */ - /* Unfortunately some UA requires us to send back the port - * number exactly as it was sent. We don't remember whether an - * UA has sent us port, so we'll just send the port indiscrimately - */ - PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER) - if (url->port /*&& context != PJSIP_URI_IN_FROMTO_HDR*/) { - *buf++ = ':'; - printed = pj_utoa(url->port, buf); - buf += printed; - } - - /* User param is allowed in all contexes */ - copy_advance_pair_no_check(buf, ";user=", 6, url->user_param); - - /* Method param is only allowed in external/other context. */ - if (context == PJSIP_URI_IN_OTHER) { - copy_advance_pair_no_check(buf, ";method=", 8, url->method_param); - } - - /* Transport is not allowed in From/To header. */ - if (context != PJSIP_URI_IN_FROMTO_HDR) { - copy_advance_pair_no_check(buf, ";transport=", 11, url->transport_param); - } - - /* TTL param is not allowed in From, To, Route, and Record-Route header. */ - if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR && - context != PJSIP_URI_IN_ROUTING_HDR) - { - pj_memcpy(buf, ";ttl=", 5); - printed = pj_utoa(url->ttl_param, buf+5); - buf += printed + 5; - } - - /* maddr param is not allowed in From and To header. */ - if (context != PJSIP_URI_IN_FROMTO_HDR) { - copy_advance_pair_no_check(buf, ";maddr=", 7, url->maddr_param); - } - - /* lr param is not allowed in From, To, and Contact header. */ - if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR && - context != PJSIP_URI_IN_CONTACT_HDR) - { - pj_str_t lr = { ";lr", 3 }; - copy_advance_no_check(buf, lr); - } - - /* Other param. */ - if (url->other_param.slen) { - copy_advance_no_check(buf, url->other_param); - } - - /* Header param. */ - if (url->header_param.slen) { - copy_advance_no_check(buf, url->header_param); - } - - *buf = '\0'; - return buf-startbuf; -} - -static int pjsip_url_compare( pjsip_uri_context_e context, - const pjsip_url *url1, const pjsip_url *url2) -{ - /* The easiest (and probably the most efficient) way to compare two URLs - are to print them, and compare them bytes per bytes. This technique - works quite well with RFC3261, as the RFC (unlike RFC2543) defines that - components specified in one URL does NOT match its default value if - it is not specified in the second URL. For example, parameter "user=ip" - does NOT match if it is omited in second URL. - - HOWEVER, THE SAME CAN NOT BE APPLIED FOR other-param NOR header-param. - For these, each of the parameters must be compared one by one. Parameter - that exists in one URL will match the comparison. But parameter that - exists in both URLs and doesn't match wont match the URL comparison. - - The solution for this is to compare 'standard' URL components with - bytes-to-bytes comparison, and compare other-param and header-param with - more intelligent comparison. - */ - char str_url1[PJSIP_MAX_URL_SIZE]; - char str_url2[PJSIP_MAX_URL_SIZE]; - int len1, len2; - - /* Must compare scheme first, as the second URI may not be SIP URL. */ - if (pj_stricmp(pjsip_uri_get_scheme(url1), pjsip_uri_get_scheme(url2))) - return -1; - - len1 = pjsip_url_print(context, url1, str_url1, sizeof(str_url1)); - if (len1 < 1) { - pj_assert(0); - return -1; - } - len2 = pjsip_url_print(context, url2, str_url2, sizeof(str_url2)); - if (len2 < 1) { - pj_assert(0); - return -1; - } - - if (len1 != len2) { - /* Not equal. */ - return -1; - } - - if (pj_native_strcmp(str_url1, str_url2)) { - /* Not equal */ - return -1; - } - - /* TODO: compare other-param and header-param in more intelligent manner. */ - PJ_TODO(HPARAM_AND_OTHER_PARAM_COMPARISON_IN_URL_COMPARISON) - - if (pj_strcmp(&url1->other_param, &url2->other_param)) { - /* Not equal. */ - return -1; - } - if (pj_strcmp(&url1->header_param, &url2->header_param)) { - /* Not equal. */ - return -1; - } - - /* Seems to be equal, isn't it. */ - return 0; - -} - - -PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url, - const pjsip_url *rhs) -{ - pj_strdup( pool, &url->user, &rhs->user); - pj_strdup( pool, &url->passwd, &rhs->passwd); - pj_strdup( pool, &url->host, &rhs->host); - url->port = rhs->port; - pj_strdup( pool, &url->user_param, &rhs->user_param); - pj_strdup( pool, &url->method_param, &rhs->method_param); - pj_strdup( pool, &url->transport_param, &rhs->transport_param); - url->ttl_param = rhs->ttl_param; - pj_strdup( pool, &url->maddr_param, &rhs->maddr_param); - pj_strdup( pool, &url->other_param, &rhs->other_param); - pj_strdup( pool, &url->header_param, &rhs->header_param); - url->lr_param = rhs->lr_param; -} - -static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs) -{ - pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url)); - if (!url) - return NULL; - - pjsip_url_init(url, IS_SIPS(rhs)); - pjsip_url_assign(pool, url, rhs); - return url; -} - -static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name) -{ - pj_assert(name->uri != NULL); - return pjsip_uri_get_scheme(name->uri); -} - -PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name) -{ - name->vptr = &name_addr_vptr; - name->uri = NULL; - name->display.slen = 0; -} - -PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool) -{ - pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); - pjsip_name_addr_init(name_addr); - 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) -{ - int printed; - char *startbuf = buf; - char *endbuf = buf + size; - - pj_assert(name->uri != NULL); - - if (context != PJSIP_URI_IN_REQ_URI) { - copy_advance(buf, name->display); - if (name->display.slen) { - *buf++ = ' '; - } - *buf++ = '<'; - } - - printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf)); - if (printed < 1) - return -1; - buf += printed; - - if (context != PJSIP_URI_IN_REQ_URI) { - *buf++ = '>'; - } - - *buf = '\0'; - return buf-startbuf; -} - -PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst, - const pjsip_name_addr *src) -{ - pj_strdup( pool, &dst->display, &src->display); - dst->uri = pjsip_uri_clone(pool, src->uri); -} - -static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool, - const pjsip_name_addr *rhs) -{ - pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr)); - if (!addr) - return NULL; - - pjsip_name_addr_init(addr); - pjsip_name_addr_assign(pool, addr, rhs); - return addr; -} - -static int pjsip_name_addr_compare( pjsip_uri_context_e context, - const pjsip_name_addr *naddr1, - const pjsip_name_addr *naddr2) -{ - int d; - - /* I'm not sure whether display name is included in the comparison. */ - if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) { - return -1; - } - - pj_assert( naddr1->uri != NULL ); - pj_assert( naddr2->uri != NULL ); - - /* Compare name-addr as URL */ - d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri); - if (d) - return d; - - return 0; -} - +
+#define IS_SIPS(url) ((url)->vptr==&sips_url_vptr)
+
+static const pj_str_t *pjsip_url_get_scheme( const pjsip_url* );
+static const pj_str_t *pjsips_url_get_scheme( const pjsip_url* );
+static const pj_str_t *pjsip_name_addr_get_scheme( const pjsip_name_addr * );
+static void *pjsip_get_uri( pjsip_uri *uri );
+static void *pjsip_name_addr_get_uri( pjsip_name_addr *name );
+
+static pj_str_t sip_str = { "sip", 3 };
+static pj_str_t sips_str = { "sips", 4 };
+
+#ifdef __GNUC__
+# define HAPPY_FLAG (void*)
+#else
+# define HAPPY_FLAG
+#endif
+
+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 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_url *url,
+ char *buf, pj_size_t size);
+static int pjsip_url_compare( pjsip_uri_context_e context,
+ const pjsip_url *url1, const pjsip_url *url2);
+static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs);
+
+static pjsip_uri_vptr sip_url_vptr =
+{
+ HAPPY_FLAG &pjsip_url_get_scheme,
+ HAPPY_FLAG &pjsip_get_uri,
+ HAPPY_FLAG &pjsip_url_print,
+ HAPPY_FLAG &pjsip_url_compare,
+ HAPPY_FLAG &pjsip_url_clone
+};
+
+static pjsip_uri_vptr sips_url_vptr =
+{
+ HAPPY_FLAG &pjsips_url_get_scheme,
+ HAPPY_FLAG &pjsip_get_uri,
+ HAPPY_FLAG &pjsip_url_print,
+ HAPPY_FLAG &pjsip_url_compare,
+ HAPPY_FLAG &pjsip_url_clone
+};
+
+static pjsip_uri_vptr name_addr_vptr =
+{
+ HAPPY_FLAG &pjsip_name_addr_get_scheme,
+ HAPPY_FLAG &pjsip_name_addr_get_uri,
+ HAPPY_FLAG &pjsip_name_addr_print,
+ HAPPY_FLAG &pjsip_name_addr_compare,
+ HAPPY_FLAG &pjsip_name_addr_clone
+};
+
+static const pj_str_t *pjsip_url_get_scheme(const pjsip_url *url)
+{
+ PJ_UNUSED_ARG(url);
+ return &sip_str;
+}
+
+static const pj_str_t *pjsips_url_get_scheme(const pjsip_url *url)
+{
+ PJ_UNUSED_ARG(url);
+ return &sips_str;
+}
+
+static void *pjsip_get_uri( pjsip_uri *uri )
+{
+ return uri;
+}
+
+static void *pjsip_name_addr_get_uri( pjsip_name_addr *name )
+{
+ return name->uri;
+}
+
+PJ_DEF(void) pjsip_url_init(pjsip_url *url, int secure)
+{
+ pj_memset(url, 0, sizeof(*url));
+ url->ttl_param = -1;
+ url->vptr = secure ? &sips_url_vptr : &sip_url_vptr;
+}
+
+PJ_DEF(pjsip_url*) pjsip_url_create( pj_pool_t *pool, int secure )
+{
+ pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
+ pjsip_url_init(url, secure);
+ return url;
+}
+
+static int pjsip_url_print( pjsip_uri_context_e context,
+ const pjsip_url *url,
+ char *buf, pj_size_t size)
+{
+ int printed;
+ pj_size_t size_required;
+ char *startbuf = buf;
+ const pj_str_t *scheme;
+ *buf = '\0';
+
+ /* Check the buffer length. */
+ size_required = 6 + url->host.slen + 10 +
+ url->user.slen + url->passwd.slen + 2 +
+ url->user_param.slen + 6 +
+ url->method_param.slen + 8 +
+ url->transport_param.slen + 11 +
+ 9 + 5 +
+ url->maddr_param.slen + 7 +
+ 3 +
+ url->other_param.slen +
+ url->header_param.slen;
+ if (size < size_required) {
+ return -1;
+ }
+
+ /* Print scheme ("sip:" or "sips:") */
+ scheme = pjsip_uri_get_scheme(url);
+ copy_advance_no_check(buf, *scheme);
+ *buf++ = ':';
+
+ /* Print "user:password@", if any. */
+ if (url->user.slen) {
+ copy_advance_no_check(buf, url->user);
+ if (url->passwd.slen) {
+ *buf++ = ':';
+ copy_advance_no_check(buf, url->passwd);
+ }
+
+ *buf++ = '@';
+ }
+
+ /* Print host. */
+ pj_assert(url->host.slen != 0);
+ copy_advance_no_check(buf, url->host);
+
+ /* Only print port if it is explicitly specified.
+ * Port is not allowed in To and From header.
+ */
+ /* Unfortunately some UA requires us to send back the port
+ * number exactly as it was sent. We don't remember whether an
+ * UA has sent us port, so we'll just send the port indiscrimately
+ */
+ PJ_TODO(SHOULD_DISALLOW_URI_PORT_IN_FROM_TO_HEADER)
+ if (url->port /*&& context != PJSIP_URI_IN_FROMTO_HDR*/) {
+ *buf++ = ':';
+ printed = pj_utoa(url->port, buf);
+ buf += printed;
+ }
+
+ /* User param is allowed in all contexes */
+ copy_advance_pair_no_check(buf, ";user=", 6, url->user_param);
+
+ /* Method param is only allowed in external/other context. */
+ if (context == PJSIP_URI_IN_OTHER) {
+ copy_advance_pair_no_check(buf, ";method=", 8, url->method_param);
+ }
+
+ /* Transport is not allowed in From/To header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ copy_advance_pair_no_check(buf, ";transport=", 11, url->transport_param);
+ }
+
+ /* TTL param is not allowed in From, To, Route, and Record-Route header. */
+ if (url->ttl_param >= 0 && context != PJSIP_URI_IN_FROMTO_HDR &&
+ context != PJSIP_URI_IN_ROUTING_HDR)
+ {
+ pj_memcpy(buf, ";ttl=", 5);
+ printed = pj_utoa(url->ttl_param, buf+5);
+ buf += printed + 5;
+ }
+
+ /* maddr param is not allowed in From and To header. */
+ if (context != PJSIP_URI_IN_FROMTO_HDR) {
+ copy_advance_pair_no_check(buf, ";maddr=", 7, url->maddr_param);
+ }
+
+ /* lr param is not allowed in From, To, and Contact header. */
+ if (url->lr_param && context != PJSIP_URI_IN_FROMTO_HDR &&
+ context != PJSIP_URI_IN_CONTACT_HDR)
+ {
+ pj_str_t lr = { ";lr", 3 };
+ copy_advance_no_check(buf, lr);
+ }
+
+ /* Other param. */
+ if (url->other_param.slen) {
+ copy_advance_no_check(buf, url->other_param);
+ }
+
+ /* Header param. */
+ if (url->header_param.slen) {
+ copy_advance_no_check(buf, url->header_param);
+ }
+
+ *buf = '\0';
+ return buf-startbuf;
+}
+
+static int pjsip_url_compare( pjsip_uri_context_e context,
+ const pjsip_url *url1, const pjsip_url *url2)
+{
+ /* The easiest (and probably the most efficient) way to compare two URLs
+ are to print them, and compare them bytes per bytes. This technique
+ works quite well with RFC3261, as the RFC (unlike RFC2543) defines that
+ components specified in one URL does NOT match its default value if
+ it is not specified in the second URL. For example, parameter "user=ip"
+ does NOT match if it is omited in second URL.
+
+ HOWEVER, THE SAME CAN NOT BE APPLIED FOR other-param NOR header-param.
+ For these, each of the parameters must be compared one by one. Parameter
+ that exists in one URL will match the comparison. But parameter that
+ exists in both URLs and doesn't match wont match the URL comparison.
+
+ The solution for this is to compare 'standard' URL components with
+ bytes-to-bytes comparison, and compare other-param and header-param with
+ more intelligent comparison.
+ */
+ char str_url1[PJSIP_MAX_URL_SIZE];
+ char str_url2[PJSIP_MAX_URL_SIZE];
+ int len1, len2;
+
+ /* Must compare scheme first, as the second URI may not be SIP URL. */
+ if (pj_stricmp(pjsip_uri_get_scheme(url1), pjsip_uri_get_scheme(url2)))
+ return -1;
+
+ len1 = pjsip_url_print(context, url1, str_url1, sizeof(str_url1));
+ if (len1 < 1) {
+ pj_assert(0);
+ return -1;
+ }
+ len2 = pjsip_url_print(context, url2, str_url2, sizeof(str_url2));
+ if (len2 < 1) {
+ pj_assert(0);
+ return -1;
+ }
+
+ if (len1 != len2) {
+ /* Not equal. */
+ return -1;
+ }
+
+ if (pj_native_strcmp(str_url1, str_url2)) {
+ /* Not equal */
+ return -1;
+ }
+
+ /* TODO: compare other-param and header-param in more intelligent manner. */
+ PJ_TODO(HPARAM_AND_OTHER_PARAM_COMPARISON_IN_URL_COMPARISON)
+
+ if (pj_strcmp(&url1->other_param, &url2->other_param)) {
+ /* Not equal. */
+ return -1;
+ }
+ if (pj_strcmp(&url1->header_param, &url2->header_param)) {
+ /* Not equal. */
+ return -1;
+ }
+
+ /* Seems to be equal, isn't it. */
+ return 0;
+
+}
+
+
+PJ_DEF(void) pjsip_url_assign(pj_pool_t *pool, pjsip_url *url,
+ const pjsip_url *rhs)
+{
+ pj_strdup( pool, &url->user, &rhs->user);
+ pj_strdup( pool, &url->passwd, &rhs->passwd);
+ pj_strdup( pool, &url->host, &rhs->host);
+ url->port = rhs->port;
+ pj_strdup( pool, &url->user_param, &rhs->user_param);
+ pj_strdup( pool, &url->method_param, &rhs->method_param);
+ pj_strdup( pool, &url->transport_param, &rhs->transport_param);
+ url->ttl_param = rhs->ttl_param;
+ pj_strdup( pool, &url->maddr_param, &rhs->maddr_param);
+ pj_strdup( pool, &url->other_param, &rhs->other_param);
+ pj_strdup( pool, &url->header_param, &rhs->header_param);
+ url->lr_param = rhs->lr_param;
+}
+
+static pjsip_url* pjsip_url_clone(pj_pool_t *pool, const pjsip_url *rhs)
+{
+ pjsip_url *url = pj_pool_alloc(pool, sizeof(pjsip_url));
+ if (!url)
+ return NULL;
+
+ pjsip_url_init(url, IS_SIPS(rhs));
+ pjsip_url_assign(pool, url, rhs);
+ return url;
+}
+
+static const pj_str_t *pjsip_name_addr_get_scheme(const pjsip_name_addr *name)
+{
+ pj_assert(name->uri != NULL);
+ return pjsip_uri_get_scheme(name->uri);
+}
+
+PJ_DEF(void) pjsip_name_addr_init(pjsip_name_addr *name)
+{
+ name->vptr = &name_addr_vptr;
+ name->uri = NULL;
+ name->display.slen = 0;
+}
+
+PJ_DEF(pjsip_name_addr*) pjsip_name_addr_create(pj_pool_t *pool)
+{
+ pjsip_name_addr *name_addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
+ pjsip_name_addr_init(name_addr);
+ 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)
+{
+ int printed;
+ char *startbuf = buf;
+ char *endbuf = buf + size;
+
+ pj_assert(name->uri != NULL);
+
+ if (context != PJSIP_URI_IN_REQ_URI) {
+ copy_advance(buf, name->display);
+ if (name->display.slen) {
+ *buf++ = ' ';
+ }
+ *buf++ = '<';
+ }
+
+ printed = pjsip_uri_print(context,name->uri, buf, size-(buf-startbuf));
+ if (printed < 1)
+ return -1;
+ buf += printed;
+
+ if (context != PJSIP_URI_IN_REQ_URI) {
+ *buf++ = '>';
+ }
+
+ *buf = '\0';
+ return buf-startbuf;
+}
+
+PJ_DEF(void) pjsip_name_addr_assign(pj_pool_t *pool, pjsip_name_addr *dst,
+ const pjsip_name_addr *src)
+{
+ pj_strdup( pool, &dst->display, &src->display);
+ dst->uri = pjsip_uri_clone(pool, src->uri);
+}
+
+static pjsip_name_addr* pjsip_name_addr_clone( pj_pool_t *pool,
+ const pjsip_name_addr *rhs)
+{
+ pjsip_name_addr *addr = pj_pool_alloc(pool, sizeof(pjsip_name_addr));
+ if (!addr)
+ return NULL;
+
+ pjsip_name_addr_init(addr);
+ pjsip_name_addr_assign(pool, addr, rhs);
+ return addr;
+}
+
+static int pjsip_name_addr_compare( pjsip_uri_context_e context,
+ const pjsip_name_addr *naddr1,
+ const pjsip_name_addr *naddr2)
+{
+ int d;
+
+ /* I'm not sure whether display name is included in the comparison. */
+ if (pj_strcmp(&naddr1->display, &naddr2->display) != 0) {
+ return -1;
+ }
+
+ pj_assert( naddr1->uri != NULL );
+ pj_assert( naddr2->uri != NULL );
+
+ /* Compare name-addr as URL */
+ d = pjsip_uri_cmp( context, naddr1->uri, naddr2->uri);
+ if (d)
+ return d;
+
+ return 0;
+}
+
diff --git a/pjsip/src/pjsua/getopt.c b/pjsip/src/pjsua/getopt.c index 5d4689cf..ce7891d2 100644 --- a/pjsip/src/pjsua/getopt.c +++ b/pjsip/src/pjsua/getopt.c @@ -1,1046 +1,1068 @@ -/* $Id$ - * - */ - -#ifdef _MSC_VER -/* in VC this file will generate a lot of warning about old style function - * declarations. - */ -# pragma warning(push, 3) -#endif - -/* - * getopt entry points - * - * modified by Mike Borella <mike_borella@mw.3com.com> - * - * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $ - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef HAVE_GETOPT_LONG - -/* getopt_long and getopt_long_only entry points for GNU getopt. - Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#include "getopt.h" - - -#include <stdio.h> - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 -#include <gnu-versions.h> -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -#include <stdlib.h> -#endif - -#ifndef NULL -#define NULL 0 -#endif - -int -getopt_long (int argc, char *const *argv, const char *options, - const struct option *long_options, int *opt_index) -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 0); -} - -/* Like getopt_long, but '-' as well as '--' can indicate a long option. - If an option that starts with '-' (not '--') doesn't match a long option, - but does match a short option, it is parsed as a short option - instead. */ - -int -getopt_long_only (argc, argv, options, long_options, opt_index) - int argc; - char *const *argv; - const char *options; - const struct option *long_options; - int *opt_index; -{ - return _getopt_internal (argc, argv, options, long_options, opt_index, 1); -} - -int -getopt (int argc, char * const * argv, const char * optstring) -{ - return _getopt_internal (argc, argv, optstring, - (const struct option *) 0, - (int *) 0, - 0); -} - -#endif /* Not ELIDE_CODE. */ - - -#ifndef _NO_PROTO -#define _NO_PROTO -#endif - - -//#include <strings.h> - -/* Comment out all this code if we are using the GNU C Library, and are not - actually compiling the library itself. This code is part of the GNU C - Library, but also included in many other GNU distributions. Compiling - and linking in this code is a waste when using the GNU C library - (especially if it is a shared library). Rather than having every GNU - program understand `configure --with-gnu-libc' and omit the object files, - it is simpler to just do this in the source for each such file. */ - -#define GETOPT_INTERFACE_VERSION 2 -#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2 -#include <gnu-versions.h> -#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION -#define ELIDE_CODE -#endif -#endif - -#ifndef ELIDE_CODE - - -/* This needs to come after some library #include - to get __GNU_LIBRARY__ defined. */ -#ifdef __GNU_LIBRARY__ -/* Don't include stdlib.h for non-GNU C libraries because some of them - contain conflicting prototypes for getopt. */ -#include <stdlib.h> -#include <unistd.h> -#endif /* GNU C library. */ - -#ifdef VMS -#include <unixlib.h> -#if HAVE_STRING_H - 0 -#include <string.h> -#endif -#endif - -#if defined (WIN32) && !defined (__CYGWIN32__) -/* It's not Unix, really. See? Capital letters. */ -#include <windows.h> -#define getpid() GetCurrentProcessId() -#endif - -#ifndef _ -/* This is for other GNU distributions with internationalized messages. - When compiling libc, the _ macro is predefined. */ -#ifdef HAVE_LIBINTL_H -# include <libintl.h> -# define _(msgid) gettext (msgid) -#else -# define _(msgid) (msgid) -#endif -#endif - -/* This version of `getopt' appears to the caller like standard Unix `getopt' - but it behaves differently for the user, since it allows the user - to intersperse the options with the other arguments. - - As `getopt' works, it permutes the elements of ARGV so that, - when it is done, all the options precede everything else. Thus - all application programs are extended to handle flexible argument order. - - Setting the environment variable POSIXLY_CORRECT disables permutation. - Then the behavior is completely standard. - - GNU application programs can use a third alternative mode in which - they can distinguish the relative order of options and other arguments. */ - -#include "getopt.h" - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -char *optarg = NULL; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -/* 1003.2 says this must be 1 before any call. */ -int optind = 1; - -/* Formerly, initialization of getopt depended on optind==0, which - causes problems with re-calling getopt as programs generally don't - know that. */ - -int __getopt_initialized = 0; - -/* The next char to be scanned in the option-element - in which the last option character we returned was found. - This allows us to pick up the scan where we left off. - - If this is zero, or a null string, it means resume the scan - by advancing to the next ARGV-element. */ - -static char *nextchar; - -/* Callers store zero here to inhibit the error message - for unrecognized options. */ - -int opterr = 1; - -/* Set to an option character which was unrecognized. - This must be initialized on some systems to avoid linking in the - system's own getopt implementation. */ - -int optopt = '?'; - -/* Describe how to deal with options that follow non-option ARGV-elements. - - If the caller did not specify anything, - the default is REQUIRE_ORDER if the environment variable - POSIXLY_CORRECT is defined, PERMUTE otherwise. - - REQUIRE_ORDER means don't recognize them as options; - stop option processing when the first non-option is seen. - This is what Unix does. - This mode of operation is selected by either setting the environment - variable POSIXLY_CORRECT, or using `+' as the first character - of the list of option characters. - - PERMUTE is the default. We permute the contents of ARGV as we scan, - so that eventually all the non-options are at the end. This allows options - to be given in any order, even with programs that were not written to - expect this. - - RETURN_IN_ORDER is an option available to programs that were written - to expect options and other ARGV-elements in any order and that care about - the ordering of the two. We describe each non-option ARGV-element - as if it were the argument of an option with character code 1. - Using `-' as the first character of the list of option characters - selects this mode of operation. - - The special argument `--' forces an end of option-scanning regardless - of the value of `ordering'. In the case of RETURN_IN_ORDER, only - `--' can cause `getopt' to return -1 with `optind' != ARGC. */ - -static enum -{ - REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER -} ordering; - -/* Value of POSIXLY_CORRECT environment variable. */ -static char *posixly_correct; - -#ifdef __GNU_LIBRARY__ -/* We want to avoid inclusion of string.h with non-GNU libraries - because there are many ways it can cause trouble. - On some systems, it contains special magic macros that don't work - in GCC. */ -#include <string.h> -#define my_index pj_native_strchr -#else - -static char * -my_index (const char *str, int chr) -{ - while (*str) - { - if (*str == chr) - return (char *) str; - str++; - } - return 0; -} - -/* If using GCC, we can safely declare strlen this way. - If not using GCC, it is ok not to declare it. */ -#ifdef __GNUC__ -/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. - That was relevant to code that was here before. */ -#if !defined (__STDC__) || !__STDC__ -/* gcc with -traditional declares the built-in strlen to return int, - and has done so at least since version 2.4.5. -- rms. */ -extern int strlen (const char *); -#endif /* not __STDC__ */ -#endif /* __GNUC__ */ - -#endif /* not __GNU_LIBRARY__ */ - -/* Handle permutation of arguments. */ - -/* Describe the part of ARGV that contains non-options that have - been skipped. `first_nonopt' is the index in ARGV of the first of them; - `last_nonopt' is the index after the last of them. */ - -static int first_nonopt; -static int last_nonopt; - -#ifdef _LIBC -/* Bash 2.0 gives us an environment variable containing flags - indicating ARGV elements that should not be considered arguments. */ - -/* Defined in getopt_init.c */ -extern char *__getopt_nonoption_flags; - -static int nonoption_flags_max_len; -static int nonoption_flags_len; - -static int original_argc; -static char *const *original_argv; - -extern pid_t __libc_pid; - -/* Make sure the environment variable bash 2.0 puts in the environment - is valid for the getopt call we must make sure that the ARGV passed - to getopt is that one passed to the process. */ -static void -__attribute__ ((unused)) -store_args_and_env (int argc, char *const *argv) -{ - /* XXX This is no good solution. We should rather copy the args so - that we can compare them later. But we must not use malloc(3). */ - original_argc = argc; - original_argv = argv; -} -text_set_element (__libc_subinit, store_args_and_env); - -# define SWAP_FLAGS(ch1, ch2) \ - if (nonoption_flags_len > 0) \ - { \ - char __tmp = __getopt_nonoption_flags[ch1]; \ - __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ - __getopt_nonoption_flags[ch2] = __tmp; \ - } -#else /* !_LIBC */ -# define SWAP_FLAGS(ch1, ch2) -#endif /* _LIBC */ - -/* Exchange two adjacent subsequences of ARGV. - One subsequence is elements [first_nonopt,last_nonopt) - which contains all the non-options that have been skipped so far. - The other is elements [last_nonopt,optind), which contains all - the options processed since those non-options were skipped. - - `first_nonopt' and `last_nonopt' are relocated so that they describe - the new indices of the non-options in ARGV after they are moved. */ - -#if defined (__STDC__) && __STDC__ -static void exchange (char **); -#endif - -static void -exchange (argv) - char **argv; -{ - int bottom = first_nonopt; - int middle = last_nonopt; - int top = optind; - char *tem; - - /* Exchange the shorter segment with the far end of the longer segment. - That puts the shorter segment into the right place. - It leaves the longer segment in the right place overall, - but it consists of two parts that need to be swapped next. */ - -#ifdef _LIBC - /* First make sure the handling of the `__getopt_nonoption_flags' - string can work normally. Our top argument must be in the range - of the string. */ - if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) - { - /* We must extend the array. The user plays games with us and - presents new arguments. */ - char *new_str = malloc (top + 1); - if (new_str == NULL) - nonoption_flags_len = nonoption_flags_max_len = 0; - else - { - memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len); - memset (&new_str[nonoption_flags_max_len], '\0', - top + 1 - nonoption_flags_max_len); - nonoption_flags_max_len = top + 1; - __getopt_nonoption_flags = new_str; - } - } -#endif - - while (top > middle && middle > bottom) - { - if (top - middle > middle - bottom) - { - /* Bottom segment is the short one. */ - int len = middle - bottom; - register int i; - - /* Swap it with the top part of the top segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); - } - /* Exclude the moved bottom segment from further swapping. */ - top -= len; - } - else - { - /* Top segment is the short one. */ - int len = top - middle; - register int i; - - /* Swap it with the bottom part of the bottom segment. */ - for (i = 0; i < len; i++) - { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - SWAP_FLAGS (bottom + i, middle + i); - } - /* Exclude the moved top segment from further swapping. */ - bottom += len; - } - } - - /* Update records for the slots the non-options now occupy. */ - - first_nonopt += (optind - last_nonopt); - last_nonopt = optind; -} - -/* Initialize the internal data when the first call is made. */ - -#if defined (__STDC__) && __STDC__ -static const char *_getopt_initialize (int, char *const *, const char *); -#endif -static const char * -_getopt_initialize (argc, argv, optstring) - int argc; - char *const *argv; - const char *optstring; -{ - /* Start processing options with ARGV-element 1 (since ARGV-element 0 - is the program name); the sequence of previously skipped - non-option ARGV-elements is empty. */ - - first_nonopt = last_nonopt = optind; - - nextchar = NULL; - - posixly_correct = getenv ("POSIXLY_CORRECT"); - - /* Determine how to handle the ordering of options and nonoptions. */ - - if (optstring[0] == '-') - { - ordering = RETURN_IN_ORDER; - ++optstring; - } - else if (optstring[0] == '+') - { - ordering = REQUIRE_ORDER; - ++optstring; - } - else if (posixly_correct != NULL) - ordering = REQUIRE_ORDER; - else - ordering = PERMUTE; - -#ifdef _LIBC - if (posixly_correct == NULL - && argc == original_argc && argv == original_argv) - { - if (nonoption_flags_max_len == 0) - { - if (__getopt_nonoption_flags == NULL - || __getopt_nonoption_flags[0] == '\0') - nonoption_flags_max_len = -1; - else - { - const char *orig_str = __getopt_nonoption_flags; - int len = nonoption_flags_max_len = strlen (orig_str); - if (nonoption_flags_max_len < argc) - nonoption_flags_max_len = argc; - __getopt_nonoption_flags = - (char *) malloc (nonoption_flags_max_len); - if (__getopt_nonoption_flags == NULL) - nonoption_flags_max_len = -1; - else - { - memcpy (__getopt_nonoption_flags, orig_str, len); - memset (&__getopt_nonoption_flags[len], '\0', - nonoption_flags_max_len - len); - } - } - } - nonoption_flags_len = nonoption_flags_max_len; - } - else - nonoption_flags_len = 0; -#endif - - return optstring; -} - -/* Scan elements of ARGV (whose length is ARGC) for option characters - given in OPTSTRING. - - If an element of ARGV starts with '-', and is not exactly "-" or "--", - then it is an option element. The characters of this element - (aside from the initial '-') are option characters. If `getopt' - is called repeatedly, it returns successively each of the option characters - from each of the option elements. - - If `getopt' finds another option character, it returns that character, - updating `optind' and `nextchar' so that the next call to `getopt' can - resume the scan with the following option character or ARGV-element. - - If there are no more option characters, `getopt' returns -1. - Then `optind' is the index in ARGV of the first ARGV-element - that is not an option. (The ARGV-elements have been permuted - so that those that are not options now come last.) - - OPTSTRING is a string containing the legitimate option characters. - If an option character is seen that is not listed in OPTSTRING, - return '?' after printing an error message. If you set `opterr' to - zero, the error message is suppressed but we still return '?'. - - If a char in OPTSTRING is followed by a colon, that means it wants an arg, - so the following text in the same ARGV-element, or the text of the following - ARGV-element, is returned in `optarg'. Two colons mean an option that - wants an optional arg; if there is text in the current ARGV-element, - it is returned in `optarg', otherwise `optarg' is set to zero. - - If OPTSTRING starts with `-' or `+', it requests different methods of - handling the non-option ARGV-elements. - See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. - - Long-named options begin with `--' instead of `-'. - Their names may be abbreviated as long as the abbreviation is unique - or is an exact match for some defined option. If they have an - argument, it follows the option name in the same ARGV-element, separated - from the option name by a `=', or else the in next ARGV-element. - When `getopt' finds a long-named option, it returns 0 if that option's - `flag' field is nonzero, the value of the option's `val' field - if the `flag' field is zero. - - The elements of ARGV aren't really const, because we permute them. - But we pretend they're const in the prototype to be compatible - with other systems. - - LONGOPTS is a vector of `struct option' terminated by an - element containing a name which is zero. - - LONGIND returns the index in LONGOPT of the long-named option found. - It is only valid when a long-named option has been found by the most - recent call. - - If LONG_ONLY is nonzero, '-' as well as '--' can introduce - long-named options. */ - -int -_getopt_internal (argc, argv, optstring, longopts, longind, long_only) - int argc; - char *const *argv; - const char *optstring; - const struct option *longopts; - int *longind; - int long_only; -{ - optarg = NULL; - - if (optind == 0 || !__getopt_initialized) - { - if (optind == 0) - optind = 1; /* Don't scan ARGV[0], the program name. */ - optstring = _getopt_initialize (argc, argv, optstring); - __getopt_initialized = 1; - } - - /* Test whether ARGV[optind] points to a non-option argument. - Either it does not have option syntax, or there is an environment flag - from the shell indicating it is not an option. The later information - is only used when the used in the GNU libc. */ -#ifdef _LIBC -#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ - || (optind < nonoption_flags_len \ - && __getopt_nonoption_flags[optind] == '1')) -#else -#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') -#endif - - if (nextchar == NULL || *nextchar == '\0') - { - /* Advance to the next ARGV-element. */ - - /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been - moved back by the user (who may also have changed the arguments). */ - if (last_nonopt > optind) - last_nonopt = optind; - if (first_nonopt > optind) - first_nonopt = optind; - - if (ordering == PERMUTE) - { - /* If we have just processed some options following some non-options, - exchange them so that the options come first. */ - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (last_nonopt != optind) - first_nonopt = optind; - - /* Skip any additional non-options - and extend the range of non-options previously skipped. */ - - while (optind < argc && NONOPTION_P) - optind++; - last_nonopt = optind; - } - - /* The special ARGV-element `--' means premature end of options. - Skip it like a null option, - then exchange with previous non-options as if it were an option, - then skip everything else like a non-option. */ - - if (optind != argc && !pj_native_strcmp(argv[optind], "--")) - { - optind++; - - if (first_nonopt != last_nonopt && last_nonopt != optind) - exchange ((char **) argv); - else if (first_nonopt == last_nonopt) - first_nonopt = optind; - last_nonopt = argc; - - optind = argc; - } - - /* If we have done all the ARGV-elements, stop the scan - and back over any non-options that we skipped and permuted. */ - - if (optind == argc) - { - /* Set the next-arg-index to point at the non-options - that we previously skipped, so the caller will digest them. */ - if (first_nonopt != last_nonopt) - optind = first_nonopt; - return -1; - } - - /* If we have come to a non-option and did not permute it, - either stop the scan or describe it to the caller and pass it by. */ - - if (NONOPTION_P) - { - if (ordering == REQUIRE_ORDER) - return -1; - optarg = argv[optind++]; - return 1; - } - - /* We have found another option-ARGV-element. - Skip the initial punctuation. */ - - nextchar = (argv[optind] + 1 - + (longopts != NULL && argv[optind][1] == '-')); - } - - /* Decode the current option-ARGV-element. */ - - /* Check whether the ARGV-element is a long option. - - If long_only and the ARGV-element has the form "-f", where f is - a valid short option, don't consider it an abbreviated form of - a long option that starts with f. Otherwise there would be no - way to give the -f short option. - - On the other hand, if there's a long option "fubar" and - the ARGV-element is "-fu", do consider that an abbreviation of - the long option, just like "--fu", and not "-f" with arg "u". - - This distinction seems to be the most useful approach. */ - - if (longopts != NULL - && (argv[optind][1] == '-' - || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = -1; - int option_index; - - for (nameend = nextchar; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) - == (unsigned int) strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - - if (ambig && !exact) - { - if (opterr) - fprintf (stderr, _("%s: option `%s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - optopt = 0; - return '?'; - } - - if (pfound != NULL) - { - option_index = indfound; - optind++; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (opterr) - { - if (argv[optind - 1][1] == '-') - /* --option */ - fprintf (stderr, - _("%s: option `--%s' doesn't allow an argument\n"), - argv[0], pfound->name); - else - { - /* +option or -option */ - fprintf (stderr, - _("%s: option `%c%s' doesn't allow an argument\n"), - argv[0], argv[optind - 1][0], pfound->name); - } - } - nextchar += strlen (nextchar); - - optopt = pfound->val; - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (opterr) - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - optopt = pfound->val; - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - - /* Can't find it as a long option. If this is not getopt_long_only, - or the option starts with '--' or is not a valid short - option, then it's an error. - Otherwise interpret it as a short option. */ - if (!long_only || argv[optind][1] == '-' - || my_index (optstring, *nextchar) == NULL) - { - if (opterr) - { - if (argv[optind][1] == '-') - /* --option */ - fprintf (stderr, _("%s: unrecognized option `--%s'\n"), - argv[0], nextchar); - else - /* +option or -option */ - fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), - argv[0], argv[optind][0], nextchar); - } - nextchar = (char *) ""; - optind++; - optopt = 0; - return '?'; - } - } - - /* Look at and handle the next short option-character. */ - - { - char c = *nextchar++; - char *temp = my_index (optstring, c); - - /* Increment `optind' when we start to process its last character. */ - if (*nextchar == '\0') - ++optind; - - if (temp == NULL || c == ':') - { - if (opterr) - { - if (posixly_correct) - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, _("%s: illegal option -- %c\n"), - argv[0], c); - else - fprintf (stderr, _("%s: invalid option -- %c\n"), - argv[0], c); - } - optopt = c; - return '?'; - } - /* Convenience. Treat POSIX -W foo same as long option --foo */ - if (temp[0] == 'W' && temp[1] == ';') - { - char *nameend; - const struct option *p; - const struct option *pfound = NULL; - int exact = 0; - int ambig = 0; - int indfound = 0; - int option_index; - - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr) - { - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - return c; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - - /* optarg is now the argument, see if it's in the - table of longopts. */ - - for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) - /* Do nothing. */ ; - - /* Test all long options for either exact match - or abbreviated matches. */ - for (p = longopts, option_index = 0; p->name; p++, option_index++) - if (!strncmp (p->name, nextchar, nameend - nextchar)) - { - if ((unsigned int) (nameend - nextchar) == strlen (p->name)) - { - /* Exact match found. */ - pfound = p; - indfound = option_index; - exact = 1; - break; - } - else if (pfound == NULL) - { - /* First nonexact match found. */ - pfound = p; - indfound = option_index; - } - else - /* Second or later nonexact match found. */ - ambig = 1; - } - if (ambig && !exact) - { - if (opterr) - fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), - argv[0], argv[optind]); - nextchar += strlen (nextchar); - optind++; - return '?'; - } - if (pfound != NULL) - { - option_index = indfound; - if (*nameend) - { - /* Don't test has_arg with >, because some C compilers don't - allow it to be used on enums. */ - if (pfound->has_arg) - optarg = nameend + 1; - else - { - if (opterr) - fprintf (stderr, _("\ -%s: option `-W %s' doesn't allow an argument\n"), - argv[0], pfound->name); - - nextchar += strlen (nextchar); - return '?'; - } - } - else if (pfound->has_arg == 1) - { - if (optind < argc) - optarg = argv[optind++]; - else - { - if (opterr) - fprintf (stderr, - _("%s: option `%s' requires an argument\n"), - argv[0], argv[optind - 1]); - nextchar += strlen (nextchar); - return optstring[0] == ':' ? ':' : '?'; - } - } - nextchar += strlen (nextchar); - if (longind != NULL) - *longind = option_index; - if (pfound->flag) - { - *(pfound->flag) = pfound->val; - return 0; - } - return pfound->val; - } - nextchar = NULL; - return 'W'; /* Let the application handle it. */ - } - if (temp[1] == ':') - { - if (temp[2] == ':') - { - /* This is an option that accepts an argument optionally. */ - if (*nextchar != '\0') - { - optarg = nextchar; - optind++; - } - else - optarg = NULL; - nextchar = NULL; - } - else - { - /* This is an option that requires an argument. */ - if (*nextchar != '\0') - { - optarg = nextchar; - /* If we end this ARGV-element by taking the rest as an arg, - we must advance to the next element now. */ - optind++; - } - else if (optind == argc) - { - if (opterr) - { - /* 1003.2 specifies the format of this message. */ - fprintf (stderr, - _("%s: option requires an argument -- %c\n"), - argv[0], c); - } - optopt = c; - if (optstring[0] == ':') - c = ':'; - else - c = '?'; - } - else - /* We already incremented `optind' once; - increment it again when taking next ARGV-elt as argument. */ - optarg = argv[optind++]; - nextchar = NULL; - } - } - return c; - } -} - -#endif /* Not ELIDE_CODE. */ - -#endif - -#ifdef _MSC_VER -# pragma warning(pop) -#endif - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifdef _MSC_VER
+/* in VC this file will generate a lot of warning about old style function
+ * declarations.
+ */
+# pragma warning(push, 3)
+#endif
+
+/*
+ * getopt entry points
+ *
+ * modified by Mike Borella <mike_borella@mw.3com.com>
+ *
+ * $Id: getopt.c,v 1.4 2000/10/30 22:06:03 mborella Exp $
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef HAVE_GETOPT_LONG
+
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987,88,89,90,91,92,93,94,96,97 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "getopt.h"
+
+
+#include <stdio.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (int argc, char *const *argv, const char *options,
+ const struct option *long_options, int *opt_index)
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (argc, argv, options, long_options, opt_index)
+ int argc;
+ char *const *argv;
+ const char *options;
+ const struct option *long_options;
+ int *opt_index;
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 1);
+}
+
+int
+getopt (int argc, char * const * argv, const char * optstring)
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+#endif /* Not ELIDE_CODE. */
+
+
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+
+//#include <strings.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#define GETOPT_INTERFACE_VERSION 2
+#if !defined (_LIBC) && defined (__GLIBC__) && __GLIBC__ >= 2
+#include <gnu-versions.h>
+#if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION
+#define ELIDE_CODE
+#endif
+#endif
+
+#ifndef ELIDE_CODE
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#include <unistd.h>
+#endif /* GNU C library. */
+
+#ifdef VMS
+#include <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really. See? Capital letters. */
+#include <windows.h>
+#define getpid() GetCurrentProcessId()
+#endif
+
+#ifndef _
+/* This is for other GNU distributions with internationalized messages.
+ When compiling libc, the _ macro is predefined. */
+#ifdef HAVE_LIBINTL_H
+# include <libintl.h>
+# define _(msgid) gettext (msgid)
+#else
+# define _(msgid) (msgid)
+#endif
+#endif
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = NULL;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Formerly, initialization of getopt depended on optind==0, which
+ causes problems with re-calling getopt as programs generally don't
+ know that. */
+
+int __getopt_initialized = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return -1 with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+/* Value of POSIXLY_CORRECT environment variable. */
+static char *posixly_correct;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index pj_native_strchr
+#else
+
+static char *
+my_index (const char *str, int chr)
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+/* If using GCC, we can safely declare strlen this way.
+ If not using GCC, it is ok not to declare it. */
+#ifdef __GNUC__
+/* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h.
+ That was relevant to code that was here before. */
+#if !defined (__STDC__) || !__STDC__
+/* gcc with -traditional declares the built-in strlen to return int,
+ and has done so at least since version 2.4.5. -- rms. */
+extern int strlen (const char *);
+#endif /* not __STDC__ */
+#endif /* __GNUC__ */
+
+#endif /* not __GNU_LIBRARY__ */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+#ifdef _LIBC
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+
+static int nonoption_flags_max_len;
+static int nonoption_flags_len;
+
+static int original_argc;
+static char *const *original_argv;
+
+extern pid_t __libc_pid;
+
+/* Make sure the environment variable bash 2.0 puts in the environment
+ is valid for the getopt call we must make sure that the ARGV passed
+ to getopt is that one passed to the process. */
+static void
+__attribute__ ((unused))
+store_args_and_env (int argc, char *const *argv)
+{
+ /* XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+ original_argc = argc;
+ original_argv = argv;
+}
+text_set_element (__libc_subinit, store_args_and_env);
+
+# define SWAP_FLAGS(ch1, ch2) \
+ if (nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+#if defined (__STDC__) && __STDC__
+static void exchange (char **);
+#endif
+
+static void
+exchange (argv)
+ char **argv;
+{
+ int bottom = first_nonopt;
+ int middle = last_nonopt;
+ int top = optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#ifdef _LIBC
+ /* First make sure the handling of the `__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = malloc (top + 1);
+ if (new_str == NULL)
+ nonoption_flags_len = nonoption_flags_max_len = 0;
+ else
+ {
+ memcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len);
+ memset (&new_str[nonoption_flags_max_len], '\0',
+ top + 1 - nonoption_flags_max_len);
+ nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ first_nonopt += (optind - last_nonopt);
+ last_nonopt = optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+#if defined (__STDC__) && __STDC__
+static const char *_getopt_initialize (int, char *const *, const char *);
+#endif
+static const char *
+_getopt_initialize (argc, argv, optstring)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ first_nonopt = last_nonopt = optind;
+
+ nextchar = NULL;
+
+ posixly_correct = getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (posixly_correct != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+
+#ifdef _LIBC
+ if (posixly_correct == NULL
+ && argc == original_argc && argv == original_argv)
+ {
+ if (nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = nonoption_flags_max_len = strlen (orig_str);
+ if (nonoption_flags_max_len < argc)
+ nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) malloc (nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ nonoption_flags_max_len = -1;
+ else
+ {
+ memcpy (__getopt_nonoption_flags, orig_str, len);
+ memset (&__getopt_nonoption_flags[len], '\0',
+ nonoption_flags_max_len - len);
+ }
+ }
+ }
+ nonoption_flags_len = nonoption_flags_max_len;
+ }
+ else
+ nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns -1.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal (argc, argv, optstring, longopts, longind, long_only)
+ int argc;
+ char *const *argv;
+ const char *optstring;
+ const struct option *longopts;
+ int *longind;
+ int long_only;
+{
+ optarg = NULL;
+
+ if (optind == 0 || !__getopt_initialized)
+ {
+ if (optind == 0)
+ optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring);
+ __getopt_initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#ifdef _LIBC
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \
+ || (optind < nonoption_flags_len \
+ && __getopt_nonoption_flags[optind] == '1'))
+#else
+#define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#endif
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (last_nonopt > optind)
+ last_nonopt = optind;
+ if (first_nonopt > optind)
+ first_nonopt = optind;
+
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc && NONOPTION_P)
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* The special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !pj_native_strcmp(argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (ordering == REQUIRE_ORDER)
+ return -1;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[optind][1] == '-'
+ || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `%s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ _("%s: option `--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+ else
+ {
+ /* +option or -option */
+ fprintf (stderr,
+ _("%s: option `%c%s' doesn't allow an argument\n"),
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ }
+ nextchar += strlen (nextchar);
+
+ optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, _("%s: unrecognized option `--%s'\n"),
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"),
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+ if (posixly_correct)
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+ else
+ fprintf (stderr, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, nextchar, nameend - nextchar))
+ {
+ if ((unsigned int) (nameend - nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"),
+ argv[0], argv[optind]);
+ nextchar += strlen (nextchar);
+ optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = nameend + 1;
+ else
+ {
+ if (opterr)
+ fprintf (stderr, _("\
+%s: option `-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+
+ nextchar += strlen (nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr,
+ _("%s: option `%s' requires an argument\n"),
+ argv[0], argv[optind - 1]);
+ nextchar += strlen (nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ nextchar += strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = NULL;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+#endif /* Not ELIDE_CODE. */
+
+#endif
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
diff --git a/pjsip/src/pjsua/getopt.h b/pjsip/src/pjsua/getopt.h index b5c91d01..d9778f15 100644 --- a/pjsip/src/pjsua/getopt.h +++ b/pjsip/src/pjsua/getopt.h @@ -1,142 +1,164 @@ -/* $Id$ - * - */ -/* Declarations for getopt. - Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. - This file is part of the GNU C Library. - - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with the GNU C Library; see the file COPYING.LIB. If not, - write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ - -#ifndef _GETOPT_H -#define _GETOPT_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -/* For communication from `getopt' to the caller. - When `getopt' finds an option that takes an argument, - the argument value is returned here. - Also, when `ordering' is RETURN_IN_ORDER, - each non-option ARGV-element is returned here. */ - -extern char *optarg; - -/* Index in ARGV of the next element to be scanned. - This is used for communication to and from the caller - and for communication between successive calls to `getopt'. - - On entry to `getopt', zero means this is the first call; initialize. - - When `getopt' returns -1, this is the index of the first of the - non-option elements that the caller should itself scan. - - Otherwise, `optind' communicates from one call to the next - how much of ARGV has been scanned so far. */ - -extern int optind; - -/* Callers store zero here to inhibit the error message `getopt' prints - for unrecognized options. */ - -extern int opterr; - -/* Set to an option character which was unrecognized. */ - -extern int optopt; - -/* Describe the long-named options requested by the application. - The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector - of `struct option' terminated by an element containing a name which is - zero. - - The field `has_arg' is: - no_argument (or 0) if the option does not take an argument, - required_argument (or 1) if the option requires an argument, - optional_argument (or 2) if the option takes an optional argument. - - If the field `flag' is not NULL, it points to a variable that is set - to the value given in the field `val' when the option is found, but - left unchanged if the option is not found. - - To have a long-named option do something other than set an `int' to - a compiled-in constant, such as set a value from `optarg', set the - option's `flag' field to zero and its `val' field to a nonzero - value (the equivalent single-letter option character, if there is - one). For long options that have a zero `flag' field, `getopt' - returns the contents of the `val' field. */ - -struct option -{ - const char *name; - /* has_arg can't be an enum because some compilers complain about - type mismatches in all the code that assumes it is an int. */ - int has_arg; - int *flag; - int val; -}; - -/* Names for the values of the `has_arg' field of `struct option'. */ - -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 - - -/* Get definitions and prototypes for functions to process the - arguments in ARGV (ARGC of them, minus the program name) for - options given in OPTS. - - Return the option character from OPTS just read. Return -1 when - there are no more options. For unrecognized options, or options - missing arguments, `optopt' is set to the option letter, and '?' is - returned. - - The OPTS string is a list of characters which are recognized option - letters, optionally followed by colons, specifying that that letter - takes an argument, to be placed in `optarg'. - - If a letter in OPTS is followed by two colons, its argument is - optional. This behavior is specific to the GNU `getopt'. - - The argument `--' causes premature termination of argument - scanning, explicitly telling `getopt' that there are no more - options. - - If OPTS begins with `--', then non-option arguments are treated as - arguments to the option '\0'. This behavior is specific to the GNU - `getopt'. */ - -int getopt (int argc, char *const *argv, const char *shortopts); - -int getopt_long (int argc, char *const *argv, const char *options, - const struct option *longopts, int *longind); -int getopt_long_only (int argc, char *const *argv, - const char *shortopts, - const struct option *longopts, int *longind); - -/* Internal only. Users should not call this directly. */ -int _getopt_internal (int argc, char *const *argv, - const char *shortopts, - const struct option *longopts, int *longind, - int long_only); - -#ifdef __cplusplus -} -#endif - -#endif /* getopt.h */ - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/* Declarations for getopt.
+ Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+ const char *name;
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+# define no_argument 0
+# define required_argument 1
+# define optional_argument 2
+
+
+/* Get definitions and prototypes for functions to process the
+ arguments in ARGV (ARGC of them, minus the program name) for
+ options given in OPTS.
+
+ Return the option character from OPTS just read. Return -1 when
+ there are no more options. For unrecognized options, or options
+ missing arguments, `optopt' is set to the option letter, and '?' is
+ returned.
+
+ The OPTS string is a list of characters which are recognized option
+ letters, optionally followed by colons, specifying that that letter
+ takes an argument, to be placed in `optarg'.
+
+ If a letter in OPTS is followed by two colons, its argument is
+ optional. This behavior is specific to the GNU `getopt'.
+
+ The argument `--' causes premature termination of argument
+ scanning, explicitly telling `getopt' that there are no more
+ options.
+
+ If OPTS begins with `--', then non-option arguments are treated as
+ arguments to the option '\0'. This behavior is specific to the GNU
+ `getopt'. */
+
+int getopt (int argc, char *const *argv, const char *shortopts);
+
+int getopt_long (int argc, char *const *argv, const char *options,
+ const struct option *longopts, int *longind);
+int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* getopt.h */
+
diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index a8a0f49a..35a89c8a 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -1,1813 +1,1835 @@ -/* $Id$ - * - */ - -#include <pjlib.h> -#include <pjsip_core.h> -#include <pjsip_ua.h> -#include <pjsip_simple.h> -#include <pjmedia.h> -#include <ctype.h> -#include <stdlib.h> -#include <pj/stun.h> - -#define START_PORT 5060 -#define MAX_BUDDIES 32 -#define THIS_FILE "main.c" -#define MAX_PRESENTITY 32 -#define PRESENCE_TIMEOUT 60 - -/* By default we'll have one worker thread, except when threading - * is disabled. - */ -#if PJ_HAS_THREADS -# define WORKER_COUNT 1 -#else -# define WORKER_COUNT 0 -#endif - -/* Global variable. */ -static struct -{ - /* Control. */ - pj_pool_factory *pf; - pjsip_endpoint *endpt; - pj_pool_t *pool; - pjsip_user_agent *user_agent; - int worker_cnt; - int worker_quit_flag; - - /* User info. */ - char user_id[64]; - pj_str_t local_uri; - pj_str_t contact; - pj_str_t real_contact; - - /* Dialog. */ - pjsip_dlg *cur_dlg; - - /* Authentication. */ - int cred_count; - pjsip_cred_info cred_info[4]; - - /* Media stack. */ - pj_bool_t null_audio; - pj_med_mgr_t *mmgr; - - /* Misc. */ - int app_log_level; - char *log_filename; - FILE *log_file; - - /* Proxy URLs */ - pj_str_t proxy; - pj_str_t outbound_proxy; - - /* UA auto options. */ - int auto_answer; /* -1 to disable. */ - int auto_hangup; /* -1 to disable */ - - /* Registration. */ - pj_str_t registrar_uri; - pjsip_regc *regc; - pj_int32_t reg_timeout; - pj_timer_entry regc_timer; - - /* STUN */ - pj_str_t stun_srv1; - int stun_port1; - pj_str_t stun_srv2; - int stun_port2; - - /* UDP sockets and their public address. */ - int sip_port; - pj_sock_t sip_sock; - pj_sockaddr_in sip_sock_name; - pj_sock_t rtp_sock; - pj_sockaddr_in rtp_sock_name; - pj_sock_t rtcp_sock; - pj_sockaddr_in rtcp_sock_name; - - /* SIMPLE */ - pj_bool_t hide_status; - pj_bool_t offer_x_ms_msg; - int im_counter; - int buddy_cnt; - pj_str_t buddy[MAX_BUDDIES]; - pj_bool_t buddy_status[MAX_BUDDIES]; - pj_bool_t no_presence; - pjsip_presentity *buddy_pres[MAX_BUDDIES]; - - int pres_cnt; - pjsip_presentity *pres[MAX_PRESENTITY]; - -} global; - -enum { AUTO_ANSWER, AUTO_HANGUP }; - -/* This is the data that will be 'attached' on per dialog basis. */ -struct dialog_data -{ - /* Media session. */ - pj_media_session_t *msession; - - /* x-ms-chat session. */ - pj_bool_t x_ms_msg_session; - - /* Cached SDP body, updated when media session changed. */ - pjsip_msg_body *body; - - /* Timer. */ - pj_bool_t has_auto_timer; - pj_timer_entry auto_timer; -}; - -/* - * These are the callbacks to be registered to dialog to receive notifications - * about various events in the dialog. - */ -static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, - pjsip_event *event ); -static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata, int retransmission); -static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata); -static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata); -static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata); -static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata); -static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_event *event); -static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event); -static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event); -static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event); -static void dlg_on_terminated (pjsip_dlg *dlg); -static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event); - -/* The callback structure that will be registered to UA layer. */ -struct pjsip_dlg_callback dlg_callback = { - &dlg_on_all_events, - &dlg_on_before_tx, - &dlg_on_tx_msg, - &dlg_on_rx_msg, - &dlg_on_incoming, - &dlg_on_calling, - &dlg_on_provisional, - &dlg_on_connecting, - &dlg_on_established, - &dlg_on_disconnected, - &dlg_on_terminated, - &dlg_on_mid_call_evt -}; - - -/* - * Auxiliary things are put in misc.c, so that this main.c file is more - * readable. - */ -#include "misc.c" - -static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap, - struct pj_timer_entry *entry) -{ - pjsip_dlg *dlg = entry->user_data; - struct dialog_data *dlg_data = dlg->user_data; - - PJ_UNUSED_ARG(timer_heap) - - dlg_data->has_auto_timer = 0; - - if (entry->id == AUTO_ANSWER) { - pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200); - if (tdata) { - struct dialog_data *dlg_data = global.cur_dlg->user_data; - tdata->msg->body = dlg_data->body; - pjsip_dlg_send_msg(dlg, tdata); - } - } else { - pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500); - if (tdata) - pjsip_dlg_send_msg(dlg, tdata); - } -} - -static void update_registration(pjsip_regc *regc, int renew) -{ - pjsip_tx_data *tdata; - - PJ_LOG(3,(THIS_FILE, "Performing SIP registration...")); - - if (renew) { - tdata = pjsip_regc_register(regc, 1); - } else { - tdata = pjsip_regc_unregister(regc); - } - - pjsip_regc_send( regc, tdata ); -} - -static void regc_cb(struct pjsip_regc_cbparam *param) -{ - /* - * Print registration status. - */ - if (param->code < 0 || param->code >= 300) { - PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)", - param->code, pjsip_get_status_text(param->code)->ptr)); - global.regc = NULL; - - } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) { - PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), " - "will re-register in %d seconds", - param->code, - pjsip_get_status_text(param->code)->ptr, - param->expiration)); - - } else { - PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code)); - } -} - -static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata, - int *timeout) -{ - int state; - int i; - char url[PJSIP_MAX_URL_SIZE]; - int urllen; - - PJ_UNUSED_ARG(rdata) - - if (*timeout > 0) { - state = PJSIP_EVENT_SUB_STATE_ACTIVE; - if (*timeout > 300) - *timeout = 300; - } else { - state = PJSIP_EVENT_SUB_STATE_TERMINATED; - } - - urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1); - if (urllen < 1) { - pj_native_strcpy(url, "<unknown>"); - } else { - url[urllen] = '\0'; - } - PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s", - url, - (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated"))); - - for (i=0; i<global.pres_cnt; ++i) - if (global.pres[i] == pres) - break; - if (i == global.pres_cnt) - global.pres[global.pres_cnt++] = pres; - - pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info ); - pjsip_presence_notify(pres, state, !global.hide_status); - -} - -static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata) -{ - pres_on_received_request(pres, rdata, &pres->sub->default_interval); -} - -/* This is called by presence framework when we receives presence update - * of a resource (buddy). - */ -static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open) -{ - int buddy_index = (int)pres->user_data; - - global.buddy_status[buddy_index] = is_open; - PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s", - global.buddy[buddy_index].ptr, - (is_open ? "Online" : "Offline"))); -} - -/* This is called when the subscription is terminated. */ -static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason) -{ - if (pres->sub->role == PJSIP_ROLE_UAC) { - int buddy_index = (int)pres->user_data; - PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)", - global.buddy[buddy_index].ptr, - reason->slen, reason->ptr)); - global.buddy_pres[buddy_index] = NULL; - global.buddy_status[buddy_index] = 0; - } else { - int i; - PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)", - reason->slen, reason->ptr)); - pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1); - for (i=0; i<global.pres_cnt; ++i) { - if (global.pres[i] == pres) { - int j; - global.pres[i] = NULL; - for (j=i+1; j<global.pres_cnt; ++j) - global.pres[j-1] = global.pres[j]; - global.pres_cnt--; - break; - } - } - } - pjsip_presence_destroy(pres); -} - - -/* Callback attached to SIP body to print the body to message buffer. */ -static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) -{ - pjsip_msg_body *body = msg_body; - return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size); -} - -/* When media session has changed, call this function to update the cached body - * information in the dialog. - */ -static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg) -{ - struct dialog_data *dlg_data = dlg->user_data; - pjsdp_session_desc *sdp; - - sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg); - if (!sdp) { - dlg_data->body = NULL; - return NULL; - } - - /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new - * "m=" line in the SDP. - */ - if (dlg_data->x_ms_msg_session >= 0 && - dlg_data->x_ms_msg_session >= (int)sdp->media_count) - { - pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m)); - sdp->media[sdp->media_count] = m; - dlg_data->x_ms_msg_session = sdp->media_count++; - } - - /* - * For "x-ms-message" line, remove all attributes and connection line etc. - */ - if (dlg_data->x_ms_msg_session >= 0) { - pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session]; - if (m) { - m->desc.media = pj_str("x-ms-message"); - m->desc.port = 5060; - m->desc.transport = pj_str("sip"); - m->desc.fmt_count = 1; - m->desc.fmt[0] = pj_str("null"); - m->attr_count = 0; - m->conn = NULL; - } - } - - dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body)); - dlg_data->body->content_type.type = pj_str("application"); - dlg_data->body->content_type.subtype = pj_str("sdp"); - dlg_data->body->len = 0; /* ignored */ - dlg_data->body->print_body = &print_msg_body; - - dlg_data->body->data = sdp; - return dlg_data->body; -} - -/* This callback will be called on every occurence of events in dialogs */ -static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, - pjsip_event *event ) -{ - PJ_UNUSED_ARG(dlg_evt) - PJ_UNUSED_ARG(event) - - PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg)); -} - -/* This callback is called before each outgoing msg is sent (including - * retransmission). Application can override this notification if it wants - * to modify the message before transmission or if it wants to do something - * else for each transmission. - */ -static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata, int ret_cnt) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(tdata) - - if (ret_cnt > 0) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)", - dlg->obj_name, ret_cnt)); - } -} - -/* This callback is called after a message is sent. */ -static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(tdata) - - PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); -} - -/* This callback is called on receipt of incoming message. */ -static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(rdata) - PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); -} - -/* This callback is called after dialog has sent INVITE */ -static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_tx_data *tdata) -{ - PJ_UNUSED_ARG(tsx) - PJ_UNUSED_ARG(tdata) - - pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG && - tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && - tsx->method.id == PJSIP_INVITE_METHOD); - - PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name)); -} - -static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp) -{ - struct dialog_data *dlg_data = dlg->user_data; - pj_bool_t sdp_x_ms_msg_index = -1; - int i; - int mcnt; - const pj_media_stream_info *mi[PJSDP_MAX_MEDIA]; - int has_active; - pj_media_sock_info sock_info; - - /* Find "m=x-ms-message" line in the SDP. */ - for (i=0; i<(int)sdp->media_count; ++i) { - if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0) - sdp_x_ms_msg_index = i; - } - - /* - * Create media session. - */ - pj_memset(&sock_info, 0, sizeof(sock_info)); - sock_info.rtp_sock = global.rtp_sock; - sock_info.rtcp_sock = global.rtcp_sock; - pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); - - dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info); - - /* A session will always be created, unless there is memory - * alloc problem. - */ - pj_assert(dlg_data->msession); - - /* See if we can take the offer by checking that we have at least - * one media stream active. - */ - mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi); - for (i=0, has_active=0; i<mcnt; ++i) { - if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) { - has_active = 1; - break; - } - } - - if (!has_active && sdp_x_ms_msg_index==-1) { - pjsip_tx_data *tdata; - - /* Unable to accept remote's SDP. - * Answer with 488 (Not Acceptable Here) - */ - /* Create 488 response. */ - tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE); - - /* Send response. */ - if (tdata) - pjsip_dlg_send_msg(dlg, tdata); - return; - } - - dlg_data->x_ms_msg_session = sdp_x_ms_msg_index; - - /* Create msg body to be used later in 2xx/response */ - create_msg_body(dlg, 0); - -} - -/* This callback is called after an INVITE is received. */ -static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_rx_data *rdata) -{ - struct dialog_data *dlg_data; - pjsip_msg *msg; - pjsip_tx_data *tdata; - char buf[128]; - int len; - - PJ_UNUSED_ARG(tsx) - - pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG && - rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && - tsx->method.id == PJSIP_INVITE_METHOD); - - /* - * Notify user! - */ - PJ_LOG(3, (THIS_FILE, "")); - PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name)); - PJ_LOG(3, (THIS_FILE, "")); - len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, - (pjsip_name_addr*)dlg->remote.info->uri, - buf, sizeof(buf)-1); - if (len > 0) { - buf[len] = '\0'; - PJ_LOG(3,(THIS_FILE, "From:\t%s", buf)); - } - len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR, - (pjsip_name_addr*)dlg->local.info->uri, - buf, sizeof(buf)-1); - if (len > 0) { - buf[len] = '\0'; - PJ_LOG(3,(THIS_FILE, "To:\t%s", buf)); - } - PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name)); - PJ_LOG(3, (THIS_FILE, "")); - - /* - * Process incoming dialog. - */ - - dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); - dlg->user_data = dlg_data; - - /* Update contact. */ - pjsip_dlg_set_contact(dlg, &global.contact); - - /* Initialize credentials. */ - pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); - - /* Create media session if the request has "application/sdp" body. */ - msg = rdata->msg; - if (msg->body && - pj_stricmp2(&msg->body->content_type.type, "application")==0 && - pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0) - { - pjsdp_session_desc *sdp; - - /* Parse SDP body, and instantiate media session based on remote's SDP. - * Then create our SDP body from the session. - */ - sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool); - if (!sdp) - goto send_answer; - - create_session_from_sdp(dlg, sdp); - - } else if (msg->body) { - /* The request has a message body other than "application/sdp" */ - pjsip_accept_hdr *accept; - - /* Create response. */ - tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); - - /* Add "Accept" header. */ - accept = pjsip_accept_hdr_create(tdata->pool); - accept->values[0] = pj_str("application/sdp"); - accept->count = 1; - pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept); - - /* Send response. */ - pjsip_dlg_send_msg(dlg, tdata); - return; - - } else { - /* The request has no message body. We can take this request, but - * no media session will be activated. - */ - /* Nothing to do here. */ - } - -send_answer: - /* Immediately answer with 100 (or 180? */ - tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING ); - pjsip_dlg_send_msg(dlg, tdata); - - /* Set current dialog to this dialog if we don't currently have - * current dialog. - */ - if (global.cur_dlg == NULL) { - global.cur_dlg = dlg; - } - - /* Auto-answer if option is specified. */ - if (global.auto_answer >= 0) { - pj_time_val delay = { 0, 0}; - struct dialog_data *dlg_data = dlg->user_data; - - PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds", - global.auto_answer)); - - delay.sec = global.auto_answer; - dlg_data->auto_timer.user_data = dlg; - dlg_data->auto_timer.id = AUTO_ANSWER; - dlg_data->auto_timer.cb = &dlg_auto_timer_callback; - dlg_data->has_auto_timer = 1; - pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); - } -} - -/* This callback is called when dialog has sent/received a provisional response - * to INVITE. - */ -static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx, - pjsip_event *event) -{ - const char *action; - - pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.tdata->msg->line.status.code/100 == 1 && - tsx->method.id == PJSIP_INVITE_METHOD) - || - (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.rdata->msg->line.status.code/100 == 1 && - tsx->method.id == PJSIP_INVITE_METHOD)); - - if (event->src_type == PJSIP_EVENT_TX_MSG) - action = "Sending"; - else - action = "Received"; - - PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)", - dlg->obj_name, action, tsx->status_code, - pjsip_get_status_text(tsx->status_code)->ptr)); -} - -/* This callback is called when 200 response to INVITE is sent/received. */ -static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event) -{ - struct dialog_data *dlg_data = dlg->user_data; - const char *action; - - pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.tdata->msg->line.status.code/100 == 2) - || - (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && - event->src.rdata->msg->line.status.code/100 == 2)); - - if (event->src_type == PJSIP_EVENT_RX_MSG) - action = "Received"; - else - action = "Sending"; - - PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action)); - - if (event->src_type == PJSIP_EVENT_RX_MSG) { - /* On receipt of 2xx response, negotiate our media capability - * and start media. - */ - pjsip_msg *msg = event->src.rdata->msg; - pjsip_msg_body *body; - pjsdp_session_desc *sdp; - - /* Get SDP from message. */ - - /* Ignore if no SDP body is present. */ - body = msg->body; - if (!body) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!", - dlg->obj_name)); - return; - } - - if (pj_stricmp2(&body->content_type.type, "application") != 0 && - pj_stricmp2(&body->content_type.subtype, "sdp") != 0) - { - PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!", - dlg->obj_name)); - return; - } - - /* Got what seems to be a SDP content. Parse it. */ - sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool); - if (!sdp) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!", - dlg->obj_name)); - return; - } - - /* Negotiate media session with remote's media capability. */ - if (pj_media_session_update (dlg_data->msession, sdp) != 0) { - PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!", - dlg->obj_name)); - return; - } - - /* Update the saved SDP body because media session has changed. - * Also set ack flag to '1', because we only want to send one format/ - * codec for each media streams. - */ - create_msg_body(dlg, 1); - - /* Activate media. */ - pj_media_session_activate (dlg_data->msession); - - } else { - pjsip_msg *msg = event->src.tdata->msg; - - if (msg->body) { - /* On transmission of 2xx response, start media session. */ - pj_media_session_activate (dlg_data->msession); - } - } - -} - -/* This callback is called when ACK to initial INVITE is sent/received. */ -static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event) -{ - const char *action; - - pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->type == PJSIP_REQUEST_MSG && - event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) - || - (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_REQUEST_MSG && - event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD)); - - if (event->src_type == PJSIP_EVENT_RX_MSG) - action = "Received"; - else - action = "Sending"; - - PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED", - dlg->obj_name, action)); - - /* Attach SDP body for outgoing ACK. */ - if (event->src_type == PJSIP_EVENT_TX_MSG && - event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) - { - struct dialog_data *dlg_data = dlg->user_data; - event->src.tdata->msg->body = dlg_data->body; - } - - /* Auto-hangup if option is specified. */ - if (global.auto_hangup >= 0) { - pj_time_val delay = { 0, 0}; - struct dialog_data *dlg_data = dlg->user_data; - - PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds", - global.auto_hangup)); - - delay.sec = global.auto_hangup; - dlg_data->auto_timer.user_data = dlg; - dlg_data->auto_timer.id = AUTO_HANGUP; - dlg_data->auto_timer.cb = &dlg_auto_timer_callback; - dlg_data->has_auto_timer = 1; - pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); - } -} - - -/* This callback is called when dialog is disconnected (because of final - * response, BYE, or timer). - */ -static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event) -{ - struct dialog_data *dlg_data = dlg->user_data; - int status_code; - const pj_str_t *reason; - - PJ_UNUSED_ARG(event) - - /* Cancel auto-answer/auto-hangup timer. */ - if (dlg_data->has_auto_timer) { - pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer); - dlg_data->has_auto_timer = 0; - } - - if (dlg->invite_tsx) - status_code = dlg->invite_tsx->status_code; - else - status_code = 200; - - if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) { - if (event->src_type == PJSIP_EVENT_RX_MSG) - reason = &event->src.rdata->msg->line.status.reason; - else if (event->src_type == PJSIP_EVENT_TX_MSG) - reason = &event->src.tdata->msg->line.status.reason; - else - reason = pjsip_get_status_text(event->obj.tsx->status_code); - } else { - reason = &event->obj.tsx->method.name; - } - - PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)", - dlg->obj_name, status_code, - reason->slen, reason->ptr)); - - if (dlg_data->msession) { - pj_media_session_destroy (dlg_data->msession); - dlg_data->msession = NULL; - } -} - -/* This callback is called when dialog is about to be destroyed. */ -static void dlg_on_terminated(pjsip_dlg *dlg) -{ - PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name)); - - /* If current dialog is equal to this dialog, update it. */ - if (global.cur_dlg == dlg) { - global.cur_dlg = global.cur_dlg->next; - if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { - global.cur_dlg = NULL; - } - } -} - -/* This callback is called for any requests when dialog is established. */ -static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event) -{ - pjsip_transaction *tsx = event->obj.tsx; - - if (event->src_type == PJSIP_EVENT_RX_MSG && - event->src.rdata->msg->type == PJSIP_REQUEST_MSG) - { - if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) { - /* Re-invitation. */ - pjsip_tx_data *tdata; - - PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)", - dlg->obj_name)); - tdata = pjsip_dlg_answer(dlg, 200); - if (tdata) { - struct dialog_data *dlg_data = dlg->user_data; - tdata->msg->body = dlg_data->body; - pjsip_dlg_send_msg(dlg, tdata); - } - } else { - /* Don't worry, endpoint will answer with 500 or whetever. */ - } - - } else if (tsx->status_code/100 == 2) { - PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)", - dlg->obj_name, - tsx->method.name.slen, tsx->method.name.ptr, - tsx->status_code, - pjsip_get_status_text(tsx->status_code)->ptr)); - - - } else if (tsx->status_code >= 300) { - pj_bool_t report_failure = PJ_TRUE; - - /* Check for authentication failures. */ - if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( global.endpt, - dlg->pool, &dlg->auth_sess, - dlg->cred_count, dlg->cred_info, - tsx->last_tx, event->src.rdata ); - if (tdata) { - int rc; - rc = pjsip_dlg_send_msg( dlg, tdata); - report_failure = (rc != 0); - } - } - if (report_failure) { - const pj_str_t *reason; - if (event->src_type == PJSIP_EVENT_RX_MSG) { - reason = &event->src.rdata->msg->line.status.reason; - } else { - reason = pjsip_get_status_text(tsx->status_code); - } - PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)", - dlg->obj_name, tsx->status_code, - reason->slen, reason->ptr)); - } - } -} - -/* Initialize sockets and optionally get the public address via STUN. */ -static pj_status_t init_sockets() -{ - enum { - RTP_START_PORT = 4000, - RTP_RANDOM_START = 2, - RTP_RETRY = 10 - }; - enum { - SIP_SOCK, - RTP_SOCK, - RTCP_SOCK, - }; - int i; - int rtp_port; - pj_sock_t sock[3]; - pj_sockaddr_in mapped_addr[3]; - - for (i=0; i<3; ++i) - sock[i] = PJ_INVALID_SOCKET; - - /* Create and bind SIP UDP socket. */ - sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); - if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) { - PJ_LOG(2,(THIS_FILE, "Unable to create socket")); - goto on_error; - } - if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) { - PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port")); - goto on_error; - } - - /* Initialize start of RTP port to try. */ - rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2; - - /* Loop retry to bind RTP and RTCP sockets. */ - for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) { - - /* Create and bind RTP socket. */ - sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); - if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) - goto on_error; - if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) { - pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; - continue; - } - - /* Create and bind RTCP socket. */ - sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); - if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET) - goto on_error; - if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) { - pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; - pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET; - continue; - } - - /* - * If we're configured to use STUN, then find out the mapped address, - * and make sure that the mapped RTCP port is adjacent with the RTP. - */ - if (global.stun_port1 == 0) { - pj_str_t hostname; - pj_sockaddr_in addr; - - /* Get local IP address. */ - char hostname_buf[PJ_MAX_HOSTNAME]; - if (gethostname(hostname_buf, sizeof(hostname_buf))) - goto on_error; - hostname = pj_str(hostname_buf); - - pj_memset( &addr, 0, sizeof(addr)); - addr.sin_family = PJ_AF_INET; - if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_SUCCESS) - goto on_error; - - for (i=0; i<3; ++i) - pj_memcpy(&mapped_addr[i], &addr, sizeof(addr)); - - mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port); - mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port); - mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1)); - break; - } else { - pj_status_t rc; - rc = pj_stun_get_mapped_addr( global.pf, 3, sock, - &global.stun_srv1, global.stun_port1, - &global.stun_srv2, global.stun_port2, - mapped_addr); - if (rc != 0) { - PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc))); - goto on_error; - } - - if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1) - break; - - pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; - pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET; - } - } - - if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) { - PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination")); - goto on_error; - } - - global.sip_sock = sock[SIP_SOCK]; - pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in)); - global.rtp_sock = sock[RTP_SOCK]; - pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in)); - global.rtcp_sock = sock[RTCP_SOCK]; - pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in)); - - PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d", - pj_inet_ntoa(global.sip_sock_name.sin_addr), - pj_ntohs(global.sip_sock_name.sin_port))); - PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d", - pj_inet_ntoa(global.rtp_sock_name.sin_addr), - pj_ntohs(global.rtp_sock_name.sin_port))); - PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d", - pj_inet_ntoa(global.rtcp_sock_name.sin_addr), - pj_ntohs(global.rtcp_sock_name.sin_port))); - return 0; - -on_error: - for (i=0; i<3; ++i) { - if (sock[i] != PJ_INVALID_SOCKET) - pj_sock_close(sock[i]); - } - return -1; -} - -static void log_function(int level, const char *buffer, int len) -{ - /* Write to both stdout and file. */ - if (level <= global.app_log_level) - pj_log_to_stdout(level, buffer, len); - if (global.log_file) { - fwrite(buffer, len, 1, global.log_file); - fflush(global.log_file); - } -} - -/* Initialize stack. */ -static pj_status_t init_stack() -{ - pj_status_t status; - pj_sockaddr_in bind_addr; - pj_sockaddr_in bind_name; - const char *local_addr; - static char local_uri[128]; - - /* Optionally set logging file. */ - if (global.log_filename) { - global.log_file = fopen(global.log_filename, "wt"); - } - - /* Initialize endpoint. This will also call initialization to all the - * modules. - */ - global.endpt = pjsip_endpt_create(global.pf); - if (global.endpt == NULL) { - return -1; - } - - /* Set dialog callback. */ - pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback); - - /* Init listener's bound address and port. */ - pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port); - pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port); - - /* Add UDP transport listener. */ - status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock, - &global.sip_sock_name); - if (status != 0) - return -1; - - local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr); - -#if PJ_HAS_TCP - /* Add TCP transport listener. */ - status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP, - &bind_addr, &bind_name); - if (status != 0) - return -1; -#endif - - /* Determine user_id to be put in Contact */ - if (global.local_uri.slen) { - pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL); - pjsip_uri *uri; - - uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0); - if (uri) { - if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) { - pjsip_url *url = (pjsip_url*)pjsip_uri_get_uri(uri); - if (url->user.slen) - strncpy(global.user_id, url->user.ptr, url->user.slen); - } - } - pj_pool_release(pool); - } - - if (global.user_id[0]=='\0') { - pj_native_strcpy(global.user_id, "user"); - } - - /* build contact */ - global.real_contact.ptr = local_uri; - global.real_contact.slen = - sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port); - - if (global.contact.slen == 0) - global.contact = global.real_contact; - - /* initialize local_uri with contact if it's not specified in cmdline */ - if (global.local_uri.slen == 0) - global.local_uri = global.contact; - - /* Init proxy. */ - if (global.proxy.slen || global.outbound_proxy.slen) { - int count = 0; - pj_str_t proxy_url[2]; - - if (global.outbound_proxy.slen) { - proxy_url[count++] = global.outbound_proxy; - } - if (global.proxy.slen) { - proxy_url[count++] = global.proxy; - } - - if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) { - PJ_LOG(2,(THIS_FILE, "Error setting proxy address!")); - return -1; - } - } - - /* initialize SIP registration if registrar is configured */ - if (global.registrar_uri.slen) { - global.regc = pjsip_regc_create( global.endpt, NULL, ®c_cb); - pjsip_regc_init( global.regc, &global.registrar_uri, - &global.local_uri, - &global.local_uri, - 1, &global.contact, - global.reg_timeout); - pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info ); - } - - return PJ_SUCCESS; -} - -/* Worker thread function, only used when threading is enabled. */ -static void *PJ_THREAD_FUNC worker_thread(void *unused) -{ - PJ_UNUSED_ARG(unused) - - while (!global.worker_quit_flag) { - pj_time_val timeout = { 0, 10 }; - pjsip_endpt_handle_events (global.endpt, &timeout); - } - return NULL; -} - - -/* Make call to the specified URI. */ -static pjsip_dlg *make_call(pj_str_t *remote_uri) -{ - pjsip_dlg *dlg; - pj_str_t local = global.contact; - pj_str_t remote = *remote_uri; - struct dialog_data *dlg_data; - pjsip_tx_data *tdata; - pj_media_sock_info sock_info; - - /* Create new dialog instance. */ - dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC); - - /* Attach our own user data. */ - dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); - dlg->user_data = dlg_data; - - /* Create media session. */ - pj_memset(&sock_info, 0, sizeof(sock_info)); - sock_info.rtp_sock = global.rtp_sock; - sock_info.rtcp_sock = global.rtcp_sock; - pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); - - dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info); - dlg_data->x_ms_msg_session = -1; - - if (global.offer_x_ms_msg) { - const pj_media_stream_info *minfo[32]; - unsigned cnt; - - cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo); - if (cnt > 0) - dlg_data->x_ms_msg_session = cnt; - } - - /* Initialize dialog with local and remote URI. */ - if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) { - pjsip_ua_destroy_dialog(dlg); - return NULL; - } - - /* Initialize credentials. */ - pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); - - /* Send INVITE! */ - tdata = pjsip_dlg_invite(dlg); - tdata->msg->body = create_msg_body (dlg, 0); - - if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) { - pjsip_ua_destroy_dialog(dlg); - return NULL; - } - - return dlg; -} - -/* - * Callback to receive incoming IM message. - */ -static int on_incoming_im_msg(pjsip_rx_data *rdata) -{ - pjsip_msg *msg = rdata->msg; - pjsip_msg_body *body = msg->body; - int len; - char to[128], from[128]; - - - len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, - rdata->from->uri, from, sizeof(from)); - if (len > 0) from[len] = '\0'; - else pj_native_strcpy(from, "<URL too long..>"); - - len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR, - rdata->to->uri, to, sizeof(to)); - if (len > 0) to[len] = '\0'; - else pj_native_strcpy(to, "<URL too long..>"); - - PJ_LOG(3,(THIS_FILE, "Incoming instant message:")); - - printf("----- BEGIN INSTANT MESSAGE ----->\n"); - printf("From:\t%s\n", from); - printf("To:\t%s\n", to); - printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : "")); - printf("<------ END INSTANT MESSAGE ------\n"); - - fflush(stdout); - - /* Must answer with final response. */ - return 200; -} - -/* - * Input URL. - */ -static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection) -{ - int i; - - *selection = -1; - - printf("\nBuddy list:\n"); - printf("---------------------------------------\n"); - for (i=0; i<global.buddy_cnt; ++i) { - printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr, - (global.buddy_status[i]?"Online":"Offline")); - } - printf("-------------------------------------\n"); - - printf("Choices\n" - "\t0 For current dialog.\n" - "\t[1-%02d] Select from buddy list\n" - "\tURL An URL\n" - , global.buddy_cnt); - printf("Input: "); - - fflush(stdout); - fgets(buf, len, stdin); - buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */ - - while (isspace(*buf)) ++buf; - - if (!*buf || *buf=='\n' || *buf=='\r') - return NULL; - - i = atoi(buf); - - if (i == 0) { - if (isdigit(*buf)) { - *selection = 0; - *out = pj_str("0"); - return out; - } else { - if (verify_sip_url(buf) != 0) { - puts("Invalid URL specified!"); - return NULL; - } - *out = pj_str(buf); - return out; - } - } else if (i > global.buddy_cnt || i < 0) { - printf("Error: invalid selection!\n"); - return NULL; - } else { - *out = global.buddy[i-1]; - *selection = i; - return out; - } -} - - -static void generic_request_callback( void *token, pjsip_event *event ) -{ - pjsip_transaction *tsx = event->obj.tsx; - - PJ_UNUSED_ARG(token) - - if (tsx->status_code/100 == 2) { - PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)", - event->obj.tsx->method.name.slen, - event->obj.tsx->method.name.ptr, - tsx->status_code, - pjsip_get_status_text(tsx->status_code)->ptr)); - } else if (tsx->status_code==401 || tsx->status_code==407) { - pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( global.endpt, - global.pool, NULL, global.cred_count, global.cred_info, - tsx->last_tx, event->src.rdata); - if (tdata) { - int rc; - pjsip_cseq_hdr *cseq; - cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); - cseq->cseq++; - rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, - &generic_request_callback); - if (rc == 0) - return; - } - PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)", - event->obj.tsx->method.name.slen, - event->obj.tsx->method.name.ptr, - event->obj.tsx->status_code, - pjsip_get_status_text(event->obj.tsx->status_code)->ptr)); - } else { - const pj_str_t *reason; - if (event->src_type == PJSIP_EVENT_RX_MSG) - reason = &event->src.rdata->msg->line.status.reason; - else - reason = pjsip_get_status_text(tsx->status_code); - PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)", - event->obj.tsx->method.name.slen, - event->obj.tsx->method.name.ptr, - event->obj.tsx->status_code, - reason->slen, reason->ptr)); - } -} - - -static void ui_send_im_message() -{ - char line[100]; - char text_buf[100]; - pj_str_t str; - pj_str_t text_msg; - int selection, rc; - pjsip_tx_data *tdata; - - if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) - return; - - - printf("Enter text to send (empty to cancel): "); fflush(stdout); - fgets(text_buf, sizeof(text_buf), stdin); - text_buf[strlen(text_buf)-1] = '\0'; - if (!*text_buf) - return; - - text_msg = pj_str(text_buf); - - if (selection==0) { - pjsip_method message_method; - pj_str_t str_MESSAGE = { "MESSAGE", 7 }; - - /* Send IM to current dialog. */ - if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { - printf("No current dialog or dialog state is not ESTABLISHED!\n"); - return; - } - - pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE); - tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 ); - - if (tdata) { - /* Create message body for the text. */ - pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body)); - body->content_type.type = pj_str("text"); - body->content_type.subtype = pj_str("plain"); - body->data = pj_pool_alloc(tdata->pool, text_msg.slen); - pj_memcpy(body->data, text_msg.ptr, text_msg.slen); - body->len = text_msg.slen; - body->print_body = &pjsip_print_text_body; - - /* Assign body to message, and send the message! */ - tdata->msg->body = body; - pjsip_dlg_send_msg( global.cur_dlg, tdata ); - } - - } else { - /* Send IM to buddy list. */ - pjsip_method message; - static pj_str_t MESSAGE = { "MESSAGE", 7 }; - pjsip_method_init_np(&message, &MESSAGE); - tdata = pjsip_endpt_create_request(global.endpt, &message, - &str, - &global.real_contact, - &str, &global.real_contact, NULL, -1, - &text_msg); - if (!tdata) { - puts("Error creating request"); - return; - } - rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback); - if (rc == 0) { - printf("Sending IM message %d\n", global.im_counter); - ++global.im_counter; - } else { - printf("Error: unable to send IM message!\n"); - } - } -} - -static void ui_send_options() -{ - char line[100]; - pj_str_t str; - int selection, rc; - pjsip_tx_data *tdata; - pjsip_method options; - - if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) - return; - - pjsip_method_set( &options, PJSIP_OPTIONS_METHOD ); - - if (selection == 0) { - /* Send OPTIONS to current dialog. */ - tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1); - if (tdata) - pjsip_dlg_send_msg( global.cur_dlg, tdata ); - } else { - /* Send OPTIONS to arbitrary party. */ - tdata = pjsip_endpt_create_request( global.endpt, &options, - &str, - &global.local_uri, &str, - &global.real_contact, - NULL, -1, NULL); - if (tdata) { - rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL, - &generic_request_callback); - if (rc != 0) - PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!")); - } - } -} - -static void init_presence() -{ - const pjsip_presence_cb pres_cb = { - NULL, - &pres_on_received_request, - &pres_on_received_refresh, - &pres_on_received_update, - &pres_on_terminated - }; - - pjsip_presence_init(&pres_cb); -} - -/* Subscribe presence information for all buddies. */ -static void subscribe_buddies_presence() -{ - int i; - for (i=0; i<global.buddy_cnt; ++i) { - pjsip_presentity *pres; - if (global.buddy_pres[i]) - continue; - pres = pjsip_presence_create( global.endpt, &global.local_uri, - &global.buddy[i], PRESENCE_TIMEOUT, (void*)i); - if (pres) { - pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info ); - pjsip_presence_subscribe( pres ); - } - global.buddy_pres[i] = pres; - } -} - -/* Unsubscribe presence information for all buddies. */ -static void unsubscribe_buddies_presence() -{ - int i; - for (i=0; i<global.buddy_cnt; ++i) { - pjsip_presentity *pres = global.buddy_pres[i]; - if (pres) { - pjsip_presence_unsubscribe(pres); - pjsip_presence_destroy(pres); - global.buddy_pres[i] = NULL; - } - } -} - -/* Unsubscribe presence. */ -static void unsubscribe_presence() -{ - int i; - - unsubscribe_buddies_presence(); - for (i=0; i<global.pres_cnt; ++i) { - pjsip_presentity *pres = global.pres[i]; - pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0); - pjsip_presence_destroy( pres ); - } -} - -/* Advertise online status to subscribers. */ -static void update_im_status() -{ - int i; - for (i=0; i<global.pres_cnt; ++i) { - pjsip_presentity *pres = global.pres[i]; - pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE, - !global.hide_status); - } -} - -/* - * Main program. - */ -int main(int argc, char *argv[]) -{ - /* set to WORKER_COUNT+1 to avoid zero size warning - * when threading is disabled. */ - pj_thread_t *thread[WORKER_COUNT+1]; - pj_caching_pool cp; - int i; - - global.sip_port = 5060; - global.auto_answer = -1; - global.auto_hangup = -1; - global.app_log_level = 3; - - pj_log_set_level(4); - pj_log_set_log_func(&log_function); - - /* Init PJLIB */ - if (pj_init() != PJ_SUCCESS) - return 1; - - /* Init caching pool. */ - pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); - global.pf = &cp.factory; - - /* Create memory pool for application. */ - global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL); - - /* Parse command line arguments. */ - if (parse_args(global.pool, argc, argv) != PJ_SUCCESS) { - pj_caching_pool_destroy(&cp); - return 1; - } - - /* Init sockets */ - if (init_sockets() != 0) { - pj_caching_pool_destroy(&cp); - return 1; - } - - /* Initialize stack. */ - if (init_stack() != PJ_SUCCESS) { - pj_caching_pool_destroy(&cp); - return 1; - } - - /* Set callback to receive incoming IM */ - pjsip_messaging_set_incoming_callback( &on_incoming_im_msg ); - - /* Set default worker count (can be zero) */ - global.worker_cnt = WORKER_COUNT; - - /* Create user worker thread(s), only when threading is enabled. */ - for (i=0; i<global.worker_cnt; ++i) { - thread[i] = pj_thread_create( global.pool, "sip%p", - &worker_thread, - NULL, 0, NULL, 0); - if (thread == NULL) { - global.worker_quit_flag = 1; - for (--i; i>=0; --i) { - pj_thread_join(thread[i]); - pj_thread_destroy(thread[i]); - } - pj_caching_pool_destroy(&cp); - return 1; - } - } - - printf("Worker thread count: %d\n", global.worker_cnt); - - /* Perform registration, if required. */ - if (global.regc) { - update_registration(global.regc, 1); - } - - /* Initialize media manager. */ - global.mmgr = pj_med_mgr_create(global.pf); - - /* Init presence. */ - init_presence(); - - /* Subscribe presence information of all buddies. */ - if (!global.no_presence) - subscribe_buddies_presence(); - - /* Initializatio completes, loop waiting for commands. */ - for (;!global.worker_quit_flag;) { - pj_str_t str; - char line[128]; - -#if WORKER_COUNT==0 - /* If worker thread does not exist, main thread must poll for evetns. - * But this won't work very well since main thread is blocked by - * fgets(). So keep pressing the ENTER key to get the events! - */ - pj_time_val timeout = { 0, 100 }; - pjsip_endpt_handle_events(global.endpt, &timeout); - puts("Keep pressing ENTER key to get the events!"); -#endif - - printf("\nCurrent dialog: "); - print_dialog(global.cur_dlg); - puts(""); - - keystroke_help(); - - fgets(line, sizeof(line), stdin); - - switch (*line) { - case 'm': - puts("Make outgoing call"); - if (ui_input_url(&str, line, sizeof(line), &i) != NULL) { - pjsip_dlg *dlg = make_call(&str); - if (global.cur_dlg == NULL) { - global.cur_dlg = dlg; - } - } - break; - case 'i': - puts("Send Instant Messaging"); - ui_send_im_message(); - break; - case 'o': - puts("Send OPTIONS"); - ui_send_options(); - break; - case 'a': - if (global.cur_dlg) { - unsigned code; - pjsip_tx_data *tdata; - struct dialog_data *dlg_data = global.cur_dlg->user_data; - - printf("Answer with status code (1xx-6xx): "); - fflush(stdout); - fgets(line, sizeof(line), stdin); - str = pj_str(line); - str.slen -= 1; - - code = pj_strtoul(&str); - tdata = pjsip_dlg_answer(global.cur_dlg, code); - if (tdata) { - if (code/100 == 2) { - tdata->msg->body = dlg_data->body; - } - pjsip_dlg_send_msg(global.cur_dlg, tdata); - - } - } else { - puts("No current dialog"); - } - break; - case 'h': - if (global.cur_dlg) { - pjsip_tx_data *tdata; - tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE); - if (tdata) { - pjsip_dlg_send_msg(global.cur_dlg, tdata); - } - } else { - puts("No current dialog"); - } - break; - case ']': - if (global.cur_dlg) { - global.cur_dlg = global.cur_dlg->next; - if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { - global.cur_dlg = global.cur_dlg->next; - } - } else { - puts("No current dialog"); - } - break; - case '[': - if (global.cur_dlg) { - global.cur_dlg = global.cur_dlg->prev; - if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { - global.cur_dlg = global.cur_dlg->prev; - } - } else { - puts("No current dialog"); - } - break; - case 'd': - pjsip_endpt_dump(global.endpt, *(line+1)=='1'); - pjsip_ua_dump(global.user_agent); - break; - case 's': - if (*(line+1) == 'u') - subscribe_buddies_presence(); - break; - case 'u': - if (*(line+1) == 's') - unsubscribe_presence(); - break; - case 't': - global.hide_status = !global.hide_status; - update_im_status(); - break; - case 'q': - goto on_exit; - case 'l': - print_all_dialogs(); - break; - } - } - -on_exit: - /* Unregister, if required. */ - if (global.regc) { - update_registration(global.regc, 0); - } - - /* Unsubscribe presence. */ - unsubscribe_presence(); - - /* Allow one second to get all events. */ - if (1) { - pj_time_val end_time; - - pj_gettimeofday(&end_time); - end_time.sec++; - - PJ_LOG(3,(THIS_FILE, "Shutting down..")); - for (;;) { - pj_time_val timeout = { 0, 20 }, now; - pjsip_endpt_handle_events (global.endpt, &timeout); - pj_gettimeofday(&now); - PJ_TIME_VAL_SUB(now, end_time); - if (now.sec >= 1) - break; - } - } - - global.worker_quit_flag = 1; - - pj_med_mgr_destroy(global.mmgr); - - /* Wait all threads to quit. */ - for (i=0; i<global.worker_cnt; ++i) { - pj_thread_join(thread[i]); - pj_thread_destroy(thread[i]); - } - - /* Destroy endpoint. */ - pjsip_endpt_destroy(global.endpt); - - /* Destroy caching pool. */ - pj_caching_pool_destroy(&cp); - - /* Close log file, if any. */ - if (global.log_file) - fclose(global.log_file); - - return 0; -} - -/* - * Register static modules to the endpoint. - */ -pj_status_t register_static_modules( pj_size_t *count, - pjsip_module **modules ) -{ - /* Reset count. */ - *count = 0; - - /* Register user agent module. */ - modules[(*count)++] = pjsip_ua_get_module(); - global.user_agent = modules[0]->mod_data; - modules[(*count)++] = pjsip_messaging_get_module(); - modules[(*count)++] = pjsip_event_sub_get_module(); - - return PJ_SUCCESS; -} +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <pjlib.h>
+#include <pjsip_core.h>
+#include <pjsip_ua.h>
+#include <pjsip_simple.h>
+#include <pjmedia.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <pj/stun.h>
+
+#define START_PORT 5060
+#define MAX_BUDDIES 32
+#define THIS_FILE "main.c"
+#define MAX_PRESENTITY 32
+#define PRESENCE_TIMEOUT 60
+
+/* By default we'll have one worker thread, except when threading
+ * is disabled.
+ */
+#if PJ_HAS_THREADS
+# define WORKER_COUNT 1
+#else
+# define WORKER_COUNT 0
+#endif
+
+/* Global variable. */
+static struct
+{
+ /* Control. */
+ pj_pool_factory *pf;
+ pjsip_endpoint *endpt;
+ pj_pool_t *pool;
+ pjsip_user_agent *user_agent;
+ int worker_cnt;
+ int worker_quit_flag;
+
+ /* User info. */
+ char user_id[64];
+ pj_str_t local_uri;
+ pj_str_t contact;
+ pj_str_t real_contact;
+
+ /* Dialog. */
+ pjsip_dlg *cur_dlg;
+
+ /* Authentication. */
+ int cred_count;
+ pjsip_cred_info cred_info[4];
+
+ /* Media stack. */
+ pj_bool_t null_audio;
+ pj_med_mgr_t *mmgr;
+
+ /* Misc. */
+ int app_log_level;
+ char *log_filename;
+ FILE *log_file;
+
+ /* Proxy URLs */
+ pj_str_t proxy;
+ pj_str_t outbound_proxy;
+
+ /* UA auto options. */
+ int auto_answer; /* -1 to disable. */
+ int auto_hangup; /* -1 to disable */
+
+ /* Registration. */
+ pj_str_t registrar_uri;
+ pjsip_regc *regc;
+ pj_int32_t reg_timeout;
+ pj_timer_entry regc_timer;
+
+ /* STUN */
+ pj_str_t stun_srv1;
+ int stun_port1;
+ pj_str_t stun_srv2;
+ int stun_port2;
+
+ /* UDP sockets and their public address. */
+ int sip_port;
+ pj_sock_t sip_sock;
+ pj_sockaddr_in sip_sock_name;
+ pj_sock_t rtp_sock;
+ pj_sockaddr_in rtp_sock_name;
+ pj_sock_t rtcp_sock;
+ pj_sockaddr_in rtcp_sock_name;
+
+ /* SIMPLE */
+ pj_bool_t hide_status;
+ pj_bool_t offer_x_ms_msg;
+ int im_counter;
+ int buddy_cnt;
+ pj_str_t buddy[MAX_BUDDIES];
+ pj_bool_t buddy_status[MAX_BUDDIES];
+ pj_bool_t no_presence;
+ pjsip_presentity *buddy_pres[MAX_BUDDIES];
+
+ int pres_cnt;
+ pjsip_presentity *pres[MAX_PRESENTITY];
+
+} global;
+
+enum { AUTO_ANSWER, AUTO_HANGUP };
+
+/* This is the data that will be 'attached' on per dialog basis. */
+struct dialog_data
+{
+ /* Media session. */
+ pj_media_session_t *msession;
+
+ /* x-ms-chat session. */
+ pj_bool_t x_ms_msg_session;
+
+ /* Cached SDP body, updated when media session changed. */
+ pjsip_msg_body *body;
+
+ /* Timer. */
+ pj_bool_t has_auto_timer;
+ pj_timer_entry auto_timer;
+};
+
+/*
+ * These are the callbacks to be registered to dialog to receive notifications
+ * about various events in the dialog.
+ */
+static void dlg_on_all_events (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
+ pjsip_event *event );
+static void dlg_on_before_tx (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata, int retransmission);
+static void dlg_on_tx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+static void dlg_on_rx_msg (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+static void dlg_on_incoming (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata);
+static void dlg_on_calling (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata);
+static void dlg_on_provisional (pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_event *event);
+static void dlg_on_connecting (pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_established (pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event);
+static void dlg_on_terminated (pjsip_dlg *dlg);
+static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event);
+
+/* The callback structure that will be registered to UA layer. */
+struct pjsip_dlg_callback dlg_callback = {
+ &dlg_on_all_events,
+ &dlg_on_before_tx,
+ &dlg_on_tx_msg,
+ &dlg_on_rx_msg,
+ &dlg_on_incoming,
+ &dlg_on_calling,
+ &dlg_on_provisional,
+ &dlg_on_connecting,
+ &dlg_on_established,
+ &dlg_on_disconnected,
+ &dlg_on_terminated,
+ &dlg_on_mid_call_evt
+};
+
+
+/*
+ * Auxiliary things are put in misc.c, so that this main.c file is more
+ * readable.
+ */
+#include "misc.c"
+
+static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_dlg *dlg = entry->user_data;
+ struct dialog_data *dlg_data = dlg->user_data;
+
+ PJ_UNUSED_ARG(timer_heap)
+
+ dlg_data->has_auto_timer = 0;
+
+ if (entry->id == AUTO_ANSWER) {
+ pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200);
+ if (tdata) {
+ struct dialog_data *dlg_data = global.cur_dlg->user_data;
+ tdata->msg->body = dlg_data->body;
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+ } else {
+ pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500);
+ if (tdata)
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+}
+
+static void update_registration(pjsip_regc *regc, int renew)
+{
+ pjsip_tx_data *tdata;
+
+ PJ_LOG(3,(THIS_FILE, "Performing SIP registration..."));
+
+ if (renew) {
+ tdata = pjsip_regc_register(regc, 1);
+ } else {
+ tdata = pjsip_regc_unregister(regc);
+ }
+
+ pjsip_regc_send( regc, tdata );
+}
+
+static void regc_cb(struct pjsip_regc_cbparam *param)
+{
+ /*
+ * Print registration status.
+ */
+ if (param->code < 0 || param->code >= 300) {
+ PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)",
+ param->code, pjsip_get_status_text(param->code)->ptr));
+ global.regc = NULL;
+
+ } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) {
+ PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), "
+ "will re-register in %d seconds",
+ param->code,
+ pjsip_get_status_text(param->code)->ptr,
+ param->expiration));
+
+ } else {
+ PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code));
+ }
+}
+
+static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata,
+ int *timeout)
+{
+ int state;
+ int i;
+ char url[PJSIP_MAX_URL_SIZE];
+ int urllen;
+
+ PJ_UNUSED_ARG(rdata)
+
+ if (*timeout > 0) {
+ state = PJSIP_EVENT_SUB_STATE_ACTIVE;
+ if (*timeout > 300)
+ *timeout = 300;
+ } else {
+ state = PJSIP_EVENT_SUB_STATE_TERMINATED;
+ }
+
+ urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1);
+ if (urllen < 1) {
+ pj_native_strcpy(url, "<unknown>");
+ } else {
+ url[urllen] = '\0';
+ }
+ PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s",
+ url,
+ (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated")));
+
+ for (i=0; i<global.pres_cnt; ++i)
+ if (global.pres[i] == pres)
+ break;
+ if (i == global.pres_cnt)
+ global.pres[global.pres_cnt++] = pres;
+
+ pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
+ pjsip_presence_notify(pres, state, !global.hide_status);
+
+}
+
+static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata)
+{
+ pres_on_received_request(pres, rdata, &pres->sub->default_interval);
+}
+
+/* This is called by presence framework when we receives presence update
+ * of a resource (buddy).
+ */
+static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open)
+{
+ int buddy_index = (int)pres->user_data;
+
+ global.buddy_status[buddy_index] = is_open;
+ PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s",
+ global.buddy[buddy_index].ptr,
+ (is_open ? "Online" : "Offline")));
+}
+
+/* This is called when the subscription is terminated. */
+static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason)
+{
+ if (pres->sub->role == PJSIP_ROLE_UAC) {
+ int buddy_index = (int)pres->user_data;
+ PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)",
+ global.buddy[buddy_index].ptr,
+ reason->slen, reason->ptr));
+ global.buddy_pres[buddy_index] = NULL;
+ global.buddy_status[buddy_index] = 0;
+ } else {
+ int i;
+ PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)",
+ reason->slen, reason->ptr));
+ pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1);
+ for (i=0; i<global.pres_cnt; ++i) {
+ if (global.pres[i] == pres) {
+ int j;
+ global.pres[i] = NULL;
+ for (j=i+1; j<global.pres_cnt; ++j)
+ global.pres[j-1] = global.pres[j];
+ global.pres_cnt--;
+ break;
+ }
+ }
+ }
+ pjsip_presence_destroy(pres);
+}
+
+
+/* Callback attached to SIP body to print the body to message buffer. */
+static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size)
+{
+ pjsip_msg_body *body = msg_body;
+ return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size);
+}
+
+/* When media session has changed, call this function to update the cached body
+ * information in the dialog.
+ */
+static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ pjsdp_session_desc *sdp;
+
+ sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg);
+ if (!sdp) {
+ dlg_data->body = NULL;
+ return NULL;
+ }
+
+ /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new
+ * "m=" line in the SDP.
+ */
+ if (dlg_data->x_ms_msg_session >= 0 &&
+ dlg_data->x_ms_msg_session >= (int)sdp->media_count)
+ {
+ pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m));
+ sdp->media[sdp->media_count] = m;
+ dlg_data->x_ms_msg_session = sdp->media_count++;
+ }
+
+ /*
+ * For "x-ms-message" line, remove all attributes and connection line etc.
+ */
+ if (dlg_data->x_ms_msg_session >= 0) {
+ pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session];
+ if (m) {
+ m->desc.media = pj_str("x-ms-message");
+ m->desc.port = 5060;
+ m->desc.transport = pj_str("sip");
+ m->desc.fmt_count = 1;
+ m->desc.fmt[0] = pj_str("null");
+ m->attr_count = 0;
+ m->conn = NULL;
+ }
+ }
+
+ dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body));
+ dlg_data->body->content_type.type = pj_str("application");
+ dlg_data->body->content_type.subtype = pj_str("sdp");
+ dlg_data->body->len = 0; /* ignored */
+ dlg_data->body->print_body = &print_msg_body;
+
+ dlg_data->body->data = sdp;
+ return dlg_data->body;
+}
+
+/* This callback will be called on every occurence of events in dialogs */
+static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt,
+ pjsip_event *event )
+{
+ PJ_UNUSED_ARG(dlg_evt)
+ PJ_UNUSED_ARG(event)
+
+ PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg));
+}
+
+/* This callback is called before each outgoing msg is sent (including
+ * retransmission). Application can override this notification if it wants
+ * to modify the message before transmission or if it wants to do something
+ * else for each transmission.
+ */
+static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata, int ret_cnt)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(tdata)
+
+ if (ret_cnt > 0) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)",
+ dlg->obj_name, ret_cnt));
+ }
+}
+
+/* This callback is called after a message is sent. */
+static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(tdata)
+
+ PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
+}
+
+/* This callback is called on receipt of incoming message. */
+static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(rdata)
+ PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg));
+}
+
+/* This callback is called after dialog has sent INVITE */
+static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_tx_data *tdata)
+{
+ PJ_UNUSED_ARG(tsx)
+ PJ_UNUSED_ARG(tdata)
+
+ pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG &&
+ tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
+ tsx->method.id == PJSIP_INVITE_METHOD);
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name));
+}
+
+static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ pj_bool_t sdp_x_ms_msg_index = -1;
+ int i;
+ int mcnt;
+ const pj_media_stream_info *mi[PJSDP_MAX_MEDIA];
+ int has_active;
+ pj_media_sock_info sock_info;
+
+ /* Find "m=x-ms-message" line in the SDP. */
+ for (i=0; i<(int)sdp->media_count; ++i) {
+ if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0)
+ sdp_x_ms_msg_index = i;
+ }
+
+ /*
+ * Create media session.
+ */
+ pj_memset(&sock_info, 0, sizeof(sock_info));
+ sock_info.rtp_sock = global.rtp_sock;
+ sock_info.rtcp_sock = global.rtcp_sock;
+ pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
+
+ dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info);
+
+ /* A session will always be created, unless there is memory
+ * alloc problem.
+ */
+ pj_assert(dlg_data->msession);
+
+ /* See if we can take the offer by checking that we have at least
+ * one media stream active.
+ */
+ mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi);
+ for (i=0, has_active=0; i<mcnt; ++i) {
+ if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) {
+ has_active = 1;
+ break;
+ }
+ }
+
+ if (!has_active && sdp_x_ms_msg_index==-1) {
+ pjsip_tx_data *tdata;
+
+ /* Unable to accept remote's SDP.
+ * Answer with 488 (Not Acceptable Here)
+ */
+ /* Create 488 response. */
+ tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE);
+
+ /* Send response. */
+ if (tdata)
+ pjsip_dlg_send_msg(dlg, tdata);
+ return;
+ }
+
+ dlg_data->x_ms_msg_session = sdp_x_ms_msg_index;
+
+ /* Create msg body to be used later in 2xx/response */
+ create_msg_body(dlg, 0);
+
+}
+
+/* This callback is called after an INVITE is received. */
+static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_rx_data *rdata)
+{
+ struct dialog_data *dlg_data;
+ pjsip_msg *msg;
+ pjsip_tx_data *tdata;
+ char buf[128];
+ int len;
+
+ PJ_UNUSED_ARG(tsx)
+
+ pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG &&
+ rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD &&
+ tsx->method.id == PJSIP_INVITE_METHOD);
+
+ /*
+ * Notify user!
+ */
+ PJ_LOG(3, (THIS_FILE, ""));
+ PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name));
+ PJ_LOG(3, (THIS_FILE, ""));
+ len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
+ (pjsip_name_addr*)dlg->remote.info->uri,
+ buf, sizeof(buf)-1);
+ if (len > 0) {
+ buf[len] = '\0';
+ PJ_LOG(3,(THIS_FILE, "From:\t%s", buf));
+ }
+ len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,
+ (pjsip_name_addr*)dlg->local.info->uri,
+ buf, sizeof(buf)-1);
+ if (len > 0) {
+ buf[len] = '\0';
+ PJ_LOG(3,(THIS_FILE, "To:\t%s", buf));
+ }
+ PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name));
+ PJ_LOG(3, (THIS_FILE, ""));
+
+ /*
+ * Process incoming dialog.
+ */
+
+ dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
+ dlg->user_data = dlg_data;
+
+ /* Update contact. */
+ pjsip_dlg_set_contact(dlg, &global.contact);
+
+ /* Initialize credentials. */
+ pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
+
+ /* Create media session if the request has "application/sdp" body. */
+ msg = rdata->msg;
+ if (msg->body &&
+ pj_stricmp2(&msg->body->content_type.type, "application")==0 &&
+ pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0)
+ {
+ pjsdp_session_desc *sdp;
+
+ /* Parse SDP body, and instantiate media session based on remote's SDP.
+ * Then create our SDP body from the session.
+ */
+ sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool);
+ if (!sdp)
+ goto send_answer;
+
+ create_session_from_sdp(dlg, sdp);
+
+ } else if (msg->body) {
+ /* The request has a message body other than "application/sdp" */
+ pjsip_accept_hdr *accept;
+
+ /* Create response. */
+ tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
+
+ /* Add "Accept" header. */
+ accept = pjsip_accept_hdr_create(tdata->pool);
+ accept->values[0] = pj_str("application/sdp");
+ accept->count = 1;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept);
+
+ /* Send response. */
+ pjsip_dlg_send_msg(dlg, tdata);
+ return;
+
+ } else {
+ /* The request has no message body. We can take this request, but
+ * no media session will be activated.
+ */
+ /* Nothing to do here. */
+ }
+
+send_answer:
+ /* Immediately answer with 100 (or 180? */
+ tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING );
+ pjsip_dlg_send_msg(dlg, tdata);
+
+ /* Set current dialog to this dialog if we don't currently have
+ * current dialog.
+ */
+ if (global.cur_dlg == NULL) {
+ global.cur_dlg = dlg;
+ }
+
+ /* Auto-answer if option is specified. */
+ if (global.auto_answer >= 0) {
+ pj_time_val delay = { 0, 0};
+ struct dialog_data *dlg_data = dlg->user_data;
+
+ PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds",
+ global.auto_answer));
+
+ delay.sec = global.auto_answer;
+ dlg_data->auto_timer.user_data = dlg;
+ dlg_data->auto_timer.id = AUTO_ANSWER;
+ dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
+ dlg_data->has_auto_timer = 1;
+ pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
+ }
+}
+
+/* This callback is called when dialog has sent/received a provisional response
+ * to INVITE.
+ */
+static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ const char *action;
+
+ pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.tdata->msg->line.status.code/100 == 1 &&
+ tsx->method.id == PJSIP_INVITE_METHOD)
+ ||
+ (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.rdata->msg->line.status.code/100 == 1 &&
+ tsx->method.id == PJSIP_INVITE_METHOD));
+
+ if (event->src_type == PJSIP_EVENT_TX_MSG)
+ action = "Sending";
+ else
+ action = "Received";
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)",
+ dlg->obj_name, action, tsx->status_code,
+ pjsip_get_status_text(tsx->status_code)->ptr));
+}
+
+/* This callback is called when 200 response to INVITE is sent/received. */
+static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ const char *action;
+
+ pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.tdata->msg->line.status.code/100 == 2)
+ ||
+ (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_RESPONSE_MSG &&
+ event->src.rdata->msg->line.status.code/100 == 2));
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ action = "Received";
+ else
+ action = "Sending";
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action));
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ /* On receipt of 2xx response, negotiate our media capability
+ * and start media.
+ */
+ pjsip_msg *msg = event->src.rdata->msg;
+ pjsip_msg_body *body;
+ pjsdp_session_desc *sdp;
+
+ /* Get SDP from message. */
+
+ /* Ignore if no SDP body is present. */
+ body = msg->body;
+ if (!body) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!",
+ dlg->obj_name));
+ return;
+ }
+
+ if (pj_stricmp2(&body->content_type.type, "application") != 0 &&
+ pj_stricmp2(&body->content_type.subtype, "sdp") != 0)
+ {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!",
+ dlg->obj_name));
+ return;
+ }
+
+ /* Got what seems to be a SDP content. Parse it. */
+ sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool);
+ if (!sdp) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!",
+ dlg->obj_name));
+ return;
+ }
+
+ /* Negotiate media session with remote's media capability. */
+ if (pj_media_session_update (dlg_data->msession, sdp) != 0) {
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!",
+ dlg->obj_name));
+ return;
+ }
+
+ /* Update the saved SDP body because media session has changed.
+ * Also set ack flag to '1', because we only want to send one format/
+ * codec for each media streams.
+ */
+ create_msg_body(dlg, 1);
+
+ /* Activate media. */
+ pj_media_session_activate (dlg_data->msession);
+
+ } else {
+ pjsip_msg *msg = event->src.tdata->msg;
+
+ if (msg->body) {
+ /* On transmission of 2xx response, start media session. */
+ pj_media_session_activate (dlg_data->msession);
+ }
+ }
+
+}
+
+/* This callback is called when ACK to initial INVITE is sent/received. */
+static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event)
+{
+ const char *action;
+
+ pj_assert((event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->type == PJSIP_REQUEST_MSG &&
+ event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
+ ||
+ (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_REQUEST_MSG &&
+ event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD));
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ action = "Received";
+ else
+ action = "Sending";
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED",
+ dlg->obj_name, action));
+
+ /* Attach SDP body for outgoing ACK. */
+ if (event->src_type == PJSIP_EVENT_TX_MSG &&
+ event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD)
+ {
+ struct dialog_data *dlg_data = dlg->user_data;
+ event->src.tdata->msg->body = dlg_data->body;
+ }
+
+ /* Auto-hangup if option is specified. */
+ if (global.auto_hangup >= 0) {
+ pj_time_val delay = { 0, 0};
+ struct dialog_data *dlg_data = dlg->user_data;
+
+ PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds",
+ global.auto_hangup));
+
+ delay.sec = global.auto_hangup;
+ dlg_data->auto_timer.user_data = dlg;
+ dlg_data->auto_timer.id = AUTO_HANGUP;
+ dlg_data->auto_timer.cb = &dlg_auto_timer_callback;
+ dlg_data->has_auto_timer = 1;
+ pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay);
+ }
+}
+
+
+/* This callback is called when dialog is disconnected (because of final
+ * response, BYE, or timer).
+ */
+static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ int status_code;
+ const pj_str_t *reason;
+
+ PJ_UNUSED_ARG(event)
+
+ /* Cancel auto-answer/auto-hangup timer. */
+ if (dlg_data->has_auto_timer) {
+ pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer);
+ dlg_data->has_auto_timer = 0;
+ }
+
+ if (dlg->invite_tsx)
+ status_code = dlg->invite_tsx->status_code;
+ else
+ status_code = 200;
+
+ if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) {
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ reason = &event->src.rdata->msg->line.status.reason;
+ else if (event->src_type == PJSIP_EVENT_TX_MSG)
+ reason = &event->src.tdata->msg->line.status.reason;
+ else
+ reason = pjsip_get_status_text(event->obj.tsx->status_code);
+ } else {
+ reason = &event->obj.tsx->method.name;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)",
+ dlg->obj_name, status_code,
+ reason->slen, reason->ptr));
+
+ if (dlg_data->msession) {
+ pj_media_session_destroy (dlg_data->msession);
+ dlg_data->msession = NULL;
+ }
+}
+
+/* This callback is called when dialog is about to be destroyed. */
+static void dlg_on_terminated(pjsip_dlg *dlg)
+{
+ PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name));
+
+ /* If current dialog is equal to this dialog, update it. */
+ if (global.cur_dlg == dlg) {
+ global.cur_dlg = global.cur_dlg->next;
+ if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+ global.cur_dlg = NULL;
+ }
+ }
+}
+
+/* This callback is called for any requests when dialog is established. */
+static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event)
+{
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ if (event->src_type == PJSIP_EVENT_RX_MSG &&
+ event->src.rdata->msg->type == PJSIP_REQUEST_MSG)
+ {
+ if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) {
+ /* Re-invitation. */
+ pjsip_tx_data *tdata;
+
+ PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)",
+ dlg->obj_name));
+ tdata = pjsip_dlg_answer(dlg, 200);
+ if (tdata) {
+ struct dialog_data *dlg_data = dlg->user_data;
+ tdata->msg->body = dlg_data->body;
+ pjsip_dlg_send_msg(dlg, tdata);
+ }
+ } else {
+ /* Don't worry, endpoint will answer with 500 or whetever. */
+ }
+
+ } else if (tsx->status_code/100 == 2) {
+ PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)",
+ dlg->obj_name,
+ tsx->method.name.slen, tsx->method.name.ptr,
+ tsx->status_code,
+ pjsip_get_status_text(tsx->status_code)->ptr));
+
+
+ } else if (tsx->status_code >= 300) {
+ pj_bool_t report_failure = PJ_TRUE;
+
+ /* Check for authentication failures. */
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( global.endpt,
+ dlg->pool, &dlg->auth_sess,
+ dlg->cred_count, dlg->cred_info,
+ tsx->last_tx, event->src.rdata );
+ if (tdata) {
+ int rc;
+ rc = pjsip_dlg_send_msg( dlg, tdata);
+ report_failure = (rc != 0);
+ }
+ }
+ if (report_failure) {
+ const pj_str_t *reason;
+ if (event->src_type == PJSIP_EVENT_RX_MSG) {
+ reason = &event->src.rdata->msg->line.status.reason;
+ } else {
+ reason = pjsip_get_status_text(tsx->status_code);
+ }
+ PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)",
+ dlg->obj_name, tsx->status_code,
+ reason->slen, reason->ptr));
+ }
+ }
+}
+
+/* Initialize sockets and optionally get the public address via STUN. */
+static pj_status_t init_sockets()
+{
+ enum {
+ RTP_START_PORT = 4000,
+ RTP_RANDOM_START = 2,
+ RTP_RETRY = 10
+ };
+ enum {
+ SIP_SOCK,
+ RTP_SOCK,
+ RTCP_SOCK,
+ };
+ int i;
+ int rtp_port;
+ pj_sock_t sock[3];
+ pj_sockaddr_in mapped_addr[3];
+
+ for (i=0; i<3; ++i)
+ sock[i] = PJ_INVALID_SOCKET;
+
+ /* Create and bind SIP UDP socket. */
+ sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) {
+ PJ_LOG(2,(THIS_FILE, "Unable to create socket"));
+ goto on_error;
+ }
+ if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) {
+ PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port"));
+ goto on_error;
+ }
+
+ /* Initialize start of RTP port to try. */
+ rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2;
+
+ /* Loop retry to bind RTP and RTCP sockets. */
+ for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) {
+
+ /* Create and bind RTP socket. */
+ sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (sock[RTP_SOCK] == PJ_INVALID_SOCKET)
+ goto on_error;
+ if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) {
+ pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ continue;
+ }
+
+ /* Create and bind RTCP socket. */
+ sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0);
+ if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET)
+ goto on_error;
+ if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) {
+ pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+ continue;
+ }
+
+ /*
+ * If we're configured to use STUN, then find out the mapped address,
+ * and make sure that the mapped RTCP port is adjacent with the RTP.
+ */
+ if (global.stun_port1 == 0) {
+ pj_str_t hostname;
+ pj_sockaddr_in addr;
+
+ /* Get local IP address. */
+ char hostname_buf[PJ_MAX_HOSTNAME];
+ if (gethostname(hostname_buf, sizeof(hostname_buf)))
+ goto on_error;
+ hostname = pj_str(hostname_buf);
+
+ pj_memset( &addr, 0, sizeof(addr));
+ addr.sin_family = PJ_AF_INET;
+ if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_SUCCESS)
+ goto on_error;
+
+ for (i=0; i<3; ++i)
+ pj_memcpy(&mapped_addr[i], &addr, sizeof(addr));
+
+ mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port);
+ mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port);
+ mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1));
+ break;
+ } else {
+ pj_status_t rc;
+ rc = pj_stun_get_mapped_addr( global.pf, 3, sock,
+ &global.stun_srv1, global.stun_port1,
+ &global.stun_srv2, global.stun_port2,
+ mapped_addr);
+ if (rc != 0) {
+ PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc)));
+ goto on_error;
+ }
+
+ if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1)
+ break;
+
+ pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET;
+ pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET;
+ }
+ }
+
+ if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) {
+ PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination"));
+ goto on_error;
+ }
+
+ global.sip_sock = sock[SIP_SOCK];
+ pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in));
+ global.rtp_sock = sock[RTP_SOCK];
+ pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in));
+ global.rtcp_sock = sock[RTCP_SOCK];
+ pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in));
+
+ PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d",
+ pj_inet_ntoa(global.sip_sock_name.sin_addr),
+ pj_ntohs(global.sip_sock_name.sin_port)));
+ PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d",
+ pj_inet_ntoa(global.rtp_sock_name.sin_addr),
+ pj_ntohs(global.rtp_sock_name.sin_port)));
+ PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d",
+ pj_inet_ntoa(global.rtcp_sock_name.sin_addr),
+ pj_ntohs(global.rtcp_sock_name.sin_port)));
+ return 0;
+
+on_error:
+ for (i=0; i<3; ++i) {
+ if (sock[i] != PJ_INVALID_SOCKET)
+ pj_sock_close(sock[i]);
+ }
+ return -1;
+}
+
+static void log_function(int level, const char *buffer, int len)
+{
+ /* Write to both stdout and file. */
+ if (level <= global.app_log_level)
+ pj_log_to_stdout(level, buffer, len);
+ if (global.log_file) {
+ fwrite(buffer, len, 1, global.log_file);
+ fflush(global.log_file);
+ }
+}
+
+/* Initialize stack. */
+static pj_status_t init_stack()
+{
+ pj_status_t status;
+ pj_sockaddr_in bind_addr;
+ pj_sockaddr_in bind_name;
+ const char *local_addr;
+ static char local_uri[128];
+
+ /* Optionally set logging file. */
+ if (global.log_filename) {
+ global.log_file = fopen(global.log_filename, "wt");
+ }
+
+ /* Initialize endpoint. This will also call initialization to all the
+ * modules.
+ */
+ global.endpt = pjsip_endpt_create(global.pf);
+ if (global.endpt == NULL) {
+ return -1;
+ }
+
+ /* Set dialog callback. */
+ pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback);
+
+ /* Init listener's bound address and port. */
+ pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port);
+ pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port);
+
+ /* Add UDP transport listener. */
+ status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock,
+ &global.sip_sock_name);
+ if (status != 0)
+ return -1;
+
+ local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr);
+
+#if PJ_HAS_TCP
+ /* Add TCP transport listener. */
+ status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP,
+ &bind_addr, &bind_name);
+ if (status != 0)
+ return -1;
+#endif
+
+ /* Determine user_id to be put in Contact */
+ if (global.local_uri.slen) {
+ pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL);
+ pjsip_uri *uri;
+
+ uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0);
+ if (uri) {
+ if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) {
+ pjsip_url *url = (pjsip_url*)pjsip_uri_get_uri(uri);
+ if (url->user.slen)
+ strncpy(global.user_id, url->user.ptr, url->user.slen);
+ }
+ }
+ pj_pool_release(pool);
+ }
+
+ if (global.user_id[0]=='\0') {
+ pj_native_strcpy(global.user_id, "user");
+ }
+
+ /* build contact */
+ global.real_contact.ptr = local_uri;
+ global.real_contact.slen =
+ sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port);
+
+ if (global.contact.slen == 0)
+ global.contact = global.real_contact;
+
+ /* initialize local_uri with contact if it's not specified in cmdline */
+ if (global.local_uri.slen == 0)
+ global.local_uri = global.contact;
+
+ /* Init proxy. */
+ if (global.proxy.slen || global.outbound_proxy.slen) {
+ int count = 0;
+ pj_str_t proxy_url[2];
+
+ if (global.outbound_proxy.slen) {
+ proxy_url[count++] = global.outbound_proxy;
+ }
+ if (global.proxy.slen) {
+ proxy_url[count++] = global.proxy;
+ }
+
+ if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) {
+ PJ_LOG(2,(THIS_FILE, "Error setting proxy address!"));
+ return -1;
+ }
+ }
+
+ /* initialize SIP registration if registrar is configured */
+ if (global.registrar_uri.slen) {
+ global.regc = pjsip_regc_create( global.endpt, NULL, ®c_cb);
+ pjsip_regc_init( global.regc, &global.registrar_uri,
+ &global.local_uri,
+ &global.local_uri,
+ 1, &global.contact,
+ global.reg_timeout);
+ pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info );
+ }
+
+ return PJ_SUCCESS;
+}
+
+/* Worker thread function, only used when threading is enabled. */
+static void *PJ_THREAD_FUNC worker_thread(void *unused)
+{
+ PJ_UNUSED_ARG(unused)
+
+ while (!global.worker_quit_flag) {
+ pj_time_val timeout = { 0, 10 };
+ pjsip_endpt_handle_events (global.endpt, &timeout);
+ }
+ return NULL;
+}
+
+
+/* Make call to the specified URI. */
+static pjsip_dlg *make_call(pj_str_t *remote_uri)
+{
+ pjsip_dlg *dlg;
+ pj_str_t local = global.contact;
+ pj_str_t remote = *remote_uri;
+ struct dialog_data *dlg_data;
+ pjsip_tx_data *tdata;
+ pj_media_sock_info sock_info;
+
+ /* Create new dialog instance. */
+ dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC);
+
+ /* Attach our own user data. */
+ dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data));
+ dlg->user_data = dlg_data;
+
+ /* Create media session. */
+ pj_memset(&sock_info, 0, sizeof(sock_info));
+ sock_info.rtp_sock = global.rtp_sock;
+ sock_info.rtcp_sock = global.rtcp_sock;
+ pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in));
+
+ dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info);
+ dlg_data->x_ms_msg_session = -1;
+
+ if (global.offer_x_ms_msg) {
+ const pj_media_stream_info *minfo[32];
+ unsigned cnt;
+
+ cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo);
+ if (cnt > 0)
+ dlg_data->x_ms_msg_session = cnt;
+ }
+
+ /* Initialize dialog with local and remote URI. */
+ if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) {
+ pjsip_ua_destroy_dialog(dlg);
+ return NULL;
+ }
+
+ /* Initialize credentials. */
+ pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info);
+
+ /* Send INVITE! */
+ tdata = pjsip_dlg_invite(dlg);
+ tdata->msg->body = create_msg_body (dlg, 0);
+
+ if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) {
+ pjsip_ua_destroy_dialog(dlg);
+ return NULL;
+ }
+
+ return dlg;
+}
+
+/*
+ * Callback to receive incoming IM message.
+ */
+static int on_incoming_im_msg(pjsip_rx_data *rdata)
+{
+ pjsip_msg *msg = rdata->msg;
+ pjsip_msg_body *body = msg->body;
+ int len;
+ char to[128], from[128];
+
+
+ len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
+ rdata->from->uri, from, sizeof(from));
+ if (len > 0) from[len] = '\0';
+ else pj_native_strcpy(from, "<URL too long..>");
+
+ len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
+ rdata->to->uri, to, sizeof(to));
+ if (len > 0) to[len] = '\0';
+ else pj_native_strcpy(to, "<URL too long..>");
+
+ PJ_LOG(3,(THIS_FILE, "Incoming instant message:"));
+
+ printf("----- BEGIN INSTANT MESSAGE ----->\n");
+ printf("From:\t%s\n", from);
+ printf("To:\t%s\n", to);
+ printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : ""));
+ printf("<------ END INSTANT MESSAGE ------\n");
+
+ fflush(stdout);
+
+ /* Must answer with final response. */
+ return 200;
+}
+
+/*
+ * Input URL.
+ */
+static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection)
+{
+ int i;
+
+ *selection = -1;
+
+ printf("\nBuddy list:\n");
+ printf("---------------------------------------\n");
+ for (i=0; i<global.buddy_cnt; ++i) {
+ printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
+ (global.buddy_status[i]?"Online":"Offline"));
+ }
+ printf("-------------------------------------\n");
+
+ printf("Choices\n"
+ "\t0 For current dialog.\n"
+ "\t[1-%02d] Select from buddy list\n"
+ "\tURL An URL\n"
+ , global.buddy_cnt);
+ printf("Input: ");
+
+ fflush(stdout);
+ fgets(buf, len, stdin);
+ buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */
+
+ while (isspace(*buf)) ++buf;
+
+ if (!*buf || *buf=='\n' || *buf=='\r')
+ return NULL;
+
+ i = atoi(buf);
+
+ if (i == 0) {
+ if (isdigit(*buf)) {
+ *selection = 0;
+ *out = pj_str("0");
+ return out;
+ } else {
+ if (verify_sip_url(buf) != 0) {
+ puts("Invalid URL specified!");
+ return NULL;
+ }
+ *out = pj_str(buf);
+ return out;
+ }
+ } else if (i > global.buddy_cnt || i < 0) {
+ printf("Error: invalid selection!\n");
+ return NULL;
+ } else {
+ *out = global.buddy[i-1];
+ *selection = i;
+ return out;
+ }
+}
+
+
+static void generic_request_callback( void *token, pjsip_event *event )
+{
+ pjsip_transaction *tsx = event->obj.tsx;
+
+ PJ_UNUSED_ARG(token)
+
+ if (tsx->status_code/100 == 2) {
+ PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)",
+ event->obj.tsx->method.name.slen,
+ event->obj.tsx->method.name.ptr,
+ tsx->status_code,
+ pjsip_get_status_text(tsx->status_code)->ptr));
+ } else if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_auth_reinit_req( global.endpt,
+ global.pool, NULL, global.cred_count, global.cred_info,
+ tsx->last_tx, event->src.rdata);
+ if (tdata) {
+ int rc;
+ pjsip_cseq_hdr *cseq;
+ cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
+ cseq->cseq++;
+ rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
+ &generic_request_callback);
+ if (rc == 0)
+ return;
+ }
+ PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)",
+ event->obj.tsx->method.name.slen,
+ event->obj.tsx->method.name.ptr,
+ event->obj.tsx->status_code,
+ pjsip_get_status_text(event->obj.tsx->status_code)->ptr));
+ } else {
+ const pj_str_t *reason;
+ if (event->src_type == PJSIP_EVENT_RX_MSG)
+ reason = &event->src.rdata->msg->line.status.reason;
+ else
+ reason = pjsip_get_status_text(tsx->status_code);
+ PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)",
+ event->obj.tsx->method.name.slen,
+ event->obj.tsx->method.name.ptr,
+ event->obj.tsx->status_code,
+ reason->slen, reason->ptr));
+ }
+}
+
+
+static void ui_send_im_message()
+{
+ char line[100];
+ char text_buf[100];
+ pj_str_t str;
+ pj_str_t text_msg;
+ int selection, rc;
+ pjsip_tx_data *tdata;
+
+ if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
+ return;
+
+
+ printf("Enter text to send (empty to cancel): "); fflush(stdout);
+ fgets(text_buf, sizeof(text_buf), stdin);
+ text_buf[strlen(text_buf)-1] = '\0';
+ if (!*text_buf)
+ return;
+
+ text_msg = pj_str(text_buf);
+
+ if (selection==0) {
+ pjsip_method message_method;
+ pj_str_t str_MESSAGE = { "MESSAGE", 7 };
+
+ /* Send IM to current dialog. */
+ if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {
+ printf("No current dialog or dialog state is not ESTABLISHED!\n");
+ return;
+ }
+
+ pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE);
+ tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 );
+
+ if (tdata) {
+ /* Create message body for the text. */
+ pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body));
+ body->content_type.type = pj_str("text");
+ body->content_type.subtype = pj_str("plain");
+ body->data = pj_pool_alloc(tdata->pool, text_msg.slen);
+ pj_memcpy(body->data, text_msg.ptr, text_msg.slen);
+ body->len = text_msg.slen;
+ body->print_body = &pjsip_print_text_body;
+
+ /* Assign body to message, and send the message! */
+ tdata->msg->body = body;
+ pjsip_dlg_send_msg( global.cur_dlg, tdata );
+ }
+
+ } else {
+ /* Send IM to buddy list. */
+ pjsip_method message;
+ static pj_str_t MESSAGE = { "MESSAGE", 7 };
+ pjsip_method_init_np(&message, &MESSAGE);
+ tdata = pjsip_endpt_create_request(global.endpt, &message,
+ &str,
+ &global.real_contact,
+ &str, &global.real_contact, NULL, -1,
+ &text_msg);
+ if (!tdata) {
+ puts("Error creating request");
+ return;
+ }
+ rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback);
+ if (rc == 0) {
+ printf("Sending IM message %d\n", global.im_counter);
+ ++global.im_counter;
+ } else {
+ printf("Error: unable to send IM message!\n");
+ }
+ }
+}
+
+static void ui_send_options()
+{
+ char line[100];
+ pj_str_t str;
+ int selection, rc;
+ pjsip_tx_data *tdata;
+ pjsip_method options;
+
+ if (ui_input_url(&str, line, sizeof(line), &selection) == NULL)
+ return;
+
+ pjsip_method_set( &options, PJSIP_OPTIONS_METHOD );
+
+ if (selection == 0) {
+ /* Send OPTIONS to current dialog. */
+ tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1);
+ if (tdata)
+ pjsip_dlg_send_msg( global.cur_dlg, tdata );
+ } else {
+ /* Send OPTIONS to arbitrary party. */
+ tdata = pjsip_endpt_create_request( global.endpt, &options,
+ &str,
+ &global.local_uri, &str,
+ &global.real_contact,
+ NULL, -1, NULL);
+ if (tdata) {
+ rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,
+ &generic_request_callback);
+ if (rc != 0)
+ PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!"));
+ }
+ }
+}
+
+static void init_presence()
+{
+ const pjsip_presence_cb pres_cb = {
+ NULL,
+ &pres_on_received_request,
+ &pres_on_received_refresh,
+ &pres_on_received_update,
+ &pres_on_terminated
+ };
+
+ pjsip_presence_init(&pres_cb);
+}
+
+/* Subscribe presence information for all buddies. */
+static void subscribe_buddies_presence()
+{
+ int i;
+ for (i=0; i<global.buddy_cnt; ++i) {
+ pjsip_presentity *pres;
+ if (global.buddy_pres[i])
+ continue;
+ pres = pjsip_presence_create( global.endpt, &global.local_uri,
+ &global.buddy[i], PRESENCE_TIMEOUT, (void*)i);
+ if (pres) {
+ pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info );
+ pjsip_presence_subscribe( pres );
+ }
+ global.buddy_pres[i] = pres;
+ }
+}
+
+/* Unsubscribe presence information for all buddies. */
+static void unsubscribe_buddies_presence()
+{
+ int i;
+ for (i=0; i<global.buddy_cnt; ++i) {
+ pjsip_presentity *pres = global.buddy_pres[i];
+ if (pres) {
+ pjsip_presence_unsubscribe(pres);
+ pjsip_presence_destroy(pres);
+ global.buddy_pres[i] = NULL;
+ }
+ }
+}
+
+/* Unsubscribe presence. */
+static void unsubscribe_presence()
+{
+ int i;
+
+ unsubscribe_buddies_presence();
+ for (i=0; i<global.pres_cnt; ++i) {
+ pjsip_presentity *pres = global.pres[i];
+ pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0);
+ pjsip_presence_destroy( pres );
+ }
+}
+
+/* Advertise online status to subscribers. */
+static void update_im_status()
+{
+ int i;
+ for (i=0; i<global.pres_cnt; ++i) {
+ pjsip_presentity *pres = global.pres[i];
+ pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE,
+ !global.hide_status);
+ }
+}
+
+/*
+ * Main program.
+ */
+int main(int argc, char *argv[])
+{
+ /* set to WORKER_COUNT+1 to avoid zero size warning
+ * when threading is disabled. */
+ pj_thread_t *thread[WORKER_COUNT+1];
+ pj_caching_pool cp;
+ int i;
+
+ global.sip_port = 5060;
+ global.auto_answer = -1;
+ global.auto_hangup = -1;
+ global.app_log_level = 3;
+
+ pj_log_set_level(4);
+ pj_log_set_log_func(&log_function);
+
+ /* Init PJLIB */
+ if (pj_init() != PJ_SUCCESS)
+ return 1;
+
+ /* Init caching pool. */
+ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+ global.pf = &cp.factory;
+
+ /* Create memory pool for application. */
+ global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL);
+
+ /* Parse command line arguments. */
+ if (parse_args(global.pool, argc, argv) != PJ_SUCCESS) {
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+
+ /* Init sockets */
+ if (init_sockets() != 0) {
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+
+ /* Initialize stack. */
+ if (init_stack() != PJ_SUCCESS) {
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+
+ /* Set callback to receive incoming IM */
+ pjsip_messaging_set_incoming_callback( &on_incoming_im_msg );
+
+ /* Set default worker count (can be zero) */
+ global.worker_cnt = WORKER_COUNT;
+
+ /* Create user worker thread(s), only when threading is enabled. */
+ for (i=0; i<global.worker_cnt; ++i) {
+ thread[i] = pj_thread_create( global.pool, "sip%p",
+ &worker_thread,
+ NULL, 0, NULL, 0);
+ if (thread == NULL) {
+ global.worker_quit_flag = 1;
+ for (--i; i>=0; --i) {
+ pj_thread_join(thread[i]);
+ pj_thread_destroy(thread[i]);
+ }
+ pj_caching_pool_destroy(&cp);
+ return 1;
+ }
+ }
+
+ printf("Worker thread count: %d\n", global.worker_cnt);
+
+ /* Perform registration, if required. */
+ if (global.regc) {
+ update_registration(global.regc, 1);
+ }
+
+ /* Initialize media manager. */
+ global.mmgr = pj_med_mgr_create(global.pf);
+
+ /* Init presence. */
+ init_presence();
+
+ /* Subscribe presence information of all buddies. */
+ if (!global.no_presence)
+ subscribe_buddies_presence();
+
+ /* Initializatio completes, loop waiting for commands. */
+ for (;!global.worker_quit_flag;) {
+ pj_str_t str;
+ char line[128];
+
+#if WORKER_COUNT==0
+ /* If worker thread does not exist, main thread must poll for evetns.
+ * But this won't work very well since main thread is blocked by
+ * fgets(). So keep pressing the ENTER key to get the events!
+ */
+ pj_time_val timeout = { 0, 100 };
+ pjsip_endpt_handle_events(global.endpt, &timeout);
+ puts("Keep pressing ENTER key to get the events!");
+#endif
+
+ printf("\nCurrent dialog: ");
+ print_dialog(global.cur_dlg);
+ puts("");
+
+ keystroke_help();
+
+ fgets(line, sizeof(line), stdin);
+
+ switch (*line) {
+ case 'm':
+ puts("Make outgoing call");
+ if (ui_input_url(&str, line, sizeof(line), &i) != NULL) {
+ pjsip_dlg *dlg = make_call(&str);
+ if (global.cur_dlg == NULL) {
+ global.cur_dlg = dlg;
+ }
+ }
+ break;
+ case 'i':
+ puts("Send Instant Messaging");
+ ui_send_im_message();
+ break;
+ case 'o':
+ puts("Send OPTIONS");
+ ui_send_options();
+ break;
+ case 'a':
+ if (global.cur_dlg) {
+ unsigned code;
+ pjsip_tx_data *tdata;
+ struct dialog_data *dlg_data = global.cur_dlg->user_data;
+
+ printf("Answer with status code (1xx-6xx): ");
+ fflush(stdout);
+ fgets(line, sizeof(line), stdin);
+ str = pj_str(line);
+ str.slen -= 1;
+
+ code = pj_strtoul(&str);
+ tdata = pjsip_dlg_answer(global.cur_dlg, code);
+ if (tdata) {
+ if (code/100 == 2) {
+ tdata->msg->body = dlg_data->body;
+ }
+ pjsip_dlg_send_msg(global.cur_dlg, tdata);
+
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case 'h':
+ if (global.cur_dlg) {
+ pjsip_tx_data *tdata;
+ tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE);
+ if (tdata) {
+ pjsip_dlg_send_msg(global.cur_dlg, tdata);
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case ']':
+ if (global.cur_dlg) {
+ global.cur_dlg = global.cur_dlg->next;
+ if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+ global.cur_dlg = global.cur_dlg->next;
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case '[':
+ if (global.cur_dlg) {
+ global.cur_dlg = global.cur_dlg->prev;
+ if (global.cur_dlg == (void*)&global.user_agent->dlg_list) {
+ global.cur_dlg = global.cur_dlg->prev;
+ }
+ } else {
+ puts("No current dialog");
+ }
+ break;
+ case 'd':
+ pjsip_endpt_dump(global.endpt, *(line+1)=='1');
+ pjsip_ua_dump(global.user_agent);
+ break;
+ case 's':
+ if (*(line+1) == 'u')
+ subscribe_buddies_presence();
+ break;
+ case 'u':
+ if (*(line+1) == 's')
+ unsubscribe_presence();
+ break;
+ case 't':
+ global.hide_status = !global.hide_status;
+ update_im_status();
+ break;
+ case 'q':
+ goto on_exit;
+ case 'l':
+ print_all_dialogs();
+ break;
+ }
+ }
+
+on_exit:
+ /* Unregister, if required. */
+ if (global.regc) {
+ update_registration(global.regc, 0);
+ }
+
+ /* Unsubscribe presence. */
+ unsubscribe_presence();
+
+ /* Allow one second to get all events. */
+ if (1) {
+ pj_time_val end_time;
+
+ pj_gettimeofday(&end_time);
+ end_time.sec++;
+
+ PJ_LOG(3,(THIS_FILE, "Shutting down.."));
+ for (;;) {
+ pj_time_val timeout = { 0, 20 }, now;
+ pjsip_endpt_handle_events (global.endpt, &timeout);
+ pj_gettimeofday(&now);
+ PJ_TIME_VAL_SUB(now, end_time);
+ if (now.sec >= 1)
+ break;
+ }
+ }
+
+ global.worker_quit_flag = 1;
+
+ pj_med_mgr_destroy(global.mmgr);
+
+ /* Wait all threads to quit. */
+ for (i=0; i<global.worker_cnt; ++i) {
+ pj_thread_join(thread[i]);
+ pj_thread_destroy(thread[i]);
+ }
+
+ /* Destroy endpoint. */
+ pjsip_endpt_destroy(global.endpt);
+
+ /* Destroy caching pool. */
+ pj_caching_pool_destroy(&cp);
+
+ /* Close log file, if any. */
+ if (global.log_file)
+ fclose(global.log_file);
+
+ return 0;
+}
+
+/*
+ * Register static modules to the endpoint.
+ */
+pj_status_t register_static_modules( pj_size_t *count,
+ pjsip_module **modules )
+{
+ /* Reset count. */
+ *count = 0;
+
+ /* Register user agent module. */
+ modules[(*count)++] = pjsip_ua_get_module();
+ global.user_agent = modules[0]->mod_data;
+ modules[(*count)++] = pjsip_messaging_get_module();
+ modules[(*count)++] = pjsip_event_sub_get_module();
+
+ return PJ_SUCCESS;
+}
diff --git a/pjsip/src/pjsua/misc.c b/pjsip/src/pjsua/misc.c index 58cc795f..d5a1c997 100644 --- a/pjsip/src/pjsua/misc.c +++ b/pjsip/src/pjsua/misc.c @@ -1,470 +1,492 @@ -/* $Id$ - * - */ - -/* - * THIS FILE IS INCLUDED BY main.c. - * IT WON'T COMPILE BY ITSELF. - */ - -#include "getopt.h" -#include <stdio.h> - - -/* - * Display program usage - */ -static void usage() -{ - puts("Usage:"); - puts(" pjsua [options] [sip-url]"); - puts(""); - puts(" [sip-url] Default URL to invite."); - puts(""); - puts("General options:"); - puts(" --config-file=file Read the config/arguments from file."); - puts(" --log-file=fname Log to filename (default stderr)"); - puts(" --log-level=N Set log max level to N (0(none) to 6(trace))"); - puts(" --app-log-level=N Set log max level for stdout display to N"); - puts(" --help Display this help screen"); - puts(" --version Display version info"); - puts(""); - puts("Media options:"); - puts(" --null-audio Use NULL audio device"); - puts(""); - puts("User Agent options:"); - puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds."); - puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds."); - puts(""); - puts("SIP options:"); - puts(" --local-port=port Set TCP/UDP port"); - puts(" --id=url Set the URL of local ID (used in From header)"); - puts(" --contact=url Override the Contact information"); - puts(" --proxy=url Set the URL of proxy server"); - puts(" --outbound=url Set the URL of outbound proxy server"); - puts(" --registrar=url Set the URL of registrar server"); - puts(" --reg-timeout=secs Set registration interval to secs (default 3600)"); - puts(""); - puts("Authentication options:"); - puts(" --realm=string Set realm"); - puts(" --username=string Set authentication username"); - puts(" --password=string Set authentication password"); - puts(""); - puts("STUN options (all must be specified):"); - puts(" --use-stun1=host[:port]"); - puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers"); - puts(""); - puts("SIMPLE options (may be specified more than once):"); - puts(" --add-buddy url Add the specified URL to the buddy list."); - puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE"); - puts(" --no-presence Do not subscribe presence of buddies"); - puts(""); - fflush(stdout); -} - -/* Display keystroke help. */ -static void keystroke_help() -{ - int i; - - printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online")); - puts(""); - puts("Buddy list:"); - puts("-------------------------------------------------------------------------------"); - for (i=0; i<global.buddy_cnt; ++i) { - printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr, - (global.buddy_status[i]?"Online":"Offline")); - } - //printf("-------------------------------------\n"); - puts(""); - //puts("Commands:"); - puts("+=============================================================================+"); - puts("| Call Commands: | IM & Presence: | Misc: |"); - puts("| | | |"); - puts("| m Make new call | i Send IM | o Send OPTIONS |"); - puts("| a Answer call | su Subscribe presence | d Dump status |"); - puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |"); - puts("| ] Select next dialog | t Toggle Online status | |"); - puts("| [ Select previous dialog | | |"); - puts("+-----------------------------------------------------------------------------+"); - puts("| q QUIT |"); - puts("+=============================================================================+"); - puts(""); - - - fflush(stdout); -} - -/* - * Verify that valid SIP url is given. - */ -static pj_status_t verify_sip_url(char *url) -{ - pjsip_uri *p; - pj_pool_t *pool; - int len = (url ? strlen(url) : 0); - - if (!len) return -1; - - pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL); - if (!pool) return -1; - - p = pjsip_parse_uri(pool, url, len, 0); - if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) - p = NULL; - - pj_pool_release(pool); - return p ? 0 : -1; -} - -/* - * Read command arguments from config file. - */ -static int read_config_file(pj_pool_t *pool, const char *filename, - int *app_argc, char ***app_argv) -{ - int i; - FILE *fhnd; - char line[200]; - int argc = 0; - char **argv; - enum { MAX_ARGS = 64 }; - - /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ - argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); - argv[argc++] = *app_argv[0]; - - /* Open config file. */ - fhnd = fopen(filename, "rt"); - if (!fhnd) { - printf("Unable to open config file %s\n", filename); - return -1; - } - - /* Scan tokens in the file. */ - while (argc < MAX_ARGS && !feof(fhnd)) { - char *token, *p = line; - - if (fgets(line, sizeof(line), fhnd) == NULL) break; - - for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; - token = strtok(NULL, " \t\r\n")) - { - int token_len; - - if (!token) break; - if (*token == '#') break; - - token_len = strlen(token); - if (!token_len) - continue; - argv[argc] = pj_pool_alloc(pool, token_len+1); - pj_memcpy(argv[argc], token, token_len+1); - ++argc; - } - } - - /* Copy arguments from command line */ - for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) - argv[argc++] = (*app_argv)[i]; - - if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { - printf("Too many arguments specified in cmd line/config file\n"); - fclose(fhnd); - return -1; - } - - fclose(fhnd); - - /* Assign the new command line back to the original command line. */ - *app_argc = argc; - *app_argv = argv; - return 0; - -} - -/* - * Parse program arguments - */ -static int parse_args(pj_pool_t *pool, int argc, char *argv[]) -{ - int c; - int option_index; - enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, - OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, - OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, - OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, - OPT_REALM, OPT_USERNAME, OPT_PASSWORD, - OPT_USE_STUN1, OPT_USE_STUN2, - OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, - OPT_AUTO_ANSWER, OPT_AUTO_HANGUP}; - struct option long_options[] = { - { "config-file",1, 0, OPT_CONFIG_FILE}, - { "log-file", 1, 0, OPT_LOG_FILE}, - { "log-level", 1, 0, OPT_LOG_LEVEL}, - { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, - { "help", 0, 0, OPT_HELP}, - { "version", 0, 0, OPT_VERSION}, - { "null-audio", 0, 0, OPT_NULL_AUDIO}, - { "local-port", 1, 0, OPT_LOCAL_PORT}, - { "proxy", 1, 0, OPT_PROXY}, - { "outbound", 1, 0, OPT_OUTBOUND_PROXY}, - { "registrar", 1, 0, OPT_REGISTRAR}, - { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, - { "id", 1, 0, OPT_ID}, - { "contact", 1, 0, OPT_CONTACT}, - { "realm", 1, 0, OPT_REALM}, - { "username", 1, 0, OPT_USERNAME}, - { "password", 1, 0, OPT_PASSWORD}, - { "use-stun1", 1, 0, OPT_USE_STUN1}, - { "use-stun2", 1, 0, OPT_USE_STUN2}, - { "add-buddy", 1, 0, OPT_ADD_BUDDY}, - { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, - { "no-presence", 0, 0, OPT_NO_PRESENCE}, - { "auto-answer",1, 0, OPT_AUTO_ANSWER}, - { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, - { NULL, 0, 0, 0} - }; - char *config_file = NULL; - - /* Run getopt once to see if user specifies config file to read. */ - while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { - switch (c) { - case 0: - config_file = optarg; - break; - } - if (config_file) - break; - } - - if (config_file) { - if (read_config_file(pool, config_file, &argc, &argv) != 0) - return -1; - } - - /* Reinitialize and re-run getopt again, possibly with new arguments - * read from config file. - */ - optind = 0; - while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { - char *err, *p; - - switch (c) { - case OPT_LOG_FILE: - global.log_filename = optarg; - break; - case OPT_LOG_LEVEL: - c = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value 0-6 for --log-level\n"); - return -1; - } - pj_log_set_level( c ); - break; - case OPT_APP_LOG_LEVEL: - global.app_log_level = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value 0-6 for --app-log-level\n"); - return -1; - } - break; - case OPT_HELP: - usage(); - return -1; - case OPT_VERSION: /* version */ - pj_dump_config(); - return -1; - case OPT_NULL_AUDIO: - global.null_audio = 1; - break; - case OPT_LOCAL_PORT: /* local-port */ - global.sip_port = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --local-port\n"); - return -1; - } - break; - case OPT_PROXY: /* proxy */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg); - return -1; - } - global.proxy = pj_str(optarg); - break; - case OPT_OUTBOUND_PROXY: /* outbound proxy */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg); - return -1; - } - global.outbound_proxy = pj_str(optarg); - break; - case OPT_REGISTRAR: /* registrar */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg); - return -1; - } - global.registrar_uri = pj_str(optarg); - break; - case OPT_REG_TIMEOUT: /* reg-timeout */ - global.reg_timeout = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --reg-timeout\n"); - return -1; - } - break; - case OPT_ID: /* id */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in local id argument\n", optarg); - return -1; - } - global.local_uri = pj_str(optarg); - break; - case OPT_CONTACT: /* contact */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid SIP URL '%s' in contact argument\n", optarg); - return -1; - } - global.contact = pj_str(optarg); - break; - case OPT_USERNAME: /* Default authentication user */ - if (!global.cred_count) global.cred_count = 1; - global.cred_info[0].username = pj_str(optarg); - break; - case OPT_REALM: /* Default authentication realm. */ - if (!global.cred_count) global.cred_count = 1; - global.cred_info[0].realm = pj_str(optarg); - break; - case OPT_PASSWORD: /* authentication password */ - if (!global.cred_count) global.cred_count = 1; - global.cred_info[0].data_type = 0; - global.cred_info[0].data = pj_str(optarg); - break; - case OPT_USE_STUN1: /* STUN server 1 */ - p = pj_native_strchr(optarg, ':'); - if (p) { - *p = '\0'; - global.stun_srv1 = pj_str(optarg); - global.stun_port1 = strtoul(p+1, &err, 10); - if (*err || global.stun_port1==0) { - printf("Error: expecting port number with option --use-stun1\n"); - return -1; - } - } else { - global.stun_port1 = 3478; - global.stun_srv1 = pj_str(optarg); - } - break; - case OPT_USE_STUN2: /* STUN server 2 */ - p = pj_native_strchr(optarg, ':'); - if (p) { - *p = '\0'; - global.stun_srv2 = pj_str(optarg); - global.stun_port2 = strtoul(p+1, &err, 10); - if (*err || global.stun_port2==0) { - printf("Error: expecting port number with option --use-stun2\n"); - return -1; - } - } else { - global.stun_port2 = 3478; - global.stun_srv2 = pj_str(optarg); - } - break; - case OPT_ADD_BUDDY: /* Add to buddy list. */ - if (verify_sip_url(optarg) != 0) { - printf("Error: invalid URL '%s' in --add-buddy option\n", optarg); - return -1; - } - if (global.buddy_cnt == MAX_BUDDIES) { - printf("Error: too many buddies in buddy list.\n"); - return -1; - } - global.buddy[global.buddy_cnt++] = pj_str(optarg); - break; - case OPT_OFFER_X_MS_MSG: - global.offer_x_ms_msg = 1; - break; - case OPT_NO_PRESENCE: - global.no_presence = 1; - break; - case OPT_AUTO_ANSWER: - global.auto_answer = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --auto-answer option\n"); - return -1; - } - break; - case OPT_AUTO_HANGUP: - global.auto_hangup = strtoul(optarg, &err, 10); - if (*err) { - printf("Error: expecting integer value for --auto-hangup option\n"); - return -1; - } - break; - } - } - - if (optind != argc) { - printf("Error: unknown options %s\n", argv[optind]); - return -1; - } - - if (global.reg_timeout == 0) - global.reg_timeout = 3600; - - return 0; -} - -/* Print dialog. */ -static void print_dialog(pjsip_dlg *dlg) -{ - if (!dlg) { - puts("none"); - return; - } - - printf("%s: call-id=%.*s", dlg->obj_name, - (int)dlg->call_id->id.slen, - dlg->call_id->id.ptr); - - printf(" (%s, %s)\n", pjsip_role_name(dlg->role), - pjsip_dlg_state_str(dlg->state)); -} - -/* Dump media statistic */ -void dump_media_statistic(pjsip_dlg *dlg) -{ - struct dialog_data *dlg_data = dlg->user_data; - pj_media_stream_stat stat[2]; - const char *statname[2] = { "TX", "RX" }; - int i; - - pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]); - - printf("Media statistic:\n"); - for (i=0; i<2; ++i) { - printf(" %s statistics:\n", statname[i]); - printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx); - printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx); - printf(" Jitter %d ms\n", stat[i].jitter); - printf(" Pkt lost %d\n", stat[i].pkt_lost); - } - printf("\n"); -} - -/* Print all dialogs. */ -static void print_all_dialogs() -{ - pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next; - - puts("List all dialogs:"); - - while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) { - printf("%c", (dlg==global.cur_dlg ? '*' : ' ')); - print_dialog(dlg); - dlg = dlg->next; - } - - puts(""); -} - +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+/*
+ * THIS FILE IS INCLUDED BY main.c.
+ * IT WON'T COMPILE BY ITSELF.
+ */
+
+#include "getopt.h"
+#include <stdio.h>
+
+
+/*
+ * Display program usage
+ */
+static void usage()
+{
+ puts("Usage:");
+ puts(" pjsua [options] [sip-url]");
+ puts("");
+ puts(" [sip-url] Default URL to invite.");
+ puts("");
+ puts("General options:");
+ puts(" --config-file=file Read the config/arguments from file.");
+ puts(" --log-file=fname Log to filename (default stderr)");
+ puts(" --log-level=N Set log max level to N (0(none) to 6(trace))");
+ puts(" --app-log-level=N Set log max level for stdout display to N");
+ puts(" --help Display this help screen");
+ puts(" --version Display version info");
+ puts("");
+ puts("Media options:");
+ puts(" --null-audio Use NULL audio device");
+ puts("");
+ puts("User Agent options:");
+ puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds.");
+ puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds.");
+ puts("");
+ puts("SIP options:");
+ puts(" --local-port=port Set TCP/UDP port");
+ puts(" --id=url Set the URL of local ID (used in From header)");
+ puts(" --contact=url Override the Contact information");
+ puts(" --proxy=url Set the URL of proxy server");
+ puts(" --outbound=url Set the URL of outbound proxy server");
+ puts(" --registrar=url Set the URL of registrar server");
+ puts(" --reg-timeout=secs Set registration interval to secs (default 3600)");
+ puts("");
+ puts("Authentication options:");
+ puts(" --realm=string Set realm");
+ puts(" --username=string Set authentication username");
+ puts(" --password=string Set authentication password");
+ puts("");
+ puts("STUN options (all must be specified):");
+ puts(" --use-stun1=host[:port]");
+ puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers");
+ puts("");
+ puts("SIMPLE options (may be specified more than once):");
+ puts(" --add-buddy url Add the specified URL to the buddy list.");
+ puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE");
+ puts(" --no-presence Do not subscribe presence of buddies");
+ puts("");
+ fflush(stdout);
+}
+
+/* Display keystroke help. */
+static void keystroke_help()
+{
+ int i;
+
+ printf("Advertise status as: %s\n", (global.hide_status ? "Offline" : "Online"));
+ puts("");
+ puts("Buddy list:");
+ puts("-------------------------------------------------------------------------------");
+ for (i=0; i<global.buddy_cnt; ++i) {
+ printf(" %d\t%s <%s>\n", i+1, global.buddy[i].ptr,
+ (global.buddy_status[i]?"Online":"Offline"));
+ }
+ //printf("-------------------------------------\n");
+ puts("");
+ //puts("Commands:");
+ puts("+=============================================================================+");
+ puts("| Call Commands: | IM & Presence: | Misc: |");
+ puts("| | | |");
+ puts("| m Make new call | i Send IM | o Send OPTIONS |");
+ puts("| a Answer call | su Subscribe presence | d Dump status |");
+ puts("| h Hangup call | us Unsubscribe presence | d1 Dump detailed |");
+ puts("| ] Select next dialog | t Toggle Online status | |");
+ puts("| [ Select previous dialog | | |");
+ puts("+-----------------------------------------------------------------------------+");
+ puts("| q QUIT |");
+ puts("+=============================================================================+");
+ puts("");
+
+
+ fflush(stdout);
+}
+
+/*
+ * Verify that valid SIP url is given.
+ */
+static pj_status_t verify_sip_url(char *url)
+{
+ pjsip_uri *p;
+ pj_pool_t *pool;
+ int len = (url ? strlen(url) : 0);
+
+ if (!len) return -1;
+
+ pool = pj_pool_create(global.pf, "check%p", 1024, 0, NULL);
+ if (!pool) return -1;
+
+ p = pjsip_parse_uri(pool, url, len, 0);
+ if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0)
+ p = NULL;
+
+ pj_pool_release(pool);
+ return p ? 0 : -1;
+}
+
+/*
+ * Read command arguments from config file.
+ */
+static int read_config_file(pj_pool_t *pool, const char *filename,
+ int *app_argc, char ***app_argv)
+{
+ int i;
+ FILE *fhnd;
+ char line[200];
+ int argc = 0;
+ char **argv;
+ enum { MAX_ARGS = 64 };
+
+ /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */
+ argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*));
+ argv[argc++] = *app_argv[0];
+
+ /* Open config file. */
+ fhnd = fopen(filename, "rt");
+ if (!fhnd) {
+ printf("Unable to open config file %s\n", filename);
+ return -1;
+ }
+
+ /* Scan tokens in the file. */
+ while (argc < MAX_ARGS && !feof(fhnd)) {
+ char *token, *p = line;
+
+ if (fgets(line, sizeof(line), fhnd) == NULL) break;
+
+ for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS;
+ token = strtok(NULL, " \t\r\n"))
+ {
+ int token_len;
+
+ if (!token) break;
+ if (*token == '#') break;
+
+ token_len = strlen(token);
+ if (!token_len)
+ continue;
+ argv[argc] = pj_pool_alloc(pool, token_len+1);
+ pj_memcpy(argv[argc], token, token_len+1);
+ ++argc;
+ }
+ }
+
+ /* Copy arguments from command line */
+ for (i=1; i<*app_argc && argc < MAX_ARGS; ++i)
+ argv[argc++] = (*app_argv)[i];
+
+ if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) {
+ printf("Too many arguments specified in cmd line/config file\n");
+ fclose(fhnd);
+ return -1;
+ }
+
+ fclose(fhnd);
+
+ /* Assign the new command line back to the original command line. */
+ *app_argc = argc;
+ *app_argv = argv;
+ return 0;
+
+}
+
+/*
+ * Parse program arguments
+ */
+static int parse_args(pj_pool_t *pool, int argc, char *argv[])
+{
+ int c;
+ int option_index;
+ enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL,
+ OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO,
+ OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR,
+ OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT,
+ OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
+ OPT_USE_STUN1, OPT_USE_STUN2,
+ OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
+ OPT_AUTO_ANSWER, OPT_AUTO_HANGUP};
+ struct option long_options[] = {
+ { "config-file",1, 0, OPT_CONFIG_FILE},
+ { "log-file", 1, 0, OPT_LOG_FILE},
+ { "log-level", 1, 0, OPT_LOG_LEVEL},
+ { "app-log-level",1,0,OPT_APP_LOG_LEVEL},
+ { "help", 0, 0, OPT_HELP},
+ { "version", 0, 0, OPT_VERSION},
+ { "null-audio", 0, 0, OPT_NULL_AUDIO},
+ { "local-port", 1, 0, OPT_LOCAL_PORT},
+ { "proxy", 1, 0, OPT_PROXY},
+ { "outbound", 1, 0, OPT_OUTBOUND_PROXY},
+ { "registrar", 1, 0, OPT_REGISTRAR},
+ { "reg-timeout",1, 0, OPT_REG_TIMEOUT},
+ { "id", 1, 0, OPT_ID},
+ { "contact", 1, 0, OPT_CONTACT},
+ { "realm", 1, 0, OPT_REALM},
+ { "username", 1, 0, OPT_USERNAME},
+ { "password", 1, 0, OPT_PASSWORD},
+ { "use-stun1", 1, 0, OPT_USE_STUN1},
+ { "use-stun2", 1, 0, OPT_USE_STUN2},
+ { "add-buddy", 1, 0, OPT_ADD_BUDDY},
+ { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
+ { "no-presence", 0, 0, OPT_NO_PRESENCE},
+ { "auto-answer",1, 0, OPT_AUTO_ANSWER},
+ { "auto-hangup",1, 0, OPT_AUTO_HANGUP},
+ { NULL, 0, 0, 0}
+ };
+ char *config_file = NULL;
+
+ /* Run getopt once to see if user specifies config file to read. */
+ while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ switch (c) {
+ case 0:
+ config_file = optarg;
+ break;
+ }
+ if (config_file)
+ break;
+ }
+
+ if (config_file) {
+ if (read_config_file(pool, config_file, &argc, &argv) != 0)
+ return -1;
+ }
+
+ /* Reinitialize and re-run getopt again, possibly with new arguments
+ * read from config file.
+ */
+ optind = 0;
+ while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) {
+ char *err, *p;
+
+ switch (c) {
+ case OPT_LOG_FILE:
+ global.log_filename = optarg;
+ break;
+ case OPT_LOG_LEVEL:
+ c = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value 0-6 for --log-level\n");
+ return -1;
+ }
+ pj_log_set_level( c );
+ break;
+ case OPT_APP_LOG_LEVEL:
+ global.app_log_level = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value 0-6 for --app-log-level\n");
+ return -1;
+ }
+ break;
+ case OPT_HELP:
+ usage();
+ return -1;
+ case OPT_VERSION: /* version */
+ pj_dump_config();
+ return -1;
+ case OPT_NULL_AUDIO:
+ global.null_audio = 1;
+ break;
+ case OPT_LOCAL_PORT: /* local-port */
+ global.sip_port = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --local-port\n");
+ return -1;
+ }
+ break;
+ case OPT_PROXY: /* proxy */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg);
+ return -1;
+ }
+ global.proxy = pj_str(optarg);
+ break;
+ case OPT_OUTBOUND_PROXY: /* outbound proxy */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg);
+ return -1;
+ }
+ global.outbound_proxy = pj_str(optarg);
+ break;
+ case OPT_REGISTRAR: /* registrar */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg);
+ return -1;
+ }
+ global.registrar_uri = pj_str(optarg);
+ break;
+ case OPT_REG_TIMEOUT: /* reg-timeout */
+ global.reg_timeout = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --reg-timeout\n");
+ return -1;
+ }
+ break;
+ case OPT_ID: /* id */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in local id argument\n", optarg);
+ return -1;
+ }
+ global.local_uri = pj_str(optarg);
+ break;
+ case OPT_CONTACT: /* contact */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid SIP URL '%s' in contact argument\n", optarg);
+ return -1;
+ }
+ global.contact = pj_str(optarg);
+ break;
+ case OPT_USERNAME: /* Default authentication user */
+ if (!global.cred_count) global.cred_count = 1;
+ global.cred_info[0].username = pj_str(optarg);
+ break;
+ case OPT_REALM: /* Default authentication realm. */
+ if (!global.cred_count) global.cred_count = 1;
+ global.cred_info[0].realm = pj_str(optarg);
+ break;
+ case OPT_PASSWORD: /* authentication password */
+ if (!global.cred_count) global.cred_count = 1;
+ global.cred_info[0].data_type = 0;
+ global.cred_info[0].data = pj_str(optarg);
+ break;
+ case OPT_USE_STUN1: /* STUN server 1 */
+ p = pj_native_strchr(optarg, ':');
+ if (p) {
+ *p = '\0';
+ global.stun_srv1 = pj_str(optarg);
+ global.stun_port1 = strtoul(p+1, &err, 10);
+ if (*err || global.stun_port1==0) {
+ printf("Error: expecting port number with option --use-stun1\n");
+ return -1;
+ }
+ } else {
+ global.stun_port1 = 3478;
+ global.stun_srv1 = pj_str(optarg);
+ }
+ break;
+ case OPT_USE_STUN2: /* STUN server 2 */
+ p = pj_native_strchr(optarg, ':');
+ if (p) {
+ *p = '\0';
+ global.stun_srv2 = pj_str(optarg);
+ global.stun_port2 = strtoul(p+1, &err, 10);
+ if (*err || global.stun_port2==0) {
+ printf("Error: expecting port number with option --use-stun2\n");
+ return -1;
+ }
+ } else {
+ global.stun_port2 = 3478;
+ global.stun_srv2 = pj_str(optarg);
+ }
+ break;
+ case OPT_ADD_BUDDY: /* Add to buddy list. */
+ if (verify_sip_url(optarg) != 0) {
+ printf("Error: invalid URL '%s' in --add-buddy option\n", optarg);
+ return -1;
+ }
+ if (global.buddy_cnt == MAX_BUDDIES) {
+ printf("Error: too many buddies in buddy list.\n");
+ return -1;
+ }
+ global.buddy[global.buddy_cnt++] = pj_str(optarg);
+ break;
+ case OPT_OFFER_X_MS_MSG:
+ global.offer_x_ms_msg = 1;
+ break;
+ case OPT_NO_PRESENCE:
+ global.no_presence = 1;
+ break;
+ case OPT_AUTO_ANSWER:
+ global.auto_answer = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --auto-answer option\n");
+ return -1;
+ }
+ break;
+ case OPT_AUTO_HANGUP:
+ global.auto_hangup = strtoul(optarg, &err, 10);
+ if (*err) {
+ printf("Error: expecting integer value for --auto-hangup option\n");
+ return -1;
+ }
+ break;
+ }
+ }
+
+ if (optind != argc) {
+ printf("Error: unknown options %s\n", argv[optind]);
+ return -1;
+ }
+
+ if (global.reg_timeout == 0)
+ global.reg_timeout = 3600;
+
+ return 0;
+}
+
+/* Print dialog. */
+static void print_dialog(pjsip_dlg *dlg)
+{
+ if (!dlg) {
+ puts("none");
+ return;
+ }
+
+ printf("%s: call-id=%.*s", dlg->obj_name,
+ (int)dlg->call_id->id.slen,
+ dlg->call_id->id.ptr);
+
+ printf(" (%s, %s)\n", pjsip_role_name(dlg->role),
+ pjsip_dlg_state_str(dlg->state));
+}
+
+/* Dump media statistic */
+void dump_media_statistic(pjsip_dlg *dlg)
+{
+ struct dialog_data *dlg_data = dlg->user_data;
+ pj_media_stream_stat stat[2];
+ const char *statname[2] = { "TX", "RX" };
+ int i;
+
+ pj_media_session_get_stat (dlg_data->msession, 0, &stat[0], &stat[1]);
+
+ printf("Media statistic:\n");
+ for (i=0; i<2; ++i) {
+ printf(" %s statistics:\n", statname[i]);
+ printf(" Pkt TX=%d RX=%d\n", stat[i].pkt_tx, stat[i].pkt_rx);
+ printf(" Octets TX=%d RX=%d\n", stat[i].oct_tx, stat[i].oct_rx);
+ printf(" Jitter %d ms\n", stat[i].jitter);
+ printf(" Pkt lost %d\n", stat[i].pkt_lost);
+ }
+ printf("\n");
+}
+
+/* Print all dialogs. */
+static void print_all_dialogs()
+{
+ pjsip_dlg *dlg = (pjsip_dlg *)global.user_agent->dlg_list.next;
+
+ puts("List all dialogs:");
+
+ while (dlg != (pjsip_dlg *) &global.user_agent->dlg_list) {
+ printf("%c", (dlg==global.cur_dlg ? '*' : ' '));
+ print_dialog(dlg);
+ dlg = dlg->next;
+ }
+
+ puts("");
+}
+
diff --git a/pjsip/src/tests/pjsip-core/main.c b/pjsip/src/tests/pjsip-core/main.c index 6212ba4a..4a450dd7 100644 --- a/pjsip/src/tests/pjsip-core/main.c +++ b/pjsip/src/tests/pjsip-core/main.c @@ -1,17 +1,39 @@ -/* $Id$ - * - */ -#include "test.h" -#include <stdio.h> - -int main() -{ - test_uri(); - test_msg(); - -#if !IS_PROFILING - puts("Press <ENTER> to quit."); - fgets( s, sizeof(s), stdin); -#endif - return 0; -} +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include "test.h"
+#include <stdio.h>
+
+int main()
+{
+ test_uri();
+ test_msg();
+
+#if !IS_PROFILING
+ puts("Press <ENTER> to quit.");
+ fgets( s, sizeof(s), stdin);
+#endif
+ return 0;
+}
diff --git a/pjsip/src/tests/pjsip-core/test.h b/pjsip/src/tests/pjsip-core/test.h index 8bb57b2a..77083131 100644 --- a/pjsip/src/tests/pjsip-core/test.h +++ b/pjsip/src/tests/pjsip-core/test.h @@ -1,11 +1,33 @@ -/* $Id$ - * - */ -#include <pj/types.h> - -pj_status_t test_uri(void); -pj_status_t test_msg(void); - -#define SILENT 1 -#define IS_PROFILING 1 -#define LOOP 2000 +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pj/types.h>
+
+pj_status_t test_uri(void);
+pj_status_t test_msg(void);
+
+#define SILENT 1
+#define IS_PROFILING 1
+#define LOOP 2000
diff --git a/pjsip/src/tests/pjsip-core/test_msg.c b/pjsip/src/tests/pjsip-core/test_msg.c index 7326e37c..395d1eac 100644 --- a/pjsip/src/tests/pjsip-core/test_msg.c +++ b/pjsip/src/tests/pjsip-core/test_msg.c @@ -1,425 +1,447 @@ -/* $Id$ - * - */ -#include <pjsip/sip_msg.h> -#include <pjsip/sip_parser.h> -#include <pj/os.h> -#include <pj/pool.h> -#include <pj/string.h> -#include <stdlib.h> -#include <stdio.h> -#include "test.h" - -#define ERR_SYNTAX_ERR (-2) -#define ERR_NOT_EQUAL (-3) -#define ERR_SYSTEM (-4) - - -static pjsip_msg *create_msg0(pj_pool_t *pool); - -struct test_msg -{ - char msg[1024]; - pjsip_msg *(*creator)(pj_pool_t *pool); - pj_size_t len; -} test_array[] = -{ - { - /* 'Normal' message with all headers. */ - "INVITE sip:user@foo SIP/2.0\n" - "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" - "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" - "Call-ID: 12345678901234567890@bar\r\n" - "Content-Length: 0\r\n" - "CSeq: 123456 INVITE\n" - "Contact: <sip:joe@bar> ; q=0.5;expires=3600,sip:user@host;q=0.500\r" - " ,sip:user2@host2\n" - "Content-Type: text/html ; charset=ISO-8859-4\r" - "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" - " <sip:server10.biloxi.com;lr>\r" - "Record-Route: <sip:server10.biloxi.com>,\r\n" - " <sip:bigbox3.site3.atlanta.com;lr>\n" - "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" - "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" - " ;received=192.0.2.1\r\n" - "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n" - "Organization: \r" - "Max-Forwards: 70\n" - "X-Header: \r\n" - "\r", - &create_msg0 - } -}; - -static pj_caching_pool cp; -static pj_pool_factory *pf = &cp.factory; -static pj_uint32_t parse_len, parse_time, print_time; - -static void pool_error(pj_pool_t *pool, pj_size_t sz) -{ - PJ_UNUSED_ARG(pool) - PJ_UNUSED_ARG(sz) - - pj_assert(0); - exit(1); -} - -static const char *STATUS_STR(pj_status_t status) -{ - switch (status) { - case 0: return "OK"; - case ERR_SYNTAX_ERR: return "Syntax Error"; - case ERR_NOT_EQUAL: return "Not Equal"; - case ERR_SYSTEM: return "System Error"; - } - return "???"; -} - -static pj_status_t test_entry( struct test_msg *entry ) -{ - pjsip_msg *parsed_msg, *ref_msg; - pj_pool_t *pool; - pj_status_t status = PJ_SUCCESS; - int len; - pj_str_t str1, str2; - pjsip_hdr *hdr1, *hdr2; - pj_hr_timestamp t1, t2; - char *msgbuf; - - enum { BUFLEN = 512 }; - - pool = pj_pool_create( pf, "", - PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, - &pool_error); - - if (entry->len == 0) { - entry->len = strlen(entry->msg); - } - - /* Parse message. */ - parse_len += entry->len; - pj_hr_gettimestamp(&t1); - parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL); - if (parsed_msg == NULL) { - status = ERR_SYNTAX_ERR; - goto on_return; - } - pj_hr_gettimestamp(&t2); - parse_time += t2.u32.lo - t1.u32.lo; - -#if IS_PROFILING - goto print_msg; -#endif - - /* Create reference message. */ - ref_msg = entry->creator(pool); - - /* Create buffer for comparison. */ - str1.ptr = pj_pool_alloc(pool, BUFLEN); - str2.ptr = pj_pool_alloc(pool, BUFLEN); - - /* Compare message type. */ - if (parsed_msg->type != ref_msg->type) { - status = ERR_NOT_EQUAL; - goto on_return; - } - - /* Compare request or status line. */ - if (parsed_msg->type == PJSIP_REQUEST_MSG) { - pjsip_method *m1 = &parsed_msg->line.req.method; - pjsip_method *m2 = &ref_msg->line.req.method; - - if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) { - status = ERR_NOT_EQUAL; - goto on_return; - } - } else { - - } - - /* Compare headers. */ - hdr1 = parsed_msg->hdr.next; - hdr2 = ref_msg->hdr.next; - - while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) { - len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN); - if (len < 1) { - status = ERR_SYSTEM; - goto on_return; - } - str1.slen = len; - - len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN); - if (len < 1) { - status = ERR_SYSTEM; - goto on_return; - } - str2.slen = len; - - if (!SILENT) { - printf("hdr1='%.*s'\n" - "hdr2='%.*s'\n\n", - str1.slen, str1.ptr, - str2.slen, str2.ptr); - } - if (pj_strcmp(&str1, &str2) != 0) { - status = ERR_NOT_EQUAL; - goto on_return; - } - - hdr1 = hdr1->next; - hdr2 = hdr2->next; - } - - if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) { - status = ERR_NOT_EQUAL; - goto on_return; - } - - /* Print message. */ -#if IS_PROFILING -print_msg: -#endif - msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN); - if (msgbuf == NULL) { - status = ERR_SYSTEM; - goto on_return; - } - pj_hr_gettimestamp(&t1); - len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN); - if (len < 1) { - status = ERR_SYSTEM; - goto on_return; - } - pj_hr_gettimestamp(&t2); - print_time += t2.u32.lo - t1.u32.lo; - status = PJ_SUCCESS; - -on_return: - pj_pool_release(pool); - return status; -} - -static void warm_up() -{ - pj_pool_t *pool; - pool = pj_pool_create( pf, "", - PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA, - &pool_error); - pj_pool_release(pool); -} - - -pj_status_t test_msg(void) -{ - pj_status_t status; - unsigned i; - - pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); - warm_up(); - - for (i=0; i<LOOP; ++i) { - status = test_entry( &test_array[0] ); - } - printf("%s\n", STATUS_STR(status)); - - printf("Total bytes: %u, parse time=%f/char, print time=%f/char\n", - parse_len, - parse_time*1.0/parse_len, - print_time*1.0/parse_len); - return PJ_SUCCESS; -} - -/*****************************************************************************/ - -static pjsip_msg *create_msg0(pj_pool_t *pool) -{ - - pjsip_msg *msg; - pjsip_name_addr *name_addr; - pjsip_url *url; - pjsip_fromto_hdr *fromto; - pjsip_cid_hdr *cid; - pjsip_clen_hdr *clen; - pjsip_cseq_hdr *cseq; - pjsip_contact_hdr *contact; - pjsip_ctype_hdr *ctype; - pjsip_routing_hdr *routing; - pjsip_via_hdr *via; - pjsip_generic_string_hdr *generic; - pj_str_t str; - - msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG); - - /* "INVITE sip:user@foo SIP/2.0\n" */ - pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD); - url = pjsip_url_create(pool, 0); - msg->line.req.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "foo"); - - /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */ - fromto = pjsip_from_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); - pj_strdup2(pool, &fromto->tag, "1234578901234567890"); - 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); - name_addr->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "joe.user"); - pj_strdup2(pool, &url->host, "bar.otherdomain.com"); - - /* "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" */ - fromto = pjsip_to_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto); - 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); - name_addr->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "foo.bar.domain.com"); - - /* "Call-ID: 12345678901234567890@bar\r\n" */ - cid = pjsip_cid_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid); - pj_strdup2(pool, &cid->id, "12345678901234567890@bar"); - - /* "Content-Length: 0\r\n" */ - clen = pjsip_clen_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen); - clen->len = 0; - - /* "CSeq: 123456 INVITE\n" */ - cseq = pjsip_cseq_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq); - cseq->cseq = 123456; - pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD); - - /* "Contact: <sip:joe@bar>;q=0.5;expires=3600*/ - contact = pjsip_contact_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); - contact->q1000 = 500; - contact->expires = 3600; - name_addr = pjsip_name_addr_create(pool); - contact->uri = (pjsip_uri*)name_addr; - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "joe"); - pj_strdup2(pool, &url->host, "bar"); - - /*, sip:user@host;q=0.500\r" */ - contact = pjsip_contact_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); - contact->q1000 = 500; - url = pjsip_url_create(pool, 0); - contact->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "host"); - - /* " ,sip:user2@host2\n" */ - contact = pjsip_contact_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact); - url = pjsip_url_create(pool, 0); - contact->uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->user, "user2"); - pj_strdup2(pool, &url->host, "host2"); - - /* "Content-Type: text/html; charset=ISO-8859-4\r" */ - ctype = pjsip_ctype_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype); - pj_strdup2(pool, &ctype->media.type, "text"); - pj_strdup2(pool, &ctype->media.subtype, "html"); - pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4"); - - /* "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" */ - routing = pjsip_route_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); - url->lr_param = 1; - - /* " <sip:server10.biloxi.com;lr>\r" */ - routing = pjsip_route_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "server10.biloxi.com"); - url->lr_param = 1; - - /* "Record-Route: <sip:server10.biloxi.com>,\r\n" */ - routing = pjsip_rr_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "server10.biloxi.com"); - url->lr_param = 0; - - /* " <sip:bigbox3.site3.atlanta.com;lr>\n" */ - routing = pjsip_rr_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing); - url = pjsip_url_create(pool, 0); - routing->name_addr.uri = (pjsip_uri*)url; - pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com"); - url->lr_param = 1; - - /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "SCTP"); - pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com"); - pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1"); - - /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n" - " ;received=192.0.2.1\r\n" */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "UDP"); - pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com"); - pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8"); - pj_strdup2(pool, &via->recvd_param, "192.0.2.1"); - - - /* "Via: SIP/2.0/UDP 10.2.1.1, */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "UDP"); - pj_strdup2(pool, &via->sent_by.host, "10.2.1.1"); - - - /*SIP/2.0/TCP 192.168.1.1\n" */ - via = pjsip_via_hdr_create(pool); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)via); - pj_strdup2(pool, &via->transport, "TCP"); - pj_strdup2(pool, &via->sent_by.host, "192.168.1.1"); - - /* "Organization: \r" */ - str.ptr = "Organization"; - str.slen = 12; - generic = pjsip_generic_string_hdr_create(pool, &str); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); - generic->hvalue.ptr = NULL; - generic->hvalue.slen = 0; - - /* "Max-Forwards: 70\n" */ - str.ptr = "Max-Forwards"; - str.slen = 12; - generic = pjsip_generic_string_hdr_create(pool, &str); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); - str.ptr = "70"; - str.slen = 2; - generic->hvalue = str; - - /* "X-Header: \r\n" */ - str.ptr = "X-Header"; - str.slen = 8; - generic = pjsip_generic_string_hdr_create(pool, &str); - pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic); - str.ptr = NULL; - str.slen = 0; - generic->hvalue = str; - - return msg; -} +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_msg.h>
+#include <pjsip/sip_parser.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "test.h"
+
+#define ERR_SYNTAX_ERR (-2)
+#define ERR_NOT_EQUAL (-3)
+#define ERR_SYSTEM (-4)
+
+
+static pjsip_msg *create_msg0(pj_pool_t *pool);
+
+struct test_msg
+{
+ char msg[1024];
+ pjsip_msg *(*creator)(pj_pool_t *pool);
+ pj_size_t len;
+} test_array[] =
+{
+ {
+ /* 'Normal' message with all headers. */
+ "INVITE sip:user@foo SIP/2.0\n"
+ "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r"
+ "To: Fellow User <sip:user@foo.bar.domain.com>\r\n"
+ "Call-ID: 12345678901234567890@bar\r\n"
+ "Content-Length: 0\r\n"
+ "CSeq: 123456 INVITE\n"
+ "Contact: <sip:joe@bar> ; q=0.5;expires=3600,sip:user@host;q=0.500\r"
+ " ,sip:user2@host2\n"
+ "Content-Type: text/html ; charset=ISO-8859-4\r"
+ "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n"
+ " <sip:server10.biloxi.com;lr>\r"
+ "Record-Route: <sip:server10.biloxi.com>,\r\n"
+ " <sip:bigbox3.site3.atlanta.com;lr>\n"
+ "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n"
+ "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
+ " ;received=192.0.2.1\r\n"
+ "Via: SIP/2.0/UDP 10.2.1.1, SIP/2.0/TCP 192.168.1.1\n"
+ "Organization: \r"
+ "Max-Forwards: 70\n"
+ "X-Header: \r\n"
+ "\r",
+ &create_msg0
+ }
+};
+
+static pj_caching_pool cp;
+static pj_pool_factory *pf = &cp.factory;
+static pj_uint32_t parse_len, parse_time, print_time;
+
+static void pool_error(pj_pool_t *pool, pj_size_t sz)
+{
+ PJ_UNUSED_ARG(pool)
+ PJ_UNUSED_ARG(sz)
+
+ pj_assert(0);
+ exit(1);
+}
+
+static const char *STATUS_STR(pj_status_t status)
+{
+ switch (status) {
+ case 0: return "OK";
+ case ERR_SYNTAX_ERR: return "Syntax Error";
+ case ERR_NOT_EQUAL: return "Not Equal";
+ case ERR_SYSTEM: return "System Error";
+ }
+ return "???";
+}
+
+static pj_status_t test_entry( struct test_msg *entry )
+{
+ pjsip_msg *parsed_msg, *ref_msg;
+ pj_pool_t *pool;
+ pj_status_t status = PJ_SUCCESS;
+ int len;
+ pj_str_t str1, str2;
+ pjsip_hdr *hdr1, *hdr2;
+ pj_hr_timestamp t1, t2;
+ char *msgbuf;
+
+ enum { BUFLEN = 512 };
+
+ pool = pj_pool_create( pf, "",
+ PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA,
+ &pool_error);
+
+ if (entry->len == 0) {
+ entry->len = strlen(entry->msg);
+ }
+
+ /* Parse message. */
+ parse_len += entry->len;
+ pj_hr_gettimestamp(&t1);
+ parsed_msg = pjsip_parse_msg(pool, entry->msg, entry->len, NULL);
+ if (parsed_msg == NULL) {
+ status = ERR_SYNTAX_ERR;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&t2);
+ parse_time += t2.u32.lo - t1.u32.lo;
+
+#if IS_PROFILING
+ goto print_msg;
+#endif
+
+ /* Create reference message. */
+ ref_msg = entry->creator(pool);
+
+ /* Create buffer for comparison. */
+ str1.ptr = pj_pool_alloc(pool, BUFLEN);
+ str2.ptr = pj_pool_alloc(pool, BUFLEN);
+
+ /* Compare message type. */
+ if (parsed_msg->type != ref_msg->type) {
+ status = ERR_NOT_EQUAL;
+ goto on_return;
+ }
+
+ /* Compare request or status line. */
+ if (parsed_msg->type == PJSIP_REQUEST_MSG) {
+ pjsip_method *m1 = &parsed_msg->line.req.method;
+ pjsip_method *m2 = &ref_msg->line.req.method;
+
+ if (m1->id != m2->id || pj_strcmp(&m1->name, &m2->name)) {
+ status = ERR_NOT_EQUAL;
+ goto on_return;
+ }
+ } else {
+
+ }
+
+ /* Compare headers. */
+ hdr1 = parsed_msg->hdr.next;
+ hdr2 = ref_msg->hdr.next;
+
+ while (hdr1 != &parsed_msg->hdr && hdr2 != &ref_msg->hdr) {
+ len = hdr1->vptr->print_on(hdr1, str1.ptr, BUFLEN);
+ if (len < 1) {
+ status = ERR_SYSTEM;
+ goto on_return;
+ }
+ str1.slen = len;
+
+ len = hdr2->vptr->print_on(hdr2, str2.ptr, BUFLEN);
+ if (len < 1) {
+ status = ERR_SYSTEM;
+ goto on_return;
+ }
+ str2.slen = len;
+
+ if (!SILENT) {
+ printf("hdr1='%.*s'\n"
+ "hdr2='%.*s'\n\n",
+ str1.slen, str1.ptr,
+ str2.slen, str2.ptr);
+ }
+ if (pj_strcmp(&str1, &str2) != 0) {
+ status = ERR_NOT_EQUAL;
+ goto on_return;
+ }
+
+ hdr1 = hdr1->next;
+ hdr2 = hdr2->next;
+ }
+
+ if (hdr1 != &parsed_msg->hdr || hdr2 != &ref_msg->hdr) {
+ status = ERR_NOT_EQUAL;
+ goto on_return;
+ }
+
+ /* Print message. */
+#if IS_PROFILING
+print_msg:
+#endif
+ msgbuf = pj_pool_alloc(pool, PJSIP_MAX_PKT_LEN);
+ if (msgbuf == NULL) {
+ status = ERR_SYSTEM;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&t1);
+ len = pjsip_msg_print(parsed_msg, msgbuf, PJSIP_MAX_PKT_LEN);
+ if (len < 1) {
+ status = ERR_SYSTEM;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&t2);
+ print_time += t2.u32.lo - t1.u32.lo;
+ status = PJ_SUCCESS;
+
+on_return:
+ pj_pool_release(pool);
+ return status;
+}
+
+static void warm_up()
+{
+ pj_pool_t *pool;
+ pool = pj_pool_create( pf, "",
+ PJSIP_POOL_LEN_RDATA*2, PJSIP_POOL_INC_RDATA,
+ &pool_error);
+ pj_pool_release(pool);
+}
+
+
+pj_status_t test_msg(void)
+{
+ pj_status_t status;
+ unsigned i;
+
+ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+ warm_up();
+
+ for (i=0; i<LOOP; ++i) {
+ status = test_entry( &test_array[0] );
+ }
+ printf("%s\n", STATUS_STR(status));
+
+ printf("Total bytes: %u, parse time=%f/char, print time=%f/char\n",
+ parse_len,
+ parse_time*1.0/parse_len,
+ print_time*1.0/parse_len);
+ return PJ_SUCCESS;
+}
+
+/*****************************************************************************/
+
+static pjsip_msg *create_msg0(pj_pool_t *pool)
+{
+
+ pjsip_msg *msg;
+ pjsip_name_addr *name_addr;
+ pjsip_url *url;
+ pjsip_fromto_hdr *fromto;
+ pjsip_cid_hdr *cid;
+ pjsip_clen_hdr *clen;
+ pjsip_cseq_hdr *cseq;
+ pjsip_contact_hdr *contact;
+ pjsip_ctype_hdr *ctype;
+ pjsip_routing_hdr *routing;
+ pjsip_via_hdr *via;
+ pjsip_generic_string_hdr *generic;
+ pj_str_t str;
+
+ msg = pjsip_msg_create(pool, PJSIP_REQUEST_MSG);
+
+ /* "INVITE sip:user@foo SIP/2.0\n" */
+ pjsip_method_set(&msg->line.req.method, PJSIP_INVITE_METHOD);
+ url = pjsip_url_create(pool, 0);
+ msg->line.req.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "foo");
+
+ /* "From: Hi I'm Joe <sip:joe.user@bar.otherdomain.com>;tag=1234578901234567890\r" */
+ fromto = pjsip_from_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
+ pj_strdup2(pool, &fromto->tag, "1234578901234567890");
+ 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);
+ name_addr->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "joe.user");
+ pj_strdup2(pool, &url->host, "bar.otherdomain.com");
+
+ /* "To: Fellow User <sip:user@foo.bar.domain.com>\r\n" */
+ fromto = pjsip_to_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)fromto);
+ 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);
+ name_addr->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "foo.bar.domain.com");
+
+ /* "Call-ID: 12345678901234567890@bar\r\n" */
+ cid = pjsip_cid_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)cid);
+ pj_strdup2(pool, &cid->id, "12345678901234567890@bar");
+
+ /* "Content-Length: 0\r\n" */
+ clen = pjsip_clen_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)clen);
+ clen->len = 0;
+
+ /* "CSeq: 123456 INVITE\n" */
+ cseq = pjsip_cseq_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)cseq);
+ cseq->cseq = 123456;
+ pjsip_method_set(&cseq->method, PJSIP_INVITE_METHOD);
+
+ /* "Contact: <sip:joe@bar>;q=0.5;expires=3600*/
+ contact = pjsip_contact_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+ contact->q1000 = 500;
+ contact->expires = 3600;
+ name_addr = pjsip_name_addr_create(pool);
+ contact->uri = (pjsip_uri*)name_addr;
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "joe");
+ pj_strdup2(pool, &url->host, "bar");
+
+ /*, sip:user@host;q=0.500\r" */
+ contact = pjsip_contact_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+ contact->q1000 = 500;
+ url = pjsip_url_create(pool, 0);
+ contact->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "host");
+
+ /* " ,sip:user2@host2\n" */
+ contact = pjsip_contact_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)contact);
+ url = pjsip_url_create(pool, 0);
+ contact->uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->user, "user2");
+ pj_strdup2(pool, &url->host, "host2");
+
+ /* "Content-Type: text/html; charset=ISO-8859-4\r" */
+ ctype = pjsip_ctype_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)ctype);
+ pj_strdup2(pool, &ctype->media.type, "text");
+ pj_strdup2(pool, &ctype->media.subtype, "html");
+ pj_strdup2(pool, &ctype->media.param, ";charset=ISO-8859-4");
+
+ /* "Route: <sip:bigbox3.site3.atlanta.com;lr>,\r\n" */
+ routing = pjsip_route_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
+ url->lr_param = 1;
+
+ /* " <sip:server10.biloxi.com;lr>\r" */
+ routing = pjsip_route_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "server10.biloxi.com");
+ url->lr_param = 1;
+
+ /* "Record-Route: <sip:server10.biloxi.com>,\r\n" */
+ routing = pjsip_rr_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "server10.biloxi.com");
+ url->lr_param = 0;
+
+ /* " <sip:bigbox3.site3.atlanta.com;lr>\n" */
+ routing = pjsip_rr_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)routing);
+ url = pjsip_url_create(pool, 0);
+ routing->name_addr.uri = (pjsip_uri*)url;
+ pj_strdup2(pool, &url->host, "bigbox3.site3.atlanta.com");
+ url->lr_param = 1;
+
+ /* "Via: SIP/2.0/SCTP bigbox3.site3.atlanta.com;branch=z9hG4bK77ef4c2312983.1\n" */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "SCTP");
+ pj_strdup2(pool, &via->sent_by.host, "bigbox3.site3.atlanta.com");
+ pj_strdup2(pool, &via->branch_param, "z9hG4bK77ef4c2312983.1");
+
+ /* "Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bKnashds8\n"
+ " ;received=192.0.2.1\r\n" */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "UDP");
+ pj_strdup2(pool, &via->sent_by.host, "pc33.atlanta.com");
+ pj_strdup2(pool, &via->branch_param, "z9hG4bKnashds8");
+ pj_strdup2(pool, &via->recvd_param, "192.0.2.1");
+
+
+ /* "Via: SIP/2.0/UDP 10.2.1.1, */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "UDP");
+ pj_strdup2(pool, &via->sent_by.host, "10.2.1.1");
+
+
+ /*SIP/2.0/TCP 192.168.1.1\n" */
+ via = pjsip_via_hdr_create(pool);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)via);
+ pj_strdup2(pool, &via->transport, "TCP");
+ pj_strdup2(pool, &via->sent_by.host, "192.168.1.1");
+
+ /* "Organization: \r" */
+ str.ptr = "Organization";
+ str.slen = 12;
+ generic = pjsip_generic_string_hdr_create(pool, &str);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+ generic->hvalue.ptr = NULL;
+ generic->hvalue.slen = 0;
+
+ /* "Max-Forwards: 70\n" */
+ str.ptr = "Max-Forwards";
+ str.slen = 12;
+ generic = pjsip_generic_string_hdr_create(pool, &str);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+ str.ptr = "70";
+ str.slen = 2;
+ generic->hvalue = str;
+
+ /* "X-Header: \r\n" */
+ str.ptr = "X-Header";
+ str.slen = 8;
+ generic = pjsip_generic_string_hdr_create(pool, &str);
+ pjsip_msg_add_hdr(msg, (pjsip_hdr*)generic);
+ str.ptr = NULL;
+ str.slen = 0;
+ generic->hvalue = str;
+
+ return msg;
+}
diff --git a/pjsip/src/tests/pjsip-core/test_uri.c b/pjsip/src/tests/pjsip-core/test_uri.c index 0c786605..bdbe8f16 100644 --- a/pjsip/src/tests/pjsip-core/test_uri.c +++ b/pjsip/src/tests/pjsip-core/test_uri.c @@ -1,645 +1,667 @@ -/* $Id$ - * - */ -#include <pjsip/sip_parser.h> -#include <pjsip/sip_uri.h> -#include <pj/os.h> -#include <pj/pool.h> -#include <pj/string.h> -#include <stdlib.h> -#include <stdio.h> -#include "test.h" - -#define ERR_SYNTAX_ERR (-2) -#define ERR_NOT_EQUAL (-3) - -#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \ - "0123456789" -#define MARK "-_.!~*'()" -#define USER "&=+$,;?/%" -#define PASS "&=+$,%" -#define PARAM_CHAR "[]/:&+$" MARK "%" - -#define POOL_SIZE 4096 - -static const char *STATUS_STR(pj_status_t status) -{ - switch (status) { - case 0: return "OK"; - case ERR_SYNTAX_ERR: return "Syntax Error"; - case ERR_NOT_EQUAL: return "Not Equal"; - } - return "???"; -} - -static pj_uint32_t parse_len, parse_time, print_time; -static pj_caching_pool cp; - - -/* URI creator functions. */ -static pjsip_uri *create_uri1( pj_pool_t *pool ); -static pjsip_uri *create_uri2( pj_pool_t *pool ); -static pjsip_uri *create_uri3( pj_pool_t *pool ); -static pjsip_uri *create_uri4( pj_pool_t *pool ); -static pjsip_uri *create_uri5( pj_pool_t *pool ); -static pjsip_uri *create_uri6( pj_pool_t *pool ); -static pjsip_uri *create_uri7( pj_pool_t *pool ); -static pjsip_uri *create_uri8( pj_pool_t *pool ); -static pjsip_uri *create_uri9( pj_pool_t *pool ); -static pjsip_uri *create_uri10( pj_pool_t *pool ); -static pjsip_uri *create_uri11( pj_pool_t *pool ); -static pjsip_uri *create_uri12( pj_pool_t *pool ); -static pjsip_uri *create_uri13( pj_pool_t *pool ); -static pjsip_uri *create_uri14( pj_pool_t *pool ); -static pjsip_uri *create_uri15( pj_pool_t *pool ); -static pjsip_uri *create_uri16( pj_pool_t *pool ); -static pjsip_uri *create_uri17( pj_pool_t *pool ); -static pjsip_uri *create_uri18( pj_pool_t *pool ); -static pjsip_uri *create_uri19( pj_pool_t *pool ); -static pjsip_uri *create_dummy( pj_pool_t *pool ); - -struct uri_test -{ - pj_status_t status; - char str[PJSIP_MAX_URL_SIZE]; - pjsip_uri *(*creator)(pj_pool_t *pool); - pj_size_t len; -} uri_test_array[] = -{ - { - PJ_SUCCESS, - "sip:localhost", - &create_uri1 - }, - { - PJ_SUCCESS, - "sip:user@localhost", - &create_uri2 - }, - { - PJ_SUCCESS, - "sip:user:password@localhost:5060", - &create_uri3, - }, - { - /* Port is specified should not match unspecified port. */ - ERR_NOT_EQUAL, - "sip:localhost:5060", - &create_uri4 - }, - { - /* All recognized parameters. */ - PJ_SUCCESS, - "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK", - &create_uri5 - }, - { - /* Params mixed with other params and header params. */ - PJ_SUCCESS, - "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" - "?Subject=Hello%20There&Server=SIP%20Server", - &create_uri6 - }, - { - /* SIPS. */ - PJ_SUCCESS, - "sips:localhost", - &create_uri7, - }, - { - /* Name address */ - PJ_SUCCESS, - "<sip:localhost>", - &create_uri8 - }, - { - /* Name address with display name and SIPS scheme with some redundant - * whitespaced. - */ - PJ_SUCCESS, - " Power Administrator <sips:localhost>", - &create_uri9 - }, - { - /* Name address. */ - PJ_SUCCESS, - " \"User\" <sip:user@localhost:5071>", - &create_uri10 - }, - { - /* Escaped sequence in display name (display=Strange User\"\\\"). */ - PJ_SUCCESS, - " \"Strange User\\\"\\\\\\\"\" <sip:localhost>", - &create_uri11, - }, - { - /* Errorneous escaping in display name. */ - ERR_SYNTAX_ERR, - " \"Rogue User\\\" <sip:localhost>", - &create_uri12, - }, - { - /* Dangling quote in display name, but that should be OK. */ - PJ_SUCCESS, - "Strange User\" <sip:localhost>", - &create_uri13, - }, - { - /* Special characters in parameter value must be quoted. */ - PJ_SUCCESS, - "sip:localhost;pvalue=\"hello world\"", - &create_uri14, - }, - { - /* Excercise strange character sets allowed in display, user, password, - * host, and port. - */ - PJ_SUCCESS, - "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@" - "my_proxy09.MY-domain.com:9801>", - &create_uri15, - }, - { - /* Another excercise to the allowed character sets to the hostname. */ - PJ_SUCCESS, - "sip:" ALPHANUM "-_.com", - &create_uri16, - }, - { - /* Another excercise to the allowed character sets to the username - * and password. - */ - PJ_SUCCESS, - "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host", - &create_uri17, - }, - { - /* Excercise to the pname and pvalue, and mixup of other-param - * between 'recognized' params. - */ - PJ_SUCCESS, - "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR - ";lr;other=1;transport=sctp;other2", - &create_uri18, - }, - { - /* This should trigger syntax error. */ - ERR_SYNTAX_ERR, - "sip:", - &create_dummy, - }, - { - /* Syntax error: whitespace after scheme. */ - ERR_SYNTAX_ERR, - "sip :host", - &create_dummy, - }, - { - /* Syntax error: whitespace before hostname. */ - ERR_SYNTAX_ERR, - "sip: host", - &create_dummy, - }, - { - /* Syntax error: invalid port. */ - ERR_SYNTAX_ERR, - "sip:user:password", - &create_dummy, - }, - { - /* Syntax error: no host. */ - ERR_SYNTAX_ERR, - "sip:user@", - &create_dummy, - }, - { - /* Syntax error: no user/host. */ - ERR_SYNTAX_ERR, - "sip:@", - &create_dummy, - }, - { - /* Syntax error: empty string. */ - ERR_SYNTAX_ERR, - "", - &create_dummy, - }, - { - PJ_SUCCESS, - "", - NULL, - }, -}; - -static pjsip_uri *create_uri1(pj_pool_t *pool) -{ - /* "sip:localhost" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri2(pj_pool_t *pool) -{ - /* "sip:user@localhost" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2( pool, &url->user, "user"); - pj_strdup2( pool, &url->host, "localhost"); - - return (pjsip_uri*) url; -} - -static pjsip_uri *create_uri3(pj_pool_t *pool) -{ - /* "sip:user:password@localhost:5060" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2( pool, &url->user, "user"); - pj_strdup2( pool, &url->passwd, "password"); - pj_strdup2( pool, &url->host, "localhost"); - url->port = 5060; - - return (pjsip_uri*) url; -} - -static pjsip_uri *create_uri4(pj_pool_t *pool) -{ - /* Like: "sip:localhost:5060", but without the port. */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri5(pj_pool_t *pool) -{ - /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - pj_strdup2(pool, &url->transport_param, "tcp"); - pj_strdup2(pool, &url->user_param, "ip"); - url->ttl_param = 255; - url->lr_param = 1; - pj_strdup2(pool, &url->maddr_param, "127.0.0.1"); - pj_strdup2(pool, &url->method_param, "ACK"); - - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri6(pj_pool_t *pool) -{ - /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry" - "?Subject=Hello%20There&Server=SIP%20Server" - */ - pjsip_url *url = pjsip_url_create(pool, 0); - - pj_strdup2(pool, &url->host, "localhost"); - pj_strdup2(pool, &url->user_param, "phone"); - pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry"); - pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server"); - return (pjsip_uri*)url; - -} - -static pjsip_uri *create_uri7(pj_pool_t *pool) -{ - /* "sips:localhost" */ - pjsip_url *url = pjsip_url_create(pool, 1); - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri8(pj_pool_t *pool) -{ - /* "<sip:localhost>" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri9(pj_pool_t *pool) -{ - /* " Power Administrator <sips:localhost>" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 1); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "Power Administrator"); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri10(pj_pool_t *pool) -{ - /* " \"User\" <sip:user@localhost:5071>" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "\"User\""); - pj_strdup2(pool, &url->user, "user"); - pj_strdup2(pool, &url->host, "localhost"); - url->port = 5071; - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri11(pj_pool_t *pool) -{ - /* " \"Strange User\\\"\\\\\\\"\" <sip:localhost>" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "\"Strange User\\\"\\\\\\\"\""); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri12(pj_pool_t *pool) -{ - /* " \"Rogue User\\\" <sip:localhost>" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "\"Rogue User\\\""); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri13(pj_pool_t *pool) -{ - /* "Strange User\" <sip:localhost>" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "Strange User\""); - pj_strdup2(pool, &url->host, "localhost"); - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri14(pj_pool_t *pool) -{ - /* "sip:localhost;pvalue=\"hello world\"" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->host, "localhost"); - pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\""); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri15(pj_pool_t *pool) -{ - /* "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@my_proxy09.my-domain.com:9801>" */ - pjsip_name_addr *name_addr = pjsip_name_addr_create(pool); - pjsip_url *url; - - url = pjsip_url_create(pool, 0); - name_addr->uri = (pjsip_uri*) url; - - pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me"); - pj_strdup2(pool, &url->user, "a19A&=+$,;?/%2c"); - pj_strdup2(pool, &url->passwd, "%09a&Zz=+$,"); - pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com"); - url->port = 9801; - return (pjsip_uri*)name_addr; -} - -static pjsip_uri *create_uri16(pj_pool_t *pool) -{ - /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->host, ALPHANUM "-_.com"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri17(pj_pool_t *pool) -{ - /* "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->user, ALPHANUM USER); - pj_strdup2(pool, &url->passwd, ALPHANUM PASS); - pj_strdup2(pool, &url->host, "host"); - return (pjsip_uri*)url; -} - -static pjsip_uri *create_uri18(pj_pool_t *pool) -{ - /* "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";lr;other=1;transport=sctp;other2" */ - pjsip_url *url; - url = pjsip_url_create(pool, 0); - pj_strdup2(pool, &url->host, "host"); - pj_strdup2(pool, &url->user_param, "ip"); - pj_strdup2(pool, &url->transport_param, "sctp"); - pj_strdup2(pool, &url->other_param, ";" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";other=1;other2"); - url->lr_param = 1; - return (pjsip_uri*)url; -} - -static pjsip_uri *create_dummy(pj_pool_t *pool) -{ - PJ_UNUSED_ARG(pool) - return NULL; -} - -/*****************************************************************************/ - -static void pool_error(pj_pool_t *pool, pj_size_t sz) -{ - PJ_UNUSED_ARG(pool) - PJ_UNUSED_ARG(sz) - - pj_assert(0); - exit(1); -} - -/* - * Test one test entry. - */ -static pj_status_t test_entry(struct uri_test *entry) -{ - pj_status_t status; - pj_pool_t *pool; - int len; - pjsip_uri *parsed_uri, *ref_uri; - pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0}; - pj_hr_timestamp t1, t2; - - pool = (*cp.factory.create_pool)( &cp.factory, "", POOL_SIZE, 0, &pool_error); - - /* Parse URI text. */ - pj_hr_gettimestamp(&t1); - parse_len += entry->len; - parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0); - if (!parsed_uri) { - /* Parsing failed. If the entry says that this is expected, then - * return OK. - */ - status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : ERR_SYNTAX_ERR; - goto on_return; - } - pj_hr_gettimestamp(&t2); - parse_time += t2.u32.lo - t1.u32.lo; - - /* Create the reference URI. */ - ref_uri = entry->creator(pool); - - /* Print both URI. */ - s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); - s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); - - pj_hr_gettimestamp(&t1); - len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE); - if (len < 1) { - status = -1; - goto on_return; - } - s1.slen = len; - - len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE); - if (len < 1) { - status = -1; - goto on_return; - } - s2.slen = len; - pj_hr_gettimestamp(&t2); - print_time += t2.u32.lo - t1.u32.lo; - - /* Full comparison of parsed URI with reference URI. */ - if (pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri) != 0) { - /* Not equal. See if this is the expected status. */ - status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : ERR_NOT_EQUAL; - goto on_return; - - } else { - /* Equal. See if this is the expected status. */ - status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -1; - if (status != PJ_SUCCESS) { - goto on_return; - } - } - - /* Compare text. */ - if (pj_strcmp(&s1, &s2) != 0) { - /* Not equal. */ - status = ERR_NOT_EQUAL; - } - -on_return: - if (!SILENT) { - printf("%.2d %s (expected status=%s)\n" - " str=%s\n" - " uri=%.*s\n" - " ref=%.*s\n\n", - entry-uri_test_array, - STATUS_STR(status), - STATUS_STR(entry->status), - entry->str, - (int)s1.slen, s1.ptr, (int)s2.slen, s2.ptr); - } - - pj_pool_release(pool); - return status; -} - -static void warm_up(pj_pool_factory *pf) -{ - pj_pool_t *pool; - struct uri_test *entry; - - pool = pj_pool_create(pf, "", POOL_SIZE, 0, &pool_error); - pjsip_parse_uri(pool, "sip:host", 8, 0); - entry = &uri_test_array[0]; - while (entry->creator) { - entry->len = strlen(entry->str); - ++entry; - } - pj_pool_release(pool); -} - -//#if !IS_PROFILING -#if 1 -pj_status_t test_uri() -{ - struct uri_test *entry; - int i=0, err=0; - pj_status_t status; - pj_hr_timestamp t1, t2; - pj_uint32_t total_time; - - pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); - warm_up(&cp.factory); - - pj_hr_gettimestamp(&t1); - for (i=0; i<LOOP; ++i) { - entry = &uri_test_array[0]; - while (entry->creator) { - status = test_entry(entry); - if (status != PJ_SUCCESS) { - ++err; - } - ++entry; - } - } - pj_hr_gettimestamp(&t2); - total_time = t2.u32.lo - t1.u32.lo; - - printf("Error=%d\n", err); - printf("Total parse len: %u bytes\n", parse_len); - printf("Total parse time: %u (%f/char), print time: %u (%f/char)\n", - parse_time, parse_time*1.0/parse_len, - print_time, print_time*1.0/parse_len); - printf("Total time: %u (%f/char)\n", total_time, total_time*1.0/parse_len); - return err; -} - -#else - -pj_status_t test_uri() -{ - struct uri_test *entry; - unsigned i; - - warm_up(); - pj_caching_pool_init(&cp, 1024*1024); - - for (i=0; i<LOOP; ++i) { - entry = &uri_test_array[0]; - while (entry->creator) { - pj_pool_t *pool; - pjsip_uri *uri1, *uri2; - - pool = pj_pool_create( &cp.factory, "", POOL_SIZE, 0, &pool_error); - uri1 = pjsip_parse_uri(pool, entry->str, strlen(entry->str)); - pj_pool_release(pool); - ++entry; - } - } - - return 0; -} - -#endif +/* $Id$
+ *
+ */
+/*
+ * PJSIP - SIP Stack
+ * (C)2003-2005 Benny Prijono <bennylp@bulukucing.org>
+ *
+ * Author:
+ * Benny Prijono <bennylp@bulukucing.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+#include <pjsip/sip_parser.h>
+#include <pjsip/sip_uri.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "test.h"
+
+#define ERR_SYNTAX_ERR (-2)
+#define ERR_NOT_EQUAL (-3)
+
+#define ALPHANUM "abcdefghijklmnopqrstuvwxyz" \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
+ "0123456789"
+#define MARK "-_.!~*'()"
+#define USER "&=+$,;?/%"
+#define PASS "&=+$,%"
+#define PARAM_CHAR "[]/:&+$" MARK "%"
+
+#define POOL_SIZE 4096
+
+static const char *STATUS_STR(pj_status_t status)
+{
+ switch (status) {
+ case 0: return "OK";
+ case ERR_SYNTAX_ERR: return "Syntax Error";
+ case ERR_NOT_EQUAL: return "Not Equal";
+ }
+ return "???";
+}
+
+static pj_uint32_t parse_len, parse_time, print_time;
+static pj_caching_pool cp;
+
+
+/* URI creator functions. */
+static pjsip_uri *create_uri1( pj_pool_t *pool );
+static pjsip_uri *create_uri2( pj_pool_t *pool );
+static pjsip_uri *create_uri3( pj_pool_t *pool );
+static pjsip_uri *create_uri4( pj_pool_t *pool );
+static pjsip_uri *create_uri5( pj_pool_t *pool );
+static pjsip_uri *create_uri6( pj_pool_t *pool );
+static pjsip_uri *create_uri7( pj_pool_t *pool );
+static pjsip_uri *create_uri8( pj_pool_t *pool );
+static pjsip_uri *create_uri9( pj_pool_t *pool );
+static pjsip_uri *create_uri10( pj_pool_t *pool );
+static pjsip_uri *create_uri11( pj_pool_t *pool );
+static pjsip_uri *create_uri12( pj_pool_t *pool );
+static pjsip_uri *create_uri13( pj_pool_t *pool );
+static pjsip_uri *create_uri14( pj_pool_t *pool );
+static pjsip_uri *create_uri15( pj_pool_t *pool );
+static pjsip_uri *create_uri16( pj_pool_t *pool );
+static pjsip_uri *create_uri17( pj_pool_t *pool );
+static pjsip_uri *create_uri18( pj_pool_t *pool );
+static pjsip_uri *create_uri19( pj_pool_t *pool );
+static pjsip_uri *create_dummy( pj_pool_t *pool );
+
+struct uri_test
+{
+ pj_status_t status;
+ char str[PJSIP_MAX_URL_SIZE];
+ pjsip_uri *(*creator)(pj_pool_t *pool);
+ pj_size_t len;
+} uri_test_array[] =
+{
+ {
+ PJ_SUCCESS,
+ "sip:localhost",
+ &create_uri1
+ },
+ {
+ PJ_SUCCESS,
+ "sip:user@localhost",
+ &create_uri2
+ },
+ {
+ PJ_SUCCESS,
+ "sip:user:password@localhost:5060",
+ &create_uri3,
+ },
+ {
+ /* Port is specified should not match unspecified port. */
+ ERR_NOT_EQUAL,
+ "sip:localhost:5060",
+ &create_uri4
+ },
+ {
+ /* All recognized parameters. */
+ PJ_SUCCESS,
+ "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK",
+ &create_uri5
+ },
+ {
+ /* Params mixed with other params and header params. */
+ PJ_SUCCESS,
+ "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
+ "?Subject=Hello%20There&Server=SIP%20Server",
+ &create_uri6
+ },
+ {
+ /* SIPS. */
+ PJ_SUCCESS,
+ "sips:localhost",
+ &create_uri7,
+ },
+ {
+ /* Name address */
+ PJ_SUCCESS,
+ "<sip:localhost>",
+ &create_uri8
+ },
+ {
+ /* Name address with display name and SIPS scheme with some redundant
+ * whitespaced.
+ */
+ PJ_SUCCESS,
+ " Power Administrator <sips:localhost>",
+ &create_uri9
+ },
+ {
+ /* Name address. */
+ PJ_SUCCESS,
+ " \"User\" <sip:user@localhost:5071>",
+ &create_uri10
+ },
+ {
+ /* Escaped sequence in display name (display=Strange User\"\\\"). */
+ PJ_SUCCESS,
+ " \"Strange User\\\"\\\\\\\"\" <sip:localhost>",
+ &create_uri11,
+ },
+ {
+ /* Errorneous escaping in display name. */
+ ERR_SYNTAX_ERR,
+ " \"Rogue User\\\" <sip:localhost>",
+ &create_uri12,
+ },
+ {
+ /* Dangling quote in display name, but that should be OK. */
+ PJ_SUCCESS,
+ "Strange User\" <sip:localhost>",
+ &create_uri13,
+ },
+ {
+ /* Special characters in parameter value must be quoted. */
+ PJ_SUCCESS,
+ "sip:localhost;pvalue=\"hello world\"",
+ &create_uri14,
+ },
+ {
+ /* Excercise strange character sets allowed in display, user, password,
+ * host, and port.
+ */
+ PJ_SUCCESS,
+ "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@"
+ "my_proxy09.MY-domain.com:9801>",
+ &create_uri15,
+ },
+ {
+ /* Another excercise to the allowed character sets to the hostname. */
+ PJ_SUCCESS,
+ "sip:" ALPHANUM "-_.com",
+ &create_uri16,
+ },
+ {
+ /* Another excercise to the allowed character sets to the username
+ * and password.
+ */
+ PJ_SUCCESS,
+ "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host",
+ &create_uri17,
+ },
+ {
+ /* Excercise to the pname and pvalue, and mixup of other-param
+ * between 'recognized' params.
+ */
+ PJ_SUCCESS,
+ "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR
+ ";lr;other=1;transport=sctp;other2",
+ &create_uri18,
+ },
+ {
+ /* This should trigger syntax error. */
+ ERR_SYNTAX_ERR,
+ "sip:",
+ &create_dummy,
+ },
+ {
+ /* Syntax error: whitespace after scheme. */
+ ERR_SYNTAX_ERR,
+ "sip :host",
+ &create_dummy,
+ },
+ {
+ /* Syntax error: whitespace before hostname. */
+ ERR_SYNTAX_ERR,
+ "sip: host",
+ &create_dummy,
+ },
+ {
+ /* Syntax error: invalid port. */
+ ERR_SYNTAX_ERR,
+ "sip:user:password",
+ &create_dummy,
+ },
+ {
+ /* Syntax error: no host. */
+ ERR_SYNTAX_ERR,
+ "sip:user@",
+ &create_dummy,
+ },
+ {
+ /* Syntax error: no user/host. */
+ ERR_SYNTAX_ERR,
+ "sip:@",
+ &create_dummy,
+ },
+ {
+ /* Syntax error: empty string. */
+ ERR_SYNTAX_ERR,
+ "",
+ &create_dummy,
+ },
+ {
+ PJ_SUCCESS,
+ "",
+ NULL,
+ },
+};
+
+static pjsip_uri *create_uri1(pj_pool_t *pool)
+{
+ /* "sip:localhost" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri2(pj_pool_t *pool)
+{
+ /* "sip:user@localhost" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2( pool, &url->user, "user");
+ pj_strdup2( pool, &url->host, "localhost");
+
+ return (pjsip_uri*) url;
+}
+
+static pjsip_uri *create_uri3(pj_pool_t *pool)
+{
+ /* "sip:user:password@localhost:5060" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2( pool, &url->user, "user");
+ pj_strdup2( pool, &url->passwd, "password");
+ pj_strdup2( pool, &url->host, "localhost");
+ url->port = 5060;
+
+ return (pjsip_uri*) url;
+}
+
+static pjsip_uri *create_uri4(pj_pool_t *pool)
+{
+ /* Like: "sip:localhost:5060", but without the port. */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri5(pj_pool_t *pool)
+{
+ /* "sip:localhost;transport=tcp;user=ip;ttl=255;lr;maddr=127.0.0.1;method=ACK" */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ pj_strdup2(pool, &url->transport_param, "tcp");
+ pj_strdup2(pool, &url->user_param, "ip");
+ url->ttl_param = 255;
+ url->lr_param = 1;
+ pj_strdup2(pool, &url->maddr_param, "127.0.0.1");
+ pj_strdup2(pool, &url->method_param, "ACK");
+
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri6(pj_pool_t *pool)
+{
+ /* "sip:localhost;pickup=hurry;user=phone;message=I%20am%20sorry"
+ "?Subject=Hello%20There&Server=SIP%20Server"
+ */
+ pjsip_url *url = pjsip_url_create(pool, 0);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ pj_strdup2(pool, &url->user_param, "phone");
+ pj_strdup2(pool, &url->other_param, ";pickup=hurry;message=I%20am%20sorry");
+ pj_strdup2(pool, &url->header_param, "?Subject=Hello%20There&Server=SIP%20Server");
+ return (pjsip_uri*)url;
+
+}
+
+static pjsip_uri *create_uri7(pj_pool_t *pool)
+{
+ /* "sips:localhost" */
+ pjsip_url *url = pjsip_url_create(pool, 1);
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri8(pj_pool_t *pool)
+{
+ /* "<sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri9(pj_pool_t *pool)
+{
+ /* " Power Administrator <sips:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 1);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "Power Administrator");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri10(pj_pool_t *pool)
+{
+ /* " \"User\" <sip:user@localhost:5071>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "\"User\"");
+ pj_strdup2(pool, &url->user, "user");
+ pj_strdup2(pool, &url->host, "localhost");
+ url->port = 5071;
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri11(pj_pool_t *pool)
+{
+ /* " \"Strange User\\\"\\\\\\\"\" <sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "\"Strange User\\\"\\\\\\\"\"");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri12(pj_pool_t *pool)
+{
+ /* " \"Rogue User\\\" <sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "\"Rogue User\\\"");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri13(pj_pool_t *pool)
+{
+ /* "Strange User\" <sip:localhost>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "Strange User\"");
+ pj_strdup2(pool, &url->host, "localhost");
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri14(pj_pool_t *pool)
+{
+ /* "sip:localhost;pvalue=\"hello world\"" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->host, "localhost");
+ pj_strdup2(pool, &url->other_param, ";pvalue=\"hello world\"");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri15(pj_pool_t *pool)
+{
+ /* "This is -. !% *_+`'~ me <sip:a19A&=+$,;?/%2c:%09a&Zz=+$,@my_proxy09.my-domain.com:9801>" */
+ pjsip_name_addr *name_addr = pjsip_name_addr_create(pool);
+ pjsip_url *url;
+
+ url = pjsip_url_create(pool, 0);
+ name_addr->uri = (pjsip_uri*) url;
+
+ pj_strdup2(pool, &name_addr->display, "This is -. !% *_+`'~ me");
+ pj_strdup2(pool, &url->user, "a19A&=+$,;?/%2c");
+ pj_strdup2(pool, &url->passwd, "%09a&Zz=+$,");
+ pj_strdup2(pool, &url->host, "my_proxy09.MY-domain.com");
+ url->port = 9801;
+ return (pjsip_uri*)name_addr;
+}
+
+static pjsip_uri *create_uri16(pj_pool_t *pool)
+{
+ /* "sip:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.com" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->host, ALPHANUM "-_.com");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri17(pj_pool_t *pool)
+{
+ /* "sip:" ALPHANUM USER ":" ALPHANUM PASS "@host" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->user, ALPHANUM USER);
+ pj_strdup2(pool, &url->passwd, ALPHANUM PASS);
+ pj_strdup2(pool, &url->host, "host");
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_uri18(pj_pool_t *pool)
+{
+ /* "sip:host;user=ip;" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";lr;other=1;transport=sctp;other2" */
+ pjsip_url *url;
+ url = pjsip_url_create(pool, 0);
+ pj_strdup2(pool, &url->host, "host");
+ pj_strdup2(pool, &url->user_param, "ip");
+ pj_strdup2(pool, &url->transport_param, "sctp");
+ pj_strdup2(pool, &url->other_param, ";" ALPHANUM PARAM_CHAR "=" ALPHANUM PARAM_CHAR ";other=1;other2");
+ url->lr_param = 1;
+ return (pjsip_uri*)url;
+}
+
+static pjsip_uri *create_dummy(pj_pool_t *pool)
+{
+ PJ_UNUSED_ARG(pool)
+ return NULL;
+}
+
+/*****************************************************************************/
+
+static void pool_error(pj_pool_t *pool, pj_size_t sz)
+{
+ PJ_UNUSED_ARG(pool)
+ PJ_UNUSED_ARG(sz)
+
+ pj_assert(0);
+ exit(1);
+}
+
+/*
+ * Test one test entry.
+ */
+static pj_status_t test_entry(struct uri_test *entry)
+{
+ pj_status_t status;
+ pj_pool_t *pool;
+ int len;
+ pjsip_uri *parsed_uri, *ref_uri;
+ pj_str_t s1 = {NULL, 0}, s2 = {NULL, 0};
+ pj_hr_timestamp t1, t2;
+
+ pool = (*cp.factory.create_pool)( &cp.factory, "", POOL_SIZE, 0, &pool_error);
+
+ /* Parse URI text. */
+ pj_hr_gettimestamp(&t1);
+ parse_len += entry->len;
+ parsed_uri = pjsip_parse_uri(pool, entry->str, entry->len, 0);
+ if (!parsed_uri) {
+ /* Parsing failed. If the entry says that this is expected, then
+ * return OK.
+ */
+ status = entry->status==ERR_SYNTAX_ERR ? PJ_SUCCESS : ERR_SYNTAX_ERR;
+ goto on_return;
+ }
+ pj_hr_gettimestamp(&t2);
+ parse_time += t2.u32.lo - t1.u32.lo;
+
+ /* Create the reference URI. */
+ ref_uri = entry->creator(pool);
+
+ /* Print both URI. */
+ s1.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+ s2.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+
+ pj_hr_gettimestamp(&t1);
+ len = pjsip_uri_print( PJSIP_URI_IN_OTHER, parsed_uri, s1.ptr, PJSIP_MAX_URL_SIZE);
+ if (len < 1) {
+ status = -1;
+ goto on_return;
+ }
+ s1.slen = len;
+
+ len = pjsip_uri_print( PJSIP_URI_IN_OTHER, ref_uri, s2.ptr, PJSIP_MAX_URL_SIZE);
+ if (len < 1) {
+ status = -1;
+ goto on_return;
+ }
+ s2.slen = len;
+ pj_hr_gettimestamp(&t2);
+ print_time += t2.u32.lo - t1.u32.lo;
+
+ /* Full comparison of parsed URI with reference URI. */
+ if (pjsip_uri_cmp(PJSIP_URI_IN_OTHER, parsed_uri, ref_uri) != 0) {
+ /* Not equal. See if this is the expected status. */
+ status = entry->status==ERR_NOT_EQUAL ? PJ_SUCCESS : ERR_NOT_EQUAL;
+ goto on_return;
+
+ } else {
+ /* Equal. See if this is the expected status. */
+ status = entry->status==PJ_SUCCESS ? PJ_SUCCESS : -1;
+ if (status != PJ_SUCCESS) {
+ goto on_return;
+ }
+ }
+
+ /* Compare text. */
+ if (pj_strcmp(&s1, &s2) != 0) {
+ /* Not equal. */
+ status = ERR_NOT_EQUAL;
+ }
+
+on_return:
+ if (!SILENT) {
+ printf("%.2d %s (expected status=%s)\n"
+ " str=%s\n"
+ " uri=%.*s\n"
+ " ref=%.*s\n\n",
+ entry-uri_test_array,
+ STATUS_STR(status),
+ STATUS_STR(entry->status),
+ entry->str,
+ (int)s1.slen, s1.ptr, (int)s2.slen, s2.ptr);
+ }
+
+ pj_pool_release(pool);
+ return status;
+}
+
+static void warm_up(pj_pool_factory *pf)
+{
+ pj_pool_t *pool;
+ struct uri_test *entry;
+
+ pool = pj_pool_create(pf, "", POOL_SIZE, 0, &pool_error);
+ pjsip_parse_uri(pool, "sip:host", 8, 0);
+ entry = &uri_test_array[0];
+ while (entry->creator) {
+ entry->len = strlen(entry->str);
+ ++entry;
+ }
+ pj_pool_release(pool);
+}
+
+//#if !IS_PROFILING
+#if 1
+pj_status_t test_uri()
+{
+ struct uri_test *entry;
+ int i=0, err=0;
+ pj_status_t status;
+ pj_hr_timestamp t1, t2;
+ pj_uint32_t total_time;
+
+ pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0);
+ warm_up(&cp.factory);
+
+ pj_hr_gettimestamp(&t1);
+ for (i=0; i<LOOP; ++i) {
+ entry = &uri_test_array[0];
+ while (entry->creator) {
+ status = test_entry(entry);
+ if (status != PJ_SUCCESS) {
+ ++err;
+ }
+ ++entry;
+ }
+ }
+ pj_hr_gettimestamp(&t2);
+ total_time = t2.u32.lo - t1.u32.lo;
+
+ printf("Error=%d\n", err);
+ printf("Total parse len: %u bytes\n", parse_len);
+ printf("Total parse time: %u (%f/char), print time: %u (%f/char)\n",
+ parse_time, parse_time*1.0/parse_len,
+ print_time, print_time*1.0/parse_len);
+ printf("Total time: %u (%f/char)\n", total_time, total_time*1.0/parse_len);
+ return err;
+}
+
+#else
+
+pj_status_t test_uri()
+{
+ struct uri_test *entry;
+ unsigned i;
+
+ warm_up();
+ pj_caching_pool_init(&cp, 1024*1024);
+
+ for (i=0; i<LOOP; ++i) {
+ entry = &uri_test_array[0];
+ while (entry->creator) {
+ pj_pool_t *pool;
+ pjsip_uri *uri1, *uri2;
+
+ pool = pj_pool_create( &cp.factory, "", POOL_SIZE, 0, &pool_error);
+ uri1 = pjsip_parse_uri(pool, entry->str, strlen(entry->str));
+ pj_pool_release(pool);
+ ++entry;
+ }
+ }
+
+ return 0;
+}
+
+#endif
|