@ -42,8 +42,9 @@
# include "asterisk/mwi.h"
struct mwi_subscription ;
static struct ao2_container * unsolicited_mwi ;
static struct ao2_container * solicited_mwi ;
AO2_GLOBAL_OBJ_STATIC ( mwi_unsolicited ) ;
AO2_GLOBAL_OBJ_STATIC ( mwi_solicited ) ;
static char * default_voicemail_extension ;
@ -581,13 +582,15 @@ static int unsubscribe_stasis(void *obj, void *arg, int flags)
}
static int create_unsolicited_mwi_subscriptions ( struct ast_sip_endpoint * endpoint ,
int recreate , int send_now ) ;
int recreate , int send_now , struct ao2_container * unsolicited_mwi , struct ao2_container * solicited_mwi ) ;
static void mwi_subscription_shutdown ( struct ast_sip_subscription * sub )
{
struct mwi_subscription * mwi_sub ;
struct ast_datastore * mwi_datastore ;
struct ast_sip_endpoint * endpoint = NULL ;
struct ao2_container * unsolicited_mwi ;
struct ao2_container * solicited_mwi ;
mwi_datastore = ast_sip_subscription_get_datastore ( sub , MWI_DATASTORE ) ;
if ( ! mwi_datastore ) {
@ -601,18 +604,25 @@ static void mwi_subscription_shutdown(struct ast_sip_subscription *sub)
endpoint = ast_sorcery_retrieve_by_id ( ast_sip_get_sorcery ( ) , " endpoint " , mwi_sub - > id ) ;
ao2_ref ( mwi_datastore , - 1 ) ;
ao2_unlink ( solicited_mwi , mwi_sub ) ;
solicited_mwi = ao2_global_obj_ref ( mwi_solicited ) ;
if ( solicited_mwi ) {
ao2_unlink ( solicited_mwi , mwi_sub ) ;
}
/*
* When a solicited subscription is removed it ' s possible an unsolicited one
* needs to be [ re - ] created . Attempt to establish unsolicited MWI .
*/
unsolicited_mwi = ao2_global_obj_ref ( mwi_unsolicited ) ;
if ( unsolicited_mwi & & endpoint ) {
ao2_lock ( unsolicited_mwi ) ;
create_unsolicited_mwi_subscriptions ( endpoint , 1 , 1 );
create_unsolicited_mwi_subscriptions ( endpoint , 1 , 1 , unsolicited_mwi , solicited_mwi );
ao2_unlock ( unsolicited_mwi ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
}
ao2_cleanup ( solicited_mwi ) ;
ao2_cleanup ( endpoint ) ;
}
@ -678,6 +688,10 @@ static int has_mwi_subscription(struct ao2_container *container,
* mwi_sub = NULL ;
* mwi_stasis = NULL ;
if ( ! container ) {
return 0 ;
}
mwi_subs = ao2_find ( container , ast_sorcery_object_get_id ( endpoint ) ,
OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK ) ;
if ( ! mwi_subs ) {
@ -707,11 +721,13 @@ static int has_mwi_subscription(struct ao2_container *container,
*
* \ param endpoint The endpoint
* \ param mailbox The mailbox
* \ param unsolicited_mwi A container of unsolicited mwi objects
*
* \ retval 1 if a solicited subscription is allowed for the endpoint / mailbox
* 0 otherwise
*/
static int allow_and_or_replace_unsolicited ( struct ast_sip_endpoint * endpoint , const char * mailbox )
static int allow_and_or_replace_unsolicited ( struct ast_sip_endpoint * endpoint , const char * mailbox ,
struct ao2_container * unsolicited_mwi )
{
struct mwi_subscription * mwi_sub ;
struct mwi_stasis_subscription * mwi_stasis ;
@ -758,11 +774,14 @@ static int send_notify(void *obj, void *arg, int flags);
*
* \ param endpoint The endpoint
* \ param mailbox The mailbox
* \ param unsolicited_mwi A container of unsolicited mwi objects
* \ param solicited_mwi A container of solicited mwi objects
*
* \ retval 1 if an unsolicited subscription is allowed for the endpoint / mailbox
* 0 otherwise
*/
static int is_unsolicited_allowed ( struct ast_sip_endpoint * endpoint , const char * mailbox )
static int is_unsolicited_allowed ( struct ast_sip_endpoint * endpoint , const char * mailbox ,
struct ao2_container * unsolicited_mwi , struct ao2_container * solicited_mwi )
{
struct mwi_subscription * mwi_sub ;
struct mwi_stasis_subscription * mwi_stasis ;
@ -828,28 +847,41 @@ static int mwi_validate_for_aor(void *obj, void *arg, int flags)
struct ast_sip_endpoint * endpoint = arg ;
char * mailboxes ;
char * mailbox ;
struct ao2_container * unsolicited_mwi ;
if ( ast_strlen_zero ( aor - > mailboxes ) ) {
return 0 ;
}
/* A reload could be taking place so lock while checking if allowed */
ao2_lock ( unsolicited_mwi ) ;
unsolicited_mwi = ao2_global_obj_ref ( mwi_unsolicited ) ;
if ( unsolicited_mwi ) {
ao2_lock ( unsolicited_mwi ) ;
}
mailboxes = ast_strdupa ( aor - > mailboxes ) ;
while ( ( mailbox = ast_strip ( strsep ( & mailboxes , " , " ) ) ) ) {
if ( ast_strlen_zero ( mailbox ) ) {
continue ;
}
if ( ! allow_and_or_replace_unsolicited ( endpoint , mailbox )) {
if ( ! allow_and_or_replace_unsolicited ( endpoint , mailbox , unsolicited_mwi )) {
ast_debug ( 1 , " Endpoint '%s' already configured for unsolicited MWI for mailbox '%s'. "
" Denying MWI subscription to %s \n " , ast_sorcery_object_get_id ( endpoint ) , mailbox ,
ast_sorcery_object_get_id ( aor ) ) ;
ao2_unlock ( unsolicited_mwi ) ;
if ( unsolicited_mwi ) {
ao2_unlock ( unsolicited_mwi ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
}
return - 1 ;
}
}
ao2_unlock ( unsolicited_mwi ) ;
if ( unsolicited_mwi ) {
ao2_unlock ( unsolicited_mwi ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
}
return 0 ;
}
@ -975,6 +1007,7 @@ static int mwi_subscription_established(struct ast_sip_subscription *sip_sub)
const char * resource = ast_sip_subscription_get_resource_name ( sip_sub ) ;
struct mwi_subscription * sub ;
struct ast_sip_endpoint * endpoint = ast_sip_subscription_get_endpoint ( sip_sub ) ;
struct ao2_container * solicited_mwi ;
/* no aor in uri? subscribe to all on endpoint */
if ( ast_strlen_zero ( resource ) ) {
@ -995,7 +1028,12 @@ static int mwi_subscription_established(struct ast_sip_subscription *sip_sub)
ast_sip_subscription_remove_datastore ( sip_sub , MWI_DATASTORE ) ;
}
ao2_link ( solicited_mwi , sub ) ;
solicited_mwi = ao2_global_obj_ref ( mwi_solicited ) ;
if ( solicited_mwi ) {
ao2_link ( solicited_mwi , sub ) ;
ao2_ref ( solicited_mwi , - 1 ) ;
}
ao2_cleanup ( sub ) ;
ao2_cleanup ( endpoint ) ;
return 0 ;
@ -1142,11 +1180,13 @@ static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
* \ param endpoint An endpoint object
* \ param recreate Whether or not unsolicited subscriptions are potentially being recreated
* \ param send_now Whether or not to send a notify once the subscription is created
* \ param unsolicited_mwi A container of unsolicited mwi objects
* \ param solicited_mwi A container of solicited mwi objects
*
* \ retval 0
*/
static int create_unsolicited_mwi_subscriptions ( struct ast_sip_endpoint * endpoint ,
int recreate , int send_now )
int recreate , int send_now , struct ao2_container * unsolicited_mwi , struct ao2_container * solicited_mwi )
{
RAII_VAR ( struct mwi_subscription * , aggregate_sub , NULL , ao2_cleanup ) ;
char * mailboxes ;
@ -1181,14 +1221,16 @@ static int create_unsolicited_mwi_subscriptions(struct ast_sip_endpoint *endpoin
}
/* Lock solicited so we don't potentially add to both containers */
ao2_lock ( solicited_mwi ) ;
if ( solicited_mwi ) {
ao2_lock ( solicited_mwi ) ;
}
mailboxes = ast_strdupa ( endpoint - > subscription . mwi . mailboxes ) ;
while ( ( mailbox = ast_strip ( strsep ( & mailboxes , " , " ) ) ) ) {
struct mwi_subscription * sub ;
struct mwi_stasis_subscription * mwi_stasis_sub ;
if ( ! is_unsolicited_allowed ( endpoint , mailbox )) {
if ( ! is_unsolicited_allowed ( endpoint , mailbox , unsolicited_mwi , solicited_mwi )) {
continue ;
}
@ -1228,13 +1270,16 @@ static int create_unsolicited_mwi_subscriptions(struct ast_sip_endpoint *endpoin
}
}
ao2_unlock ( solicited_mwi ) ;
if ( solicited_mwi ) {
ao2_unlock ( solicited_mwi ) ;
}
return 0 ;
}
static int create_mwi_subscriptions_for_endpoint ( void * obj , void * arg , int flags )
static int create_mwi_subscriptions_for_endpoint ( void * obj , void * arg , void * data , int flags )
{
return create_unsolicited_mwi_subscriptions ( obj , 0 , 0 );
return create_unsolicited_mwi_subscriptions ( obj , 0 , 0 , arg , data );
}
static int unsubscribe ( void * obj , void * arg , int flags )
@ -1248,9 +1293,16 @@ static int unsubscribe(void *obj, void *arg, int flags)
static void create_mwi_subscriptions ( void )
{
struct ao2_container * unsolicited_mwi ;
struct ao2_container * solicited_mwi ;
struct ao2_container * endpoints ;
struct ast_variable * var ;
unsolicited_mwi = ao2_global_obj_ref ( mwi_unsolicited ) ;
if ( ! unsolicited_mwi ) {
return ;
}
var = ast_variable_new ( " mailboxes != " , " " , " " ) ;
endpoints = ast_sorcery_retrieve_by_fields ( ast_sip_get_sorcery ( ) , " endpoint " ,
@ -1258,9 +1310,12 @@ static void create_mwi_subscriptions(void)
ast_variables_destroy ( var ) ;
if ( ! endpoints ) {
ao2_ref ( unsolicited_mwi , - 1 ) ;
return ;
}
solicited_mwi = ao2_global_obj_ref ( mwi_solicited ) ;
/* We remove all the old stasis subscriptions first before applying the new configuration. This
* prevents a situation where there might be multiple overlapping stasis subscriptions for an
* endpoint for mailboxes . Though there may be mailbox changes during the gap between unsubscribing
@ -1269,10 +1324,12 @@ static void create_mwi_subscriptions(void)
*/
ao2_lock ( unsolicited_mwi ) ;
ao2_callback ( unsolicited_mwi , OBJ_NOLOCK | OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE , unsubscribe , NULL ) ;
ao2_callback ( endpoints , OBJ_NODATA , create_mwi_subscriptions_for_endpoint , NULL ) ;
ao2_callback _data ( endpoints , OBJ_NODATA , create_mwi_subscriptions_for_endpoint , unsolicited_mwi , solicited_mwi ) ;
ao2_unlock ( unsolicited_mwi ) ;
ao2_ref ( endpoints , - 1 ) ;
ao2_cleanup ( solicited_mwi ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
}
/*! \brief Function called to send MWI NOTIFY on any unsolicited mailboxes relating to this AOR */
@ -1299,6 +1356,8 @@ static void mwi_contact_changed(const struct ast_sip_contact *contact)
char * id = ast_strdupa ( ast_sorcery_object_get_id ( contact ) ) ;
char * aor = NULL ;
struct ast_sip_endpoint * endpoint = NULL ;
struct ao2_container * unsolicited_mwi ;
struct ao2_container * solicited_mwi ;
if ( contact - > endpoint ) {
endpoint = ao2_bump ( contact - > endpoint ) ;
@ -1313,10 +1372,20 @@ static void mwi_contact_changed(const struct ast_sip_contact *contact)
return ;
}
unsolicited_mwi = ao2_global_obj_ref ( mwi_unsolicited ) ;
if ( ! unsolicited_mwi ) {
ao2_cleanup ( endpoint ) ;
return ;
}
solicited_mwi = ao2_global_obj_ref ( mwi_solicited ) ;
ao2_lock ( unsolicited_mwi ) ;
create_mwi_subscriptions_for_endpoint ( endpoint , NULL , 0 ) ;
create_ unsolicited_mwi_subscriptions( endpoint , 0 , 0 , unsolicited_mwi , solicited_mwi ) ;
ao2_unlock ( unsolicited_mwi ) ;
ao2_cleanup ( endpoint ) ;
ao2_cleanup ( solicited_mwi ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
aor = strsep ( & id , " ;@ " ) ;
ao2_callback ( unsolicited_mwi , OBJ_NODATA , send_contact_notify , aor ) ;
@ -1342,6 +1411,7 @@ static void mwi_contact_deleted(const void *object)
struct mwi_subscription * mwi_sub ;
struct ast_sip_endpoint * endpoint = NULL ;
struct ast_sip_contact * found_contact ;
struct ao2_container * unsolicited_mwi ;
if ( contact - > endpoint ) {
endpoint = ao2_bump ( contact - > endpoint ) ;
@ -1364,6 +1434,11 @@ static void mwi_contact_deleted(const void *object)
return ;
}
unsolicited_mwi = ao2_global_obj_ref ( mwi_unsolicited ) ;
if ( ! unsolicited_mwi ) {
return ;
}
ao2_lock ( unsolicited_mwi ) ;
mwi_subs = ao2_find ( unsolicited_mwi , contact - > endpoint_name ,
OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK | OBJ_UNLINK ) ;
@ -1374,6 +1449,7 @@ static void mwi_contact_deleted(const void *object)
ao2_iterator_destroy ( mwi_subs ) ;
}
ao2_unlock ( unsolicited_mwi ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
}
/*! \brief Observer for contacts so unsolicited MWI is sent when a contact changes */
@ -1386,7 +1462,12 @@ static const struct ast_sorcery_observer mwi_contact_observer = {
/*! \brief Task invoked to send initial MWI NOTIFY for unsolicited */
static int send_initial_notify_all ( void * obj )
{
ao2_callback ( unsolicited_mwi , OBJ_NODATA , send_notify , NULL ) ;
struct ao2_container * unsolicited_mwi = ao2_global_obj_ref ( mwi_unsolicited ) ;
if ( unsolicited_mwi ) {
ao2_callback ( unsolicited_mwi , OBJ_NODATA , send_notify , NULL ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
}
return 0 ;
}
@ -1434,8 +1515,38 @@ static int reload(void)
return 0 ;
}
static int unload_module ( void )
{
struct ao2_container * unsolicited_mwi ;
ast_sorcery_observer_remove ( ast_sip_get_sorcery ( ) , " global " , & global_observer ) ;
ast_sorcery_observer_remove ( ast_sip_get_sorcery ( ) , " contact " , & mwi_contact_observer ) ;
unsolicited_mwi = ao2_global_obj_replace ( mwi_unsolicited , NULL ) ;
if ( unsolicited_mwi ) {
ao2_callback ( unsolicited_mwi , OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE , unsubscribe , NULL ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
}
ao2_global_obj_release ( mwi_solicited ) ;
if ( ast_serializer_pool_destroy ( mwi_serializer_pool ) ) {
ast_log ( LOG_WARNING , " Unload incomplete. Try again later \n " ) ;
return - 1 ;
}
mwi_serializer_pool = NULL ;
ast_sip_unregister_subscription_handler ( & mwi_handler ) ;
ast_free ( default_voicemail_extension ) ;
default_voicemail_extension = NULL ;
return 0 ;
}
static int load_module ( void )
{
struct ao2_container * mwi_container ;
if ( ast_sip_register_subscription_handler ( & mwi_handler ) ) {
return AST_MODULE_LOAD_DECLINE ;
}
@ -1446,22 +1557,23 @@ static int load_module(void)
ast_log ( AST_LOG_WARNING , " Failed to create MWI serializer pool. The default SIP pool will be used for MWI \n " ) ;
}
solicited_mwi = ao2_container_alloc_hash ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , MWI_BUCKETS ,
mwi_container = ao2_container_alloc_hash ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , MWI_BUCKETS ,
mwi_sub_hash , NULL , mwi_sub_cmp ) ;
if ( ! solicited_mwi ) {
ast_serializer_pool_destroy ( mwi_serializer_pool ) ;
ast_sip_unregister_subscription_handler ( & mwi_handler ) ;
if ( ! mwi_container ) {
unload_module ( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
ao2_global_obj_replace_unref ( mwi_solicited , mwi_container ) ;
ao2_ref ( mwi_container , - 1 ) ;
unsolicited_mwi = ao2_container_alloc_hash ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , MWI_BUCKETS ,
mwi_container = ao2_container_alloc_hash ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , MWI_BUCKETS ,
mwi_sub_hash , NULL , mwi_sub_cmp ) ;
if ( ! unsolicited_mwi ) {
ast_serializer_pool_destroy ( mwi_serializer_pool ) ;
ast_sip_unregister_subscription_handler ( & mwi_handler ) ;
ao2_ref ( solicited_mwi , - 1 ) ;
if ( ! mwi_container ) {
unload_module ( ) ;
return AST_MODULE_LOAD_DECLINE ;
}
ao2_global_obj_replace_unref ( mwi_unsolicited , mwi_container ) ;
ao2_ref ( mwi_container , - 1 ) ;
ast_sorcery_observer_add ( ast_sip_get_sorcery ( ) , " contact " , & mwi_contact_observer ) ;
ast_sorcery_observer_add ( ast_sip_get_sorcery ( ) , " global " , & global_observer ) ;
@ -1493,35 +1605,6 @@ static int load_module(void)
return AST_MODULE_LOAD_SUCCESS ;
}
static int unload_module ( void )
{
ast_sorcery_observer_remove ( ast_sip_get_sorcery ( ) , " global " , & global_observer ) ;
ast_sorcery_observer_remove ( ast_sip_get_sorcery ( ) , " contact " , & mwi_contact_observer ) ;
if ( unsolicited_mwi ) {
ao2_callback ( unsolicited_mwi , OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE , unsubscribe , NULL ) ;
ao2_ref ( unsolicited_mwi , - 1 ) ;
unsolicited_mwi = NULL ;
}
if ( solicited_mwi ) {
ao2_ref ( solicited_mwi , - 1 ) ;
solicited_mwi = NULL ;
}
if ( ast_serializer_pool_destroy ( mwi_serializer_pool ) ) {
ast_log ( LOG_WARNING , " Unload incomplete. Try again later \n " ) ;
return - 1 ;
}
mwi_serializer_pool = NULL ;
ast_sip_unregister_subscription_handler ( & mwi_handler ) ;
ast_free ( default_voicemail_extension ) ;
default_voicemail_extension = NULL ;
return 0 ;
}
AST_MODULE_INFO ( ASTERISK_GPL_KEY , AST_MODFLAG_LOAD_ORDER , " PJSIP MWI resource " ,
. support_level = AST_MODULE_SUPPORT_CORE ,
. load = load_module ,