📄 evsub.c
字号:
/* $Id: evsub.c 974 2007-02-19 01:13:53Z bennylp $ *//* * Copyright (C) 2003-2007 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/evsub.h>#include <pjsip-simple/evsub_msg.h>#include <pjsip-simple/errno.h>#include <pjsip/sip_errno.h>#include <pjsip/sip_module.h>#include <pjsip/sip_endpoint.h>#include <pjsip/sip_dialog.h>#include <pjsip/sip_auth.h>#include <pjsip/sip_transaction.h>#include <pjsip/sip_event.h>#include <pj/assert.h>#include <pj/guid.h>#include <pj/log.h>#include <pj/os.h>#include <pj/pool.h>#include <pj/string.h>#define THIS_FILE "evsub.c"/* * Global constant *//* Let's define this enum, so that it'll trigger compilation error * when somebody define the same enum in sip_msg.h */enum{ PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD, PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD};const pjsip_method pjsip_subscribe_method = { PJSIP_SUBSCRIBE_METHOD, { "SUBSCRIBE", 9 }};const pjsip_method pjsip_notify_method = { PJSIP_NOTIFY_METHOD, { "NOTIFY", 6 }};/* * Static prototypes. */static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*);static pj_status_t mod_evsub_unload(void);/* * State names. */static pj_str_t evsub_state_names[] = { { "NULL", 4}, { "SENT", 4}, { "ACCEPTED", 8}, { "PENDING", 7}, { "ACTIVE", 6}, { "TERMINATED", 10}, { "UNKNOWN", 7}};/* * Timer constants. *//* Number of seconds to send SUBSCRIBE before the actual expiration */#define TIME_UAC_REFRESH 5/* Time to wait for the final NOTIFY after sending unsubscription */#define TIME_UAC_TERMINATE 5/* If client responds NOTIFY with non-2xx final response (such as 401), * wait for this seconds for further NOTIFY, otherwise client will * unsubscribe */#define TIME_UAC_WAIT_NOTIFY 5/* * Timer id */enum timer_id{ /* No timer. */ TIMER_TYPE_NONE, /* Time to refresh client subscription. * The action is to call on_client_refresh() callback. */ TIMER_TYPE_UAC_REFRESH, /* UAS timeout after to subscription refresh. * The action is to call on_server_timeout() callback. */ TIMER_TYPE_UAS_TIMEOUT, /* UAC waiting for final NOTIFY after unsubscribing * The action is to terminate. */ TIMER_TYPE_UAC_TERMINATE, /* UAC waiting for further NOTIFY after sending non-2xx response to * NOTIFY. The action is to unsubscribe. */ TIMER_TYPE_UAC_WAIT_NOTIFY, /* Max nb of timer types. */ TIMER_TYPE_MAX};static const char *timer_names[] = { "None", "UAC_REFRESH", "UAS_TIMEOUT" "UAC_TERMINATE", "UAC_WAIT_NOTIFY", "INVALID_TIMER"};/* * Definition of event package. */struct evpkg{ PJ_DECL_LIST_MEMBER(struct evpkg); pj_str_t pkg_name; pjsip_module *pkg_mod; unsigned pkg_expires; pjsip_accept_hdr *pkg_accept;};/* * Event subscription module (mod-evsub). */static struct mod_evsub{ pjsip_module mod; pj_pool_t *pool; pjsip_endpoint *endpt; struct evpkg pkg_list; pjsip_allow_events_hdr *allow_events_hdr;} mod_evsub = { { NULL, NULL, /* prev, next. */ { "mod-evsub", 9 }, /* Name. */ -1, /* Id */ PJSIP_MOD_PRIORITY_DIALOG_USAGE, /* Priority */ NULL, /* load() */ NULL, /* start() */ NULL, /* stop() */ &mod_evsub_unload, /* unload() */ NULL, /* on_rx_request() */ NULL, /* on_rx_response() */ NULL, /* on_tx_request. */ NULL, /* on_tx_response() */ &mod_evsub_on_tsx_state, /* on_tsx_state() */ }};/* * Event subscription session. */struct pjsip_evsub{ char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */ pj_pool_t *pool; /**< Pool. */ pjsip_endpoint *endpt; /**< Endpoint instance. */ pjsip_dialog *dlg; /**< Underlying dialog. */ struct evpkg *pkg; /**< The event package. */ unsigned option; /**< Options. */ pjsip_evsub_user user; /**< Callback. */ pj_bool_t call_cb; /**< Notify callback? */ pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */ pjsip_evsub_state state; /**< Subscription state. */ pj_str_t state_str; /**< String describing the state. */ pjsip_evsub_state dst_state; /**< Pending state to be set. */ pj_str_t dst_state_str;/**< Pending state to be set. */ pjsip_method method; /**< Method that established subscr.*/ pjsip_event_hdr *event; /**< Event description. */ pjsip_expires_hdr *expires; /**< Expires header */ pjsip_accept_hdr *accept; /**< Local Accept header. */ pj_time_val refresh_time; /**< Time to refresh. */ pj_timer_entry timer; /**< Internal timer. */ int pending_tsx; /**< Number of pending transactions.*/ pjsip_transaction *pending_sub; /**< Pending UAC SUBSCRIBE tsx. */ void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */};/* * This is the structure that will be "attached" to dialog. * The purpose is to allow multiple subscriptions inside a dialog. */struct dlgsub{ PJ_DECL_LIST_MEMBER(struct dlgsub); pjsip_evsub *sub;};/* Static vars. */static const pj_str_t STR_EVENT = { "Event", 5 };static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };static const pj_str_t STR_TERMINATED = { "terminated", 10 };static const pj_str_t STR_ACTIVE = { "active", 6 };static const pj_str_t STR_PENDING = { "pending", 7 };static const pj_str_t STR_TIMEOUT = { "timeout", 7};/* * On unload module. */static pj_status_t mod_evsub_unload(void){ pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool); mod_evsub.pool = NULL; return PJ_SUCCESS;}/* Proto for pjsipsimple_strerror(). * Defined in errno.c */PJ_DECL(pj_str_t) pjsipsimple_strerror( pj_status_t statcode, char *buf, pj_size_t bufsize );/* * Init and register module. */PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt){ pj_status_t status; pj_str_t method_tags[] = { { "SUBSCRIBE", 9}, { "NOTIFY", 6} }; pj_register_strerror(PJSIP_SIMPLE_ERRNO_START, PJ_ERRNO_SPACE_SIZE, &pjsipsimple_strerror); PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL); PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP); /* Keep endpoint for future reference: */ mod_evsub.endpt = endpt; /* Init event package list: */ pj_list_init(&mod_evsub.pkg_list); /* Create pool: */ mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 4000, 4000); if (!mod_evsub.pool) return PJ_ENOMEM; /* Register module: */ status = pjsip_endpt_register_module(endpt, &mod_evsub.mod); if (status != PJ_SUCCESS) goto on_error; /* Create Allow-Events header: */ mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool); /* Register SIP-event specific headers parser: */ pjsip_evsub_init_parser(); /* Register new methods SUBSCRIBE and NOTIFY in Allow-ed header */ pjsip_endpt_add_capability(endpt, &mod_evsub.mod, PJSIP_H_ALLOW, NULL, 2, method_tags); /* Done. */ return PJ_SUCCESS;on_error: if (mod_evsub.pool) { pjsip_endpt_release_pool(endpt, mod_evsub.pool); mod_evsub.pool = NULL; } mod_evsub.endpt = NULL; return status;}/* * Get the instance of the module. */PJ_DEF(pjsip_module*) pjsip_evsub_instance(void){ PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL); return &mod_evsub.mod;}/* * Get the event subscription instance in the transaction. */PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx){ return tsx->mod_data[mod_evsub.mod.id];}/* * Set event subscription's module data. */PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id, void *data ){ PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return); sub->mod_data[mod_id] = data;}/* * Get event subscription's module data. */PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id ){ PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL); return sub->mod_data[mod_id];}/* * Find registered event package with matching name. */static struct evpkg* find_pkg(const pj_str_t *event_name){ struct evpkg *pkg; pkg = mod_evsub.pkg_list.next; while (pkg != &mod_evsub.pkg_list) { if (pj_stricmp(&pkg->pkg_name, event_name) == 0) { return pkg; } pkg = pkg->next; } return NULL;}/* * Register an event package */PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, const pj_str_t *event_name, unsigned expires, unsigned accept_cnt, const pj_str_t accept[]){ struct evpkg *pkg; unsigned i; PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL); PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values), PJ_ETOOMANY); /* Make sure no module with the specified name already registered: */ PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS); /* Create new event package: */ pkg = pj_pool_alloc(mod_evsub.pool, sizeof(struct evpkg)); pkg->pkg_mod = pkg_mod; pkg->pkg_expires = expires; pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name); pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool); pkg->pkg_accept->count = accept_cnt; for (i=0; i<accept_cnt; ++i) { pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]); } /* Add to package list: */ pj_list_push_back(&mod_evsub.pkg_list, pkg); /* Add to Allow-Events header: */ if (mod_evsub.allow_events_hdr->count != PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values)) { mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] = pkg->pkg_name; ++mod_evsub.allow_events_hdr->count; } /* Add to endpoint's Accept header */ pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod, PJSIP_H_ACCEPT, NULL, pkg->pkg_accept->count, pkg->pkg_accept->values); /* Done */ PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s", (int)event_name->slen, event_name->ptr, (int)pkg_mod->name.slen, pkg_mod->name.ptr)); return PJ_SUCCESS;}/* * Retrieve Allow-Events header */PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m){ struct mod_evsub *mod; if (m == NULL) m = pjsip_evsub_instance(); mod = (struct mod_evsub*)m; return (pjsip_hdr*) mod->allow_events_hdr;}/* * Update expiration time. */static void update_expires( pjsip_evsub *sub, pj_uint32_t interval ){ pj_gettimeofday(&sub->refresh_time); sub->refresh_time.sec += interval;}/* * Schedule timer. */static void set_timer( pjsip_evsub *sub, int timer_id, pj_int32_t seconds){ if (sub->timer.id != TIMER_TYPE_NONE) { PJ_LOG(5,(sub->obj_name, "%s %s timer", (timer_id==sub->timer.id ? "Updating" : "Cancelling"), timer_names[sub->timer.id])); pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); sub->timer.id = TIMER_TYPE_NONE; } if (timer_id != TIMER_TYPE_NONE) { pj_time_val timeout; PJ_ASSERT_ON_FAIL(seconds > 0, return); PJ_ASSERT_ON_FAIL(timer_id>TIMER_TYPE_NONE && timer_id<TIMER_TYPE_MAX, return); timeout.sec = seconds; timeout.msec = 0; sub->timer.id = timer_id; pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout); PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds", timer_names[sub->timer.id], timeout.sec)); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -