@ -56,6 +56,9 @@
*/
static struct ao2_container * publishers ;
/*! Serializer for outbound extension state publishing. */
static struct ast_taskprocessor * publish_exten_state_serializer ;
/*!
* \ brief A subscription for extension state
*
@ -542,6 +545,142 @@ static void to_ami(struct ast_sip_subscription *sub,
exten_state_sub - > last_exten_state ) ) ;
}
struct exten_state_pub_data {
/*! Publishers needing state update */
AST_VECTOR ( name , struct exten_state_publisher * ) pubs ;
/*! Body generator state data */
struct ast_sip_exten_state_data exten_state_data ;
} ;
static void exten_state_pub_data_destroy ( struct exten_state_pub_data * doomed )
{
if ( ! doomed ) {
return ;
}
ast_free ( ( void * ) doomed - > exten_state_data . exten ) ;
ast_free ( doomed - > exten_state_data . presence_subtype ) ;
ast_free ( doomed - > exten_state_data . presence_message ) ;
ao2_cleanup ( doomed - > exten_state_data . device_state_info ) ;
AST_VECTOR_CALLBACK_VOID ( & doomed - > pubs , ao2_ref , - 1 ) ;
AST_VECTOR_FREE ( & doomed - > pubs ) ;
ast_free ( doomed ) ;
}
static struct exten_state_pub_data * exten_state_pub_data_alloc ( const char * exten , struct ast_state_cb_info * info )
{
struct exten_state_pub_data * pub_data ;
pub_data = ast_calloc ( 1 , sizeof ( * pub_data ) ) ;
if ( ! pub_data ) {
return NULL ;
}
if ( AST_VECTOR_INIT ( & pub_data - > pubs , ao2_container_count ( publishers ) ) ) {
exten_state_pub_data_destroy ( pub_data ) ;
return NULL ;
}
/* Save off currently known information for the body generators. */
pub_data - > exten_state_data . exten = ast_strdup ( exten ) ;
pub_data - > exten_state_data . exten_state = info - > exten_state ;
pub_data - > exten_state_data . presence_state = info - > presence_state ;
pub_data - > exten_state_data . presence_subtype = ast_strdup ( info - > presence_subtype ) ;
pub_data - > exten_state_data . presence_message = ast_strdup ( info - > presence_message ) ;
pub_data - > exten_state_data . device_state_info = ao2_bump ( info - > device_state_info ) ;
if ( ! pub_data - > exten_state_data . exten
| | ! pub_data - > exten_state_data . presence_subtype
| | ! pub_data - > exten_state_data . presence_message ) {
exten_state_pub_data_destroy ( pub_data ) ;
return NULL ;
}
return pub_data ;
}
/*!
* \ internal
* \ brief Create exten state PUBLISH messages under PJSIP thread .
* \ since 14.0 .0
*
* \ return 0
*/
static int exten_state_publisher_cb ( void * data )
{
struct exten_state_pub_data * pub_data = data ;
struct exten_state_publisher * publisher ;
size_t idx ;
struct ast_str * body_text ;
pj_pool_t * pool ;
struct ast_sip_body_data gen_data = {
. body_type = AST_SIP_EXTEN_STATE_DATA ,
. body_data = & pub_data - > exten_state_data ,
} ;
struct ast_sip_body body ;
body_text = ast_str_create ( 64 ) ;
if ( ! body_text ) {
exten_state_pub_data_destroy ( pub_data ) ;
return 0 ;
}
/* Need a PJSIP memory pool to generate the bodies. */
pool = pjsip_endpt_create_pool ( ast_sip_get_pjsip_endpoint ( ) , " pub_state_body " ,
1024 , 1024 ) ;
if ( ! pool ) {
ast_log ( LOG_WARNING , " Exten state publishing unable to create memory pool \n " ) ;
exten_state_pub_data_destroy ( pub_data ) ;
ast_free ( body_text ) ;
return 0 ;
}
pub_data - > exten_state_data . pool = pool ;
for ( idx = 0 ; idx < AST_VECTOR_SIZE ( & pub_data - > pubs ) ; + + idx ) {
const char * uri ;
int res ;
publisher = AST_VECTOR_GET ( & pub_data - > pubs , idx ) ;
uri = ast_sip_publish_client_get_from_uri ( publisher - > client ) ;
if ( ast_strlen_zero ( uri ) ) {
ast_log ( LOG_WARNING , " PUBLISH client '%s' has no from_uri or server_uri defined. \n " ,
publisher - > name ) ;
continue ;
}
ast_copy_string ( pub_data - > exten_state_data . local , uri , sizeof ( pub_data - > exten_state_data . local ) ) ;
uri = ast_sip_publish_client_get_to_uri ( publisher - > client ) ;
if ( ast_strlen_zero ( uri ) ) {
ast_log ( LOG_WARNING , " PUBLISH client '%s' has no to_uri or server_uri defined. \n " ,
publisher - > name ) ;
continue ;
}
ast_copy_string ( pub_data - > exten_state_data . remote , uri , sizeof ( pub_data - > exten_state_data . remote ) ) ;
res = ast_sip_pubsub_generate_body_content ( publisher - > body_type ,
publisher - > body_subtype , & gen_data , & body_text ) ;
pj_pool_reset ( pool ) ;
if ( res ) {
ast_log ( LOG_WARNING ,
" PUBLISH client '%s' unable to generate %s/%s PUBLISH body. \n " ,
publisher - > name , publisher - > body_type , publisher - > body_subtype ) ;
continue ;
}
body . type = publisher - > body_type ;
body . subtype = publisher - > body_subtype ;
body . body_text = ast_str_buffer ( body_text ) ;
ast_sip_publish_client_send ( publisher - > client , & body ) ;
}
pjsip_endpt_release_pool ( ast_sip_get_pjsip_endpoint ( ) , pool ) ;
ast_free ( body_text ) ;
exten_state_pub_data_destroy ( pub_data ) ;
return 0 ;
}
/*!
* \ brief Global extension state callback function
*/
@ -549,17 +688,46 @@ static int exten_state_publisher_state_cb(const char *context, const char *exten
{
struct ao2_iterator publisher_iter ;
struct exten_state_publisher * publisher ;
struct exten_state_pub_data * pub_data = NULL ;
ast_debug ( 5 , " Exten state publisher: %s@%s Reason:%s State:%s Presence:%s Subtype:'%s' Message:'%s' \n " ,
exten , context ,
info - > reason = = AST_HINT_UPDATE_DEVICE
? " Device "
: info - > reason = = AST_HINT_UPDATE_PRESENCE
? " Presence "
: " Unknown " ,
ast_extension_state2str ( info - > exten_state ) ,
ast_presence_state2str ( info - > presence_state ) ,
S_OR ( info - > presence_subtype , " " ) ,
S_OR ( info - > presence_message , " " ) ) ;
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 */
if ( ! pub_data ) {
pub_data = exten_state_pub_data_alloc ( exten , info ) ;
if ( ! pub_data ) {
ao2_ref ( publisher , - 1 ) ;
break ;
}
}
ao2_ref ( publisher , + 1 ) ;
AST_VECTOR_APPEND ( & pub_data - > pubs , publisher ) ;
ast_debug ( 5 , " '%s' will publish exten state \n " , publisher - > name ) ;
}
ao2_iterator_destroy ( & publisher_iter ) ;
if ( pub_data
& & ast_sip_push_task ( publish_exten_state_serializer , exten_state_publisher_cb ,
pub_data ) ) {
exten_state_pub_data_destroy ( pub_data ) ;
}
return 0 ;
}
@ -755,6 +923,10 @@ static int unload_module(void)
ast_sip_unregister_subscription_handler ( & presence_handler ) ;
ast_extension_state_del ( 0 , exten_state_publisher_state_cb ) ;
ast_taskprocessor_unreference ( publish_exten_state_serializer ) ;
publish_exten_state_serializer = NULL ;
ao2_cleanup ( publishers ) ;
publishers = NULL ;
@ -777,6 +949,12 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE ;
}
publish_exten_state_serializer = ast_sip_create_serializer ( " pjsip/exten_state " ) ;
if ( ! publish_exten_state_serializer ) {
unload_module ( ) ;
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 ) ;