📄 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 + -