@ -20,16 +20,20 @@
< depend > pjproject < / depend >
< depend > res_pjsip < / depend >
< depend > res_pjsip_pubsub < / depend >
< depend > res_pjsip_outbound_publish < / depend >
< support_level > core < / support_level >
* * */
# include "asterisk.h"
# include <regex.h>
# include <pjsip.h>
# include <pjsip_simple.h>
# include <pjlib.h>
# include "asterisk/res_pjsip.h"
# include "asterisk/res_pjsip_outbound_publish.h"
# include "asterisk/res_pjsip_pubsub.h"
# include "asterisk/res_pjsip_body_generator_types.h"
# include "asterisk/module.h"
@ -42,6 +46,16 @@
# define BODY_SIZE 1024
# define EVENT_TYPE_SIZE 50
/*!
* \ brief The number of buckets to use for storing publishers
*/
# define PUBLISHER_BUCKETS 31
/*!
* \ brief Container of active outbound extension state publishers
*/
static struct ao2_container * publishers ;
/*!
* \ brief A subscription for extension state
*
@ -68,6 +82,29 @@ struct exten_state_subscription {
enum ast_presence_state last_presence_state ;
} ;
/*!
* \ brief An extension state publisher
*
*/
struct exten_state_publisher {
/*! Regular expression for context filtering */
regex_t context_regex ;
/*! Regular expression for extension filtering */
regex_t exten_regex ;
/*! Publish client to use for sending publish messages */
struct ast_sip_outbound_publish_client * client ;
/*! Whether context filtering is active */
unsigned int context_filter ;
/*! Whether extension filtering is active */
unsigned int exten_filter ;
/*! The body type to use for this publisher - stored after the name */
char * body_type ;
/*! The body subtype to use for this publisher - stored after the body type */
char * body_subtype ;
/*! The name of this publisher */
char name [ 0 ] ;
} ;
# define DEFAULT_PRESENCE_BODY "application / pidf+xml"
# define DEFAULT_DIALOG_BODY "application / dialog-info+xml"
@ -77,6 +114,9 @@ static int subscription_established(struct ast_sip_subscription *sub);
static void * get_notify_data ( struct ast_sip_subscription * sub ) ;
static void to_ami ( struct ast_sip_subscription * sub ,
struct ast_str * * buf ) ;
static int publisher_start ( struct ast_sip_outbound_publish * configuration ,
struct ast_sip_outbound_publish_client * client ) ;
static int publisher_stop ( struct ast_sip_outbound_publish_client * client ) ;
struct ast_sip_notifier presence_notifier = {
. default_accept = DEFAULT_PRESENCE_BODY ,
@ -101,6 +141,12 @@ struct ast_sip_subscription_handler presence_handler = {
. notifier = & presence_notifier ,
} ;
struct ast_sip_event_publisher_handler presence_publisher = {
. event_name = " presence " ,
. start_publishing = publisher_start ,
. stop_publishing = publisher_stop ,
} ;
struct ast_sip_subscription_handler dialog_handler = {
. event_name = " dialog " ,
. body_type = AST_SIP_EXTEN_STATE_DATA ,
@ -110,6 +156,12 @@ struct ast_sip_subscription_handler dialog_handler = {
. notifier = & dialog_notifier ,
} ;
struct ast_sip_event_publisher_handler dialog_publisher = {
. event_name = " dialog " ,
. start_publishing = publisher_start ,
. stop_publishing = publisher_stop ,
} ;
static void exten_state_subscription_destructor ( void * obj )
{
struct exten_state_subscription * sub = obj ;
@ -490,31 +542,264 @@ static void to_ami(struct ast_sip_subscription *sub,
exten_state_sub - > last_exten_state ) ) ;
}
/*!
* \ brief Global extension state callback function
*/
static int exten_state_publisher_state_cb ( const char * context , const char * exten , struct ast_state_cb_info * info , void * data )
{
struct ao2_iterator publisher_iter ;
struct exten_state_publisher * publisher ;
publisher_iter = ao2_iterator_init ( publishers , 0 ) ;
for ( ; ( publisher = ao2_iterator_next ( & publisher_iter ) ) ; ao2_ref ( publisher , - 1 ) ) {
if ( ( publisher - > context_filter & & regexec ( & publisher - > context_regex , context , 0 , NULL , 0 ) ) | |
( publisher - > exten_filter & & regexec ( & publisher - > exten_regex , exten , 0 , NULL , 0 ) ) ) {
continue ;
}
/* This is a placeholder for additional code to come */
}
ao2_iterator_destroy ( & publisher_iter ) ;
return 0 ;
}
/*!
* \ brief Hashing function for extension state publisher
*/
static int exten_state_publisher_hash ( const void * obj , const int flags )
{
const struct exten_state_publisher * object ;
const char * key ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_KEY :
key = obj ;
break ;
case OBJ_SEARCH_OBJECT :
object = obj ;
key = object - > name ;
break ;
default :
ast_assert ( 0 ) ;
return 0 ;
}
return ast_str_hash ( key ) ;
}
/*!
* \ brief Comparator function for extension state publisher
*/
static int exten_state_publisher_cmp ( void * obj , void * arg , int flags )
{
const struct exten_state_publisher * object_left = obj ;
const struct exten_state_publisher * object_right = arg ;
const char * right_key = arg ;
int cmp ;
switch ( flags & OBJ_SEARCH_MASK ) {
case OBJ_SEARCH_OBJECT :
right_key = object_right - > name ;
/* Fall through */
case OBJ_SEARCH_KEY :
cmp = strcmp ( object_left - > name , right_key ) ;
break ;
case OBJ_SEARCH_PARTIAL_KEY :
/* Not supported by container. */
ast_assert ( 0 ) ;
return 0 ;
default :
cmp = 0 ;
break ;
}
if ( cmp ) {
return 0 ;
}
return CMP_MATCH ;
}
/*!
* \ brief Destructor for extension state publisher
*/
static void exten_state_publisher_destroy ( void * obj )
{
struct exten_state_publisher * publisher = obj ;
if ( publisher - > context_filter ) {
regfree ( & publisher - > context_regex ) ;
}
if ( publisher - > exten_filter ) {
regfree ( & publisher - > exten_regex ) ;
}
ao2_cleanup ( publisher - > client ) ;
}
static int build_regex ( regex_t * regex , const char * text )
{
int res ;
if ( ( res = regcomp ( regex , text , REG_EXTENDED | REG_ICASE | REG_NOSUB ) ) ) {
size_t len = regerror ( res , regex , NULL , 0 ) ;
char buf [ len ] ;
regerror ( res , regex , buf , len ) ;
ast_log ( LOG_ERROR , " Could not compile regex '%s': %s \n " , text , buf ) ;
return - 1 ;
}
return 0 ;
}
static int publisher_start ( struct ast_sip_outbound_publish * configuration , struct ast_sip_outbound_publish_client * client )
{
struct exten_state_publisher * publisher ;
size_t name_size ;
size_t body_type_size ;
size_t body_subtype_size ;
char * body_subtype ;
const char * body_full ;
const char * body_type ;
const char * name ;
const char * context ;
const char * exten ;
name = ast_sorcery_object_get_id ( configuration ) ;
body_full = ast_sorcery_object_get_extended ( configuration , " body " ) ;
if ( ast_strlen_zero ( body_full ) ) {
ast_log ( LOG_ERROR , " Outbound extension state publisher '%s': Body not set \n " ,
name ) ;
return - 1 ;
}
body_subtype = ast_strdupa ( body_full ) ;
body_type = strsep ( & body_subtype , " / " ) ;
if ( ast_strlen_zero ( body_type ) | | ast_strlen_zero ( body_subtype ) ) {
ast_log ( LOG_ERROR , " Outbound extension state publisher '%s': Body '%s' missing type or subtype \n " ,
name , body_full ) ;
return - 1 ;
}
name_size = strlen ( name ) + 1 ;
body_type_size = strlen ( body_type ) + 1 ;
body_subtype_size = strlen ( body_subtype ) + 1 ;
publisher = ao2_alloc_options (
sizeof ( * publisher ) + name_size + body_type_size + body_subtype_size ,
exten_state_publisher_destroy , AO2_ALLOC_OPT_LOCK_NOLOCK ) ;
if ( ! publisher ) {
return - 1 ;
}
ast_copy_string ( publisher - > name , name , name_size ) ;
publisher - > body_type = publisher - > name + name_size ;
ast_copy_string ( publisher - > body_type , body_type , body_type_size ) ;
publisher - > body_subtype = publisher - > body_type + body_type_size ;
ast_copy_string ( publisher - > body_subtype , body_subtype , body_subtype_size ) ;
context = ast_sorcery_object_get_extended ( configuration , " context " ) ;
if ( ! ast_strlen_zero ( context ) ) {
if ( build_regex ( & publisher - > context_regex , context ) ) {
ast_log ( LOG_ERROR , " Outbound extension state publisher '%s': Could not build context filter '%s' \n " ,
name , context ) ;
ao2_ref ( publisher , - 1 ) ;
return - 1 ;
}
publisher - > context_filter = 1 ;
}
exten = ast_sorcery_object_get_extended ( configuration , " exten " ) ;
if ( ! ast_strlen_zero ( exten ) ) {
if ( build_regex ( & publisher - > exten_regex , exten ) ) {
ast_log ( LOG_ERROR , " Outbound extension state publisher '%s': Could not build exten filter '%s' \n " ,
name , exten ) ;
ao2_ref ( publisher , - 1 ) ;
return - 1 ;
}
publisher - > exten_filter = 1 ;
}
publisher - > client = ao2_bump ( client ) ;
ao2_lock ( publishers ) ;
if ( ! ao2_container_count ( publishers ) ) {
ast_extension_state_add ( NULL , NULL , exten_state_publisher_state_cb , NULL ) ;
}
ao2_link_flags ( publishers , publisher , OBJ_NOLOCK ) ;
ao2_unlock ( publishers ) ;
ao2_ref ( publisher , - 1 ) ;
return 0 ;
}
static int publisher_stop ( struct ast_sip_outbound_publish_client * client )
{
ao2_find ( publishers , ast_sorcery_object_get_id ( client ) , OBJ_SEARCH_KEY | OBJ_UNLINK | OBJ_NODATA ) ;
return 0 ;
}
static int unload_module ( void )
{
ast_sip_unregister_event_publisher_handler ( & dialog_publisher ) ;
ast_sip_unregister_subscription_handler ( & dialog_handler ) ;
ast_sip_unregister_event_publisher_handler ( & presence_publisher ) ;
ast_sip_unregister_subscription_handler ( & presence_handler ) ;
ast_extension_state_del ( 0 , exten_state_publisher_state_cb ) ;
ao2_cleanup ( publishers ) ;
publishers = NULL ;
return 0 ;
}
static int load_module ( void )
{
CHECK_PJSIP_MODULE_LOADED ( ) ;
CHECK_PJSIP_PUBSUB_MODULE_LOADED ( ) ;
if ( ! ast_module_check ( " res_pjsip_outbound_publish.so " ) ) {
ast_log ( LOG_WARNING , " This module requires the 'res_pjsip_outbound_publish.so' module to be loaded \n " ) ;
return AST_MODULE_LOAD_DECLINE ;
}
publishers = ao2_container_alloc ( PUBLISHER_BUCKETS , exten_state_publisher_hash ,
exten_state_publisher_cmp ) ;
if ( ! publishers ) {
ast_log ( LOG_WARNING , " Unable to create container to store extension state publishers \n " ) ;
return AST_MODULE_LOAD_DECLINE ;
}
if ( ast_sip_register_subscription_handler ( & presence_handler ) ) {
ast_log ( LOG_WARNING , " Unable to register subscription handler %s \n " ,
presence_handler . event_name ) ;
unload_module ( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
if ( ast_sip_register_event_publisher_handler ( & presence_publisher ) ) {
ast_log ( LOG_WARNING , " Unable to register presence publisher %s \n " ,
presence_publisher . event_name ) ;
unload_module ( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
if ( ast_sip_register_subscription_handler ( & dialog_handler ) ) {
ast_log ( LOG_WARNING , " Unable to register subscription handler %s \n " ,
dialog_handler . event_name ) ;
ast_sip_unregister_subscription_handler ( & presence_handler ) ;
unload_module( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
return AST_MODULE_LOAD_SUCCESS ;
}
if ( ast_sip_register_event_publisher_handler ( & dialog_publisher ) ) {
ast_log ( LOG_WARNING , " Unable to register presence publisher %s \n " ,
dialog_publisher . event_name ) ;
unload_module ( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
static int unload_module ( void )
{
ast_sip_unregister_subscription_handler ( & dialog_handler ) ;
ast_sip_unregister_subscription_handler ( & presence_handler ) ;
return 0 ;
return AST_MODULE_LOAD_SUCCESS ;
}
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_LOAD_ORDER , " PJSIP Extension State Notifications " ,