summaryrefslogtreecommitdiff
path: root/pjsip/src/pjsip-simple/event_notify.c
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip/src/pjsip-simple/event_notify.c')
-rw-r--r--pjsip/src/pjsip-simple/event_notify.c1644
1 files changed, 0 insertions, 1644 deletions
diff --git a/pjsip/src/pjsip-simple/event_notify.c b/pjsip/src/pjsip-simple/event_notify.c
deleted file mode 100644
index 51fe7694..00000000
--- a/pjsip/src/pjsip-simple/event_notify.c
+++ /dev/null
@@ -1,1644 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <pjsip_simple/event_notify.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_util.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_CATCH_ANY {
- 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 );
- }
-}
-