@ -42,6 +42,7 @@
struct mwi_subscription ;
static struct ao2_container * unsolicited_mwi ;
static struct ao2_container * solicited_mwi ;
static char * default_voicemail_extension ;
@ -119,6 +120,8 @@ struct mwi_subscription {
char * aors ;
/*! Is the MWI solicited (i.e. Initiated with an external SUBSCRIBE) ? */
unsigned int is_solicited ;
/*! True if this subscription is to be terminated */
unsigned int terminate ;
/*! Identifier for the subscription.
* The identifier is the same as the corresponding endpoint ' s stasis ID .
* Used as a hash key
@ -665,7 +668,7 @@ static void send_mwi_notify(struct mwi_subscription *sub)
ao2_cleanup ( aor ) ;
ao2_cleanup ( endpoint ) ;
ast_sip_subscription_notify ( sub - > sip_sub , & data , 0 ) ;
ast_sip_subscription_notify ( sub - > sip_sub , & data , sub - > terminate ) ;
return ;
}
@ -676,18 +679,22 @@ static void send_mwi_notify(struct mwi_subscription *sub)
static int unsubscribe_stasis ( void * obj , void * arg , int flags )
{
struct mwi_stasis_subscription * mwi_stasis = obj ;
if ( mwi_stasis - > mwi_subscriber ) {
ast_debug ( 3 , " Removing stasis subscription to mailbox %s \n " , mwi_stasis - > mailbox ) ;
mwi_stasis - > mwi_subscriber = ast_mwi_unsubscribe_and_join ( mwi_stasis - > mwi_subscriber ) ;
}
return CMP_MATCH ;
}
static int create_unsolicited_mwi_subscriptions ( struct ast_sip_endpoint * endpoint ,
int recreate , int send_now ) ;
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 ;
mwi_datastore = ast_sip_subscription_get_datastore ( sub , MWI_DATASTORE ) ;
if ( ! mwi_datastore ) {
@ -695,10 +702,25 @@ static void mwi_subscription_shutdown(struct ast_sip_subscription *sub)
}
mwi_sub = mwi_datastore - > data ;
ao2_callback ( mwi_sub - > stasis_subs , OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE , unsubscribe_stasis , NULL ) ;
ast_sip_subscription_remove_datastore ( sub , MWI_DATASTORE ) ;
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 ) ;
/*
* When a solicited subscription is removed it ' s possible an unsolicited one
* needs to be [ re - ] created . Attempt to establish unsolicited MWI .
*/
if ( unsolicited_mwi & & endpoint ) {
ao2_lock ( unsolicited_mwi ) ;
create_unsolicited_mwi_subscriptions ( endpoint , 1 , 1 ) ;
ao2_unlock ( unsolicited_mwi ) ;
}
ao2_cleanup ( endpoint ) ;
}
static void mwi_ds_destroy ( void * data )
@ -734,43 +756,165 @@ static int add_mwi_datastore(struct mwi_subscription *sub)
}
/*!
* \ brief Determines if an endpoint is receiving unsolicited MWI for a particular mailbox .
* \ internal
* \ brief Determine if an MWI subscription already exists for the given endpoint / mailbox
*
* Search the given container , and attempt to find out if the given endpoint has a
* current subscription within . If so pass back the associated mwi_subscription and
* mwi_stasis_subscription objects .
*
* \ note If a subscription is located then the caller is responsible for removing the
* references to the passed back mwi_subscription and mwi_stasis_subscription objects .
*
* \ note Must be called with the given container already locked .
*
* \ param endpoint The endpoint to check
* \ param mailbox The candidate mailbox
* \ retval 0 The endpoint does not receive unsolicited MWI for this mailbox
* \ retval 1 The endpoint receives unsolicited MWI for this mailbox
* \ param container The ao2_container to search
* \ param endpoint The endpoint to find
* \ param mailbox The mailbox potentially subscribed
* \ param mwi_sub [ out ] May contain the located mwi_subscription
* \ param mwi_stasis [ out ] May contain the located mwi_stasis_subscription
*
* \ retval 1 if a subscription was located , 0 otherwise
*/
static int endpoint_receives_unsolicited_mwi_for_mailbox ( struct ast_sip_endpoint * endpoint ,
const char * mailbox )
static int has_mwi_subscription ( struct ao2_container * container ,
struct ast_sip_endpoint * endpoint , const char * mailbox ,
struct mwi_subscription * * mwi_sub , struct mwi_stasis_subscription * * mwi_stasis )
{
struct ao2_iterator * mwi_subs ;
struct mwi_subscription * mwi_sub ;
const char * endpoint_id = ast_sorcery_object_get_id ( endpoint ) ;
int ret = 0 ;
mwi_subs = ao2_find ( unsolicited_mwi , endpoint_id , OBJ_SEARCH_KEY | OBJ_MULTIPLE ) ;
* mwi_sub = NULL ;
* mwi_stasis = NULL ;
mwi_subs = ao2_find ( container , ast_sorcery_object_get_id ( endpoint ) ,
OBJ_SEARCH_KEY | OBJ_MULTIPLE | OBJ_NOLOCK ) ;
if ( ! mwi_subs ) {
return 0 ;
}
for ( ; ( mwi_sub = ao2_iterator_next ( mwi_subs ) ) & & ! ret ; ao2_cleanup ( mwi_sub ) ) {
struct mwi_stasis_subscription * mwi_stasis ;
mwi_stasis = ao2_find ( mwi_sub - > stasis_subs , mailbox , OBJ_SEARCH_KEY ) ;
if ( mwi_stasis ) {
if ( endpoint - > subscription . mwi . subscribe_replaces_unsolicited ) {
unsubscribe_stasis ( mwi_stasis , NULL , 0 ) ;
ao2_unlink ( mwi_sub - > stasis_subs , mwi_stasis ) ;
} else {
ret = 1 ;
}
ao2_cleanup ( mwi_stasis ) ;
while ( ( * mwi_sub = ao2_iterator_next ( mwi_subs ) ) ) {
* mwi_stasis = ao2_find ( ( * mwi_sub ) - > stasis_subs , mailbox , OBJ_SEARCH_KEY ) ;
if ( * mwi_stasis ) {
/* If found then caller is responsible for unrefs of passed back objects */
break ;
}
ao2_ref ( * mwi_sub , - 1 ) ;
}
ao2_iterator_destroy ( mwi_subs ) ;
return ret ;
return * mwi_stasis ? 1 : 0 ;
}
/*!
* \ internal
* \ brief Allow and / or replace the unsolicited subscription
*
* Checks to see if solicited subscription is allowed . If allowed , and an
* unsolicited one exists then prepare for replacement by removing the
* current unsolicited subscription .
*
* \ param endpoint The endpoint
* \ param mailbox The mailbox
*
* \ 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 )
{
struct mwi_subscription * mwi_sub ;
struct mwi_stasis_subscription * mwi_stasis ;
if ( ! has_mwi_subscription ( unsolicited_mwi , endpoint , mailbox , & mwi_sub , & mwi_stasis ) ) {
/* If no unsolicited subscription then allow the solicited one */
return 1 ;
}
if ( ! endpoint - > subscription . mwi . subscribe_replaces_unsolicited ) {
/* Has unsolicited subscription and can't replace, so disallow */
ao2_ref ( mwi_stasis , - 1 ) ;
ao2_ref ( mwi_sub , - 1 ) ;
return 0 ;
}
/*
* The unsolicited subscription exists , and it is allowed to be replaced .
* So , first remove the unsolicited stasis subscription , and if aggregation
* is not enabled then also remove the mwi_subscription object as well .
*/
ast_debug ( 1 , " Unsolicited subscription being replaced by solicited for "
" endpoint '%s' mailbox '%s' \n " , ast_sorcery_object_get_id ( endpoint ) , mailbox ) ;
unsubscribe_stasis ( mwi_stasis , NULL , 0 ) ;
ao2_unlink ( mwi_sub - > stasis_subs , mwi_stasis ) ;
if ( ! endpoint - > subscription . mwi . aggregate ) {
ao2_unlink ( unsolicited_mwi , mwi_sub ) ;
}
ao2_ref ( mwi_stasis , - 1 ) ;
ao2_ref ( mwi_sub , - 1 ) ;
/* This solicited subscription is replacing an unsolicited one, so allow */
return 1 ;
}
static int send_notify ( void * obj , void * arg , int flags ) ;
/*!
* \ internal
* \ brief Determine if an unsolicited MWI subscription is allowed
*
* \ param endpoint The endpoint
* \ param mailbox The mailbox
*
* \ 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 )
{
struct mwi_subscription * mwi_sub ;
struct mwi_stasis_subscription * mwi_stasis ;
if ( ast_strlen_zero ( mailbox ) ) {
return 0 ;
}
/*
* First check if an unsolicited subscription exists . If it does then we don ' t
* want to add another one .
*/
if ( has_mwi_subscription ( unsolicited_mwi , endpoint , mailbox , & mwi_sub , & mwi_stasis ) ) {
ao2_ref ( mwi_stasis , - 1 ) ;
ao2_ref ( mwi_sub , - 1 ) ;
return 0 ;
}
/*
* If there is no unsolicited subscription , next check to see if a solicited
* subscription exists for the endpoint / mailbox . If not , then allow .
*/
if ( ! has_mwi_subscription ( solicited_mwi , endpoint , mailbox , & mwi_sub , & mwi_stasis ) ) {
return 1 ;
}
/*
* If however , a solicited subscription does exist then we ' ll need to see if that
* subscription is allowed to replace the unsolicited one . If is allowed to replace
* then disallow the unsolicited one .
*/
if ( endpoint - > subscription . mwi . subscribe_replaces_unsolicited ) {
ao2_ref ( mwi_stasis , - 1 ) ;
ao2_ref ( mwi_sub , - 1 ) ;
return 0 ;
}
/* Otherwise, shutdown the solicited subscription and allow the unsolicited */
mwi_sub - > terminate = 1 ;
send_notify ( mwi_sub , NULL , 0 ) ;
ao2_ref ( mwi_stasis , - 1 ) ;
ao2_ref ( mwi_sub , - 1 ) ;
return 1 ;
}
/*!
@ -796,19 +940,23 @@ static int mwi_validate_for_aor(void *obj, void *arg, int flags)
return 0 ;
}
/* A reload could be taking place so lock while checking if allowed */
ao2_lock ( unsolicited_mwi ) ;
mailboxes = ast_strdupa ( aor - > mailboxes ) ;
while ( ( mailbox = ast_strip ( strsep ( & mailboxes , " , " ) ) ) ) {
if ( ast_strlen_zero ( mailbox ) ) {
continue ;
}
if ( endpoint_receives_unsolicited_mwi_for_mailbox ( endpoint , mailbox ) ) {
if ( ! allow_and_or_replace_unsolicited ( endpoint , mailbox ) ) {
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 ) ;
return - 1 ;
}
}
ao2_unlock ( unsolicited_mwi ) ;
return 0 ;
}
@ -954,6 +1102,7 @@ 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 ) ;
ao2_cleanup ( sub ) ;
ao2_cleanup ( endpoint ) ;
return 0 ;
@ -1090,12 +1239,25 @@ static void mwi_stasis_cb(void *userdata, struct stasis_subscription *sub,
}
}
/*! \note Called with the unsolicited_mwi container lock held. */
static int create_mwi_subscriptions_for_endpoint ( void * obj , void * arg , int flags )
/*!
* \ internal
* \ brief Create unsolicited MWI subscriptions for an endpoint
*
* \ note Call with the unsolicited_mwi container lock held .
*
* \ 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
*
* \ retval 0
*/
static int create_unsolicited_mwi_subscriptions ( struct ast_sip_endpoint * endpoint ,
int recreate , int send_now )
{
RAII_VAR ( struct mwi_subscription * , aggregate_sub , NULL , ao2_cleanup ) ;
struct ast_sip_endpoint * endpoint = obj ;
char * mailboxes , * mailbox ;
char * mailboxes ;
char * mailbox ;
int sub_added = 0 ;
if ( ast_strlen_zero ( endpoint - > subscription . mwi . mailboxes ) ) {
return 0 ;
@ -1104,45 +1266,83 @@ static int create_mwi_subscriptions_for_endpoint(void *obj, void *arg, int flags
if ( endpoint - > subscription . mwi . aggregate ) {
const char * endpoint_id = ast_sorcery_object_get_id ( endpoint ) ;
/* Check if subscription exists */
/* Check if aggregate subscription exists */
aggregate_sub = ao2_find ( unsolicited_mwi , endpoint_id , OBJ_SEARCH_KEY | OBJ_NOLOCK ) ;
if ( aggregate_sub ) {
/*
* If enabled there should only ever exist a single aggregate subscription object
* for an endpoint . So if it exists just return unless subscriptions are potentially
* being added back in . If that ' s the case then continue .
*/
if ( aggregate_sub & & ! recreate ) {
return 0 ;
}
aggregate_sub = mwi_subscription_alloc ( endpoint , 0 , NULL ) ;
if ( ! aggregate_sub ) {
return 0 ;
aggregate_sub = mwi_subscription_alloc ( endpoint , 0 , NULL ) ;
if ( ! aggregate_sub ) {
return 0 ; /* No MWI aggregation for you */
}
}
}
/* Lock solicited so we don't potentially add to both containers */
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 ;
/* check if subscription exists */
if ( ast_strlen_zero ( mailbox ) | |
( ! aggregate_sub & & endpoint_receives_unsolicited_mwi_for_mailbox ( endpoint , mailbox ) ) ) {
if ( ! is_unsolicited_allowed ( endpoint , mailbox ) ) {
continue ;
}
sub = aggregate_sub ? : mwi_subscription_alloc ( endpoint , 0 , NULL ) ;
if ( ! sub ) {
continue ;
}
mwi_stasis_sub = mwi_stasis_subscription_alloc ( mailbox , sub ) ;
if ( mwi_stasis_sub ) {
ao2_link ( sub - > stasis_subs , mwi_stasis_sub ) ;
ao2_ref ( mwi_stasis_sub , - 1 ) ;
}
if ( ! aggregate_sub & & sub ) {
if ( ! aggregate_sub ) {
ao2_link_flags ( unsolicited_mwi , sub , OBJ_NOLOCK ) ;
if ( send_now ) {
send_notify ( sub , NULL , 0 ) ;
}
ao2_ref ( sub , - 1 ) ;
}
if ( aggregate_sub & & ! sub_added ) {
/* If aggregation track if at least one subscription has been added */
sub_added = 1 ;
}
}
if ( aggregate_sub ) {
ao2_link_flags ( unsolicited_mwi , aggregate_sub , OBJ_NOLOCK ) ;
if ( ao2_container_count ( aggregate_sub - > stasis_subs ) ) {
ao2_link_flags ( unsolicited_mwi , aggregate_sub , OBJ_NOLOCK ) ;
if ( send_now & & sub_added ) {
send_notify ( aggregate_sub , NULL , 0 ) ;
}
} else {
/* No stasis subscriptions then no MWI data to aggregate */
ao2_ref ( aggregate_sub , - 1 ) ;
}
}
ao2_unlock ( solicited_mwi ) ;
return 0 ;
}
static int create_mwi_subscriptions_for_endpoint ( void * obj , void * arg , int flags )
{
return create_unsolicited_mwi_subscriptions ( obj , 0 , 0 ) ;
}
static int unsubscribe ( void * obj , void * arg , int flags )
{
struct mwi_subscription * mwi_sub = obj ;
@ -1347,11 +1547,20 @@ 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_sub_hash , NULL , mwi_sub_cmp ) ;
if ( ! solicited_mwi ) {
mwi_serializer_pool_shutdown ( ) ;
ast_sip_unregister_subscription_handler ( & mwi_handler ) ;
return AST_MODULE_LOAD_DECLINE ;
}
unsolicited_mwi = ao2_container_alloc_hash ( AO2_ALLOC_OPT_LOCK_MUTEX , 0 , MWI_BUCKETS ,
mwi_sub_hash , NULL , mwi_sub_cmp ) ;
if ( ! unsolicited_mwi ) {
mwi_serializer_pool_shutdown ( ) ;
ast_sip_unregister_subscription_handler ( & mwi_handler ) ;
ao2_ref ( solicited_mwi , - 1 ) ;
return AST_MODULE_LOAD_DECLINE ;
}
@ -1384,6 +1593,8 @@ static int unload_module(void)
ao2_ref ( unsolicited_mwi , - 1 ) ;
unsolicited_mwi = NULL ;
ao2_cleanup ( solicited_mwi ) ;
mwi_serializer_pool_shutdown ( ) ;
ast_sip_unregister_subscription_handler ( & mwi_handler ) ;