@ -30,6 +30,7 @@
ASTERISK_FILE_VERSION ( __FILE__ , " $Revision$ " )
# include "asterisk/callerid.h"
# include "asterisk/channel.h"
# include "asterisk/manager.h"
# include "asterisk/stasis_message_router.h"
@ -80,6 +81,22 @@ static struct stasis_message_router *channel_state_router;
< / parameter >
< parameter name = " Uniqueid " >
< / parameter >
< / syntax >
< / managerEventInstance >
< / managerEvent >
< managerEvent language = " en_US " name = " Newstate " >
< managerEventInstance class = " EVENT_FLAG_CALL " >
< synopsis > Raised when a channel ' s state changes . < / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter) " / >
< / syntax >
< / managerEventInstance >
< / managerEvent >
< managerEvent language = " en_US " name = " Hangup " >
< managerEventInstance class = " EVENT_FLAG_CALL " >
< synopsis > Raised when a channel is hung up . < / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter) " / >
< parameter name = " Cause " >
< para > A numeric cause code for why the channel was hung up . < / para >
< / parameter >
@ -89,19 +106,51 @@ static struct stasis_message_router *channel_state_router;
< / syntax >
< / managerEventInstance >
< / managerEvent >
< managerEvent language = " en_US " name = " Newstate " >
< managerEvent language = " en_US " name = " HangupRequest " >
< managerEventInstance class = " EVENT_FLAG_CALL " >
< synopsis > Raised when a channel' s state changes . < / synopsis >
< synopsis > Raised when a hangup is requested . < / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter) " / >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Hangup']/managerEventInstance/syntax/parameter[@name='Cause']) " / >
< / syntax >
< / managerEventInstance >
< / managerEvent >
< managerEvent language = " en_US " name = " Hangup" >
< managerEvent language = " en_US " name = " Soft HangupRequest " >
< managerEventInstance class = " EVENT_FLAG_CALL " >
< synopsis > Raised when a channel is hung up . < / synopsis >
< synopsis > Raised when a soft hangup is requested with a specific cause code . < / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter) " / >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Hangup']/managerEventInstance/syntax/parameter[@name='Cause']) " / >
< / syntax >
< / managerEventInstance >
< / managerEvent >
< managerEvent language = " en_US " name = " NewExten " >
< managerEventInstance class = " EVENT_FLAG_DIALPLAN " >
< synopsis > Raised when a channel enters a new context , extension , priority . < / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter) " / >
< parameter name = " Extension " >
< para > Deprecated in 12 , but kept for
backward compatability . Please use
' Exten ' instead . < / para >
< / parameter >
< parameter name = " Application " >
< para > The application about to be executed . < / para >
< / parameter >
< parameter name = " AppData " >
< para > The data to be passed to the application . < / para >
< / parameter >
< / syntax >
< / managerEventInstance >
< / managerEvent >
< managerEvent language = " en_US " name = " NewCallerid " >
< managerEventInstance class = " EVENT_FLAG_CALL " >
< synopsis > Raised when a channel receives new Caller ID information . < / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter) " / >
< parameter name = " CID-CallingPres " >
< para > A description of the Caller ID presentation . < / para >
< / parameter >
< / syntax >
< / managerEventInstance >
< / managerEvent >
@ -137,9 +186,7 @@ static struct ast_str *manager_build_channel_state_string(
" Context: %s \r \n "
" Exten: %s \r \n "
" Priority: %d \r \n "
" Uniqueid: %s \r \n "
" Cause: %d \r \n "
" Cause-txt: %s \r \n " ,
" Uniqueid: %s \r \n " ,
snapshot - > name ,
snapshot - > state ,
ast_state2str ( snapshot - > state ) ,
@ -151,123 +198,276 @@ static struct ast_str *manager_build_channel_state_string(
snapshot - > context ,
snapshot - > exten ,
snapshot - > priority ,
snapshot - > uniqueid ,
snapshot - > hangupcause ,
ast_cause2str ( snapshot - > hangupcause ) ) ;
snapshot - > uniqueid ) ;
if ( ! res ) {
return NULL ;
}
if ( snapshot - > manager_vars ) {
struct ast_var_t * var ;
AST_LIST_TRAVERSE ( snapshot - > manager_vars , var , entries ) {
ast_str_append ( & out , 0 , " ChanVariable(%s): %s=%s \r \n " ,
snapshot - > name ,
var - > name , var - > value ) ;
}
}
return out ;
}
static inline int cep_has_changed (
const struct ast_channel_snapshot * old_snapshot ,
const struct ast_channel_snapshot * new_snapshot )
/*! \brief Struct containing info for an AMI channel event to send out. */
struct snapshot_manager_event {
/*! event_flags manager_event() flags parameter. */
int event_flags ;
/*! manager_event manager_event() category. */
const char * manager_event ;
AST_DECLARE_STRING_FIELDS (
/* extra fields to include in the event. */
AST_STRING_FIELD ( extra_fields ) ;
) ;
} ;
static void snapshot_manager_event_dtor ( void * obj )
{
ast_assert ( old_snapshot ! = NULL ) ;
ast_assert ( new_snapshot ! = NULL ) ;
return old_snapshot - > priority ! = new_snapshot - > priority | |
strcmp ( old_snapshot - > context , new_snapshot - > context ) ! = 0 | |
strcmp ( old_snapshot - > exten , new_snapshot - > exten ) ! = 0 ;
struct snapshot_manager_event * ev = obj ;
ast_string_field_free_memory ( ev ) ;
}
static void channel_snapshot_update ( void * data , struct stasis_subscription * sub ,
struct stasis_topic * topic ,
struct stasis_message * message )
/*!
* \ brief Construct a \ ref snapshot_manager_event .
* \ param event_flags manager_event ( ) flags parameter .
* \ param manager_event manager_event ( ) category .
* \ param extra_fields_fmt Format string for extra fields to include .
* Or NO_EXTRA_FIELDS for no extra fields .
* \ return New \ ref snapshot_manager_event object .
* \ return \ c NULL on error .
*/
static struct snapshot_manager_event *
__attribute__ ( ( format ( printf , 3 , 4 ) ) )
snapshot_manager_event_create (
int event_flags ,
const char * manager_event ,
const char * extra_fields_fmt ,
. . . )
{
RAII_VAR ( struct ast_str * , channel_event_string , NULL , ast_free ) ;
struct stasis_cache_update * update = stasis_message_data ( message ) ;
struct ast_channel_snapshot * old_snapshot ;
struct ast_channel_snapshot * new_snapshot ;
int is_hungup , was_hungup ;
char * manager_event = NULL ;
int new_exten ;
RAII_VAR ( struct snapshot_manager_event * , ev , NULL , ao2_cleanup ) ;
va_list argp ;
if ( ast_channel_snapshot ( ) ! = update - > type ) {
return ;
ast_assert ( extra_fields_fmt ! = NULL ) ;
ast_assert ( manager_event ! = NULL ) ;
ev = ao2_alloc ( sizeof ( * ev ) , snapshot_manager_event_dtor ) ;
if ( ! ev ) {
return NULL ;
}
old_snapshot = stasis_message_data ( update - > old_snapshot ) ;
new_snapshot = stasis_message_data ( update - > new_snapshot ) ;
if ( ast_string_field_init ( ev , 20 ) ) {
return NULL ;
}
ev - > manager_event = manager_event ;
ev - > event_flags = event_flags ;
va_start ( argp , extra_fields_fmt ) ;
ast_string_field_ptr_build_va ( ev , & ev - > extra_fields , extra_fields_fmt ,
argp ) ;
va_end ( argp ) ;
ao2_ref ( ev , + 1 ) ;
return ev ;
}
/*! GCC warns about blank or NULL format strings. So, shenanigans! */
# define NO_EXTRA_FIELDS "%s", ""
/*! \brief Typedef for callbacks that get called on channel snapshot updates */
typedef struct snapshot_manager_event * ( * snapshot_monitor ) (
struct ast_channel_snapshot * old_snapshot ,
struct ast_channel_snapshot * new_snapshot ) ;
/*! \brief Handle channel state changes */
static struct snapshot_manager_event * channel_state_change (
struct ast_channel_snapshot * old_snapshot ,
struct ast_channel_snapshot * new_snapshot )
{
int is_hungup , was_hungup ;
if ( ! new_snapshot ) {
/* Ignore cache clearing events; we'll see the hangup first */
return ;
return NULL ;
}
was_hungup = ( old_snapshot & & ast_test_flag ( & old_snapshot - > flags , AST_FLAG_ZOMBIE ) ) ? 1 : 0 ;
is_hungup = ast_test_flag ( & new_snapshot - > flags , AST_FLAG_ZOMBIE ) ? 1 : 0 ;
/* The Newchannel, Newstate and Hangup events are closely related, in
* in that they are mutually exclusive , basically different flavors
* of a new channel state event .
*/
if ( ! old_snapshot ) {
manager_event = " Newchannel " ;
return snapshot_manager_event_create (
EVENT_FLAG_CALL , " Newchannel " , NO_EXTRA_FIELDS ) ;
}
if ( old_snapshot & & old_snapshot - > state ! = new_snapshot - > state ) {
manager_event = " Newstate " ;
}
was_hungup = ast_test_flag ( & old_snapshot - > flags , AST_FLAG_ZOMBIE ) ? 1 : 0 ;
is_hungup = ast_test_flag ( & new_snapshot - > flags , AST_FLAG_ZOMBIE ) ? 1 : 0 ;
if ( ! was_hungup & & is_hungup ) {
manager_event = " Hangup " ;
return snapshot_manager_event_create (
EVENT_FLAG_CALL , " Hangup " ,
" Cause: %d \r \n "
" Cause-txt: %s \r \n " ,
new_snapshot - > hangupcause ,
ast_cause2str ( new_snapshot - > hangupcause ) ) ;
}
/* Detect Newexten transitions
* - if new snapshot has an application set AND
* - first snapshot OR
* - if the old snapshot has no application ( first Newexten ) OR
* - if the context / priority / exten changes
if ( old_snapshot - > state ! = new_snapshot - > state ) {
return snapshot_manager_event_create (
EVENT_FLAG_CALL , " Newstate " , NO_EXTRA_FIELDS ) ;
}
/* No event */
return NULL ;
}
/*!
* \ brief Compares the context , exten and priority of two snapshots .
* \ param old_snapshot Old snapshot
* \ param new_snapshot New snapshot
* \ return True ( non - zero ) if context , exten or priority are identical .
* \ return False ( zero ) if context , exten and priority changed .
*/
static inline int cep_equal (
const struct ast_channel_snapshot * old_snapshot ,
const struct ast_channel_snapshot * new_snapshot )
{
ast_assert ( old_snapshot ! = NULL ) ;
ast_assert ( new_snapshot ! = NULL ) ;
/* We actually get some snapshots with CEP set, but before the
* application is set . Since empty application is invalid , we treat
* setting the application from nothing as a CEP change .
*/
new_exten = ! ast_strlen_zero ( new_snapshot - > appl ) & & (
! old_snapshot | |
ast_strlen_zero ( old_snapshot - > appl ) | |
cep_has_changed ( old_snapshot , new_snapshot ) ) ;
if ( manager_event | | new_exten ) {
channel_event_string =
manager_build_channel_state_string ( new_snapshot ) ;
if ( ast_strlen_zero ( old_snapshot - > appl ) & &
! ast_strlen_zero ( new_snapshot - > appl ) ) {
return 0 ;
}
if ( ! channel_event_string ) {
return ;
return old_snapshot - > priority = = new_snapshot - > priority & &
strcmp ( old_snapshot - > context , new_snapshot - > context ) = = 0 & &
strcmp ( old_snapshot - > exten , new_snapshot - > exten ) = = 0 ;
}
static struct snapshot_manager_event * channel_newexten (
struct ast_channel_snapshot * old_snapshot ,
struct ast_channel_snapshot * new_snapshot )
{
/* No Newexten event on cache clear */
if ( ! new_snapshot ) {
return NULL ;
}
/* Empty application is not valid for a Newexten event */
if ( ast_strlen_zero ( new_snapshot - > appl ) ) {
return NULL ;
}
if ( old_snapshot & & cep_equal ( old_snapshot , new_snapshot ) ) {
return NULL ;
}
/* DEPRECATED: Extension field deprecated in 12; remove in 14 */
return snapshot_manager_event_create (
EVENT_FLAG_CALL , " Newexten " ,
" Extension: %s \r \n "
" Application: %s \r \n "
" AppData: %s \r \n " ,
new_snapshot - > exten ,
new_snapshot - > appl ,
new_snapshot - > data ) ;
}
/*!
* \ brief Compares the callerid info of two snapshots .
* \ param old_snapshot Old snapshot
* \ param new_snapshot New snapshot
* \ return True ( non - zero ) if callerid are identical .
* \ return False ( zero ) if callerid changed .
*/
static inline int caller_id_equal (
const struct ast_channel_snapshot * old_snapshot ,
const struct ast_channel_snapshot * new_snapshot )
{
ast_assert ( old_snapshot ! = NULL ) ;
ast_assert ( new_snapshot ! = NULL ) ;
return strcmp ( old_snapshot - > caller_number , new_snapshot - > caller_number ) = = 0 & &
strcmp ( old_snapshot - > caller_name , new_snapshot - > caller_name ) = = 0 ;
}
static struct snapshot_manager_event * channel_new_callerid (
struct ast_channel_snapshot * old_snapshot ,
struct ast_channel_snapshot * new_snapshot )
{
/* No NewCallerid event on cache clear or first event */
if ( ! old_snapshot | | ! new_snapshot ) {
return NULL ;
}
if ( caller_id_equal ( old_snapshot , new_snapshot ) ) {
return NULL ;
}
/* Channel state change events */
if ( manager_event ) {
manager_event ( EVENT_FLAG_CALL , manager_event , " %s " ,
ast_str_buffer ( channel_event_string ) ) ;
return snapshot_manager_event_create (
EVENT_FLAG_CALL , " NewCallerid " ,
" CID-CallingPres: %d (%s) \r \n " ,
new_snapshot - > caller_pres ,
ast_describe_caller_presentation ( new_snapshot - > caller_pres ) ) ;
}
snapshot_monitor monitors [ ] = {
channel_state_change ,
channel_newexten ,
channel_new_callerid
} ;
static void channel_snapshot_update ( void * data , struct stasis_subscription * sub ,
struct stasis_topic * topic ,
struct stasis_message * message )
{
RAII_VAR ( struct ast_str * , channel_event_string , NULL , ast_free ) ;
struct stasis_cache_update * update ;
struct ast_channel_snapshot * old_snapshot ;
struct ast_channel_snapshot * new_snapshot ;
size_t i ;
update = stasis_message_data ( message ) ;
if ( ast_channel_snapshot ( ) ! = update - > type ) {
return ;
}
if ( new_exten ) {
/* DEPRECATED: Extension field deprecated in 12; remove in 14 */
/*** DOCUMENTATION
< managerEventInstance >
< synopsis > Raised when a channel enters a new context , extension , priority . < / synopsis >
< syntax >
< xi : include xpointer = " xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter) " / >
< parameter name = " Extension " >
< para > Deprecated in 12 , but kept for
backward compatability . Please use
' Exten ' instead . < / para >
< / parameter >
< parameter name = " Application " >
< para > The application about to be executed . < / para >
< / parameter >
< parameter name = " AppData " >
< para > The data to be passed to the application . < / para >
< / parameter >
< / syntax >
< / managerEventInstance >
* * */
manager_event ( EVENT_FLAG_DIALPLAN , " Newexten " ,
" %s "
" Extension: %s \r \n "
" Application: %s \r \n "
" AppData: %s \r \n " ,
ast_str_buffer ( channel_event_string ) ,
new_snapshot - > exten ,
new_snapshot - > appl ,
new_snapshot - > data ) ;
old_snapshot = stasis_message_data ( update - > old_snapshot ) ;
new_snapshot = stasis_message_data ( update - > new_snapshot ) ;
for ( i = 0 ; i < ARRAY_LEN ( monitors ) ; + + i ) {
RAII_VAR ( struct snapshot_manager_event * , ev , NULL , ao2_cleanup ) ;
ev = monitors [ i ] ( old_snapshot , new_snapshot ) ;
if ( ! ev ) {
continue ;
}
/* If we haven't already, build the channel event string */
if ( ! channel_event_string ) {
channel_event_string =
manager_build_channel_state_string ( new_snapshot ) ;
if ( ! channel_event_string ) {
return ;
}
}
manager_event ( ev - > event_flags , ev - > manager_event , " %s%s " ,
ast_str_buffer ( channel_event_string ) ,
ev - > extra_fields ) ;
}
}
@ -347,6 +547,43 @@ static void channel_userevent(struct ast_channel_blob *obj)
ast_str_buffer ( channel_event_string ) , eventname , body ) ;
}
static void channel_hangup_request ( struct ast_channel_blob * obj )
{
RAII_VAR ( struct ast_str * , extra , NULL , ast_free ) ;
RAII_VAR ( struct ast_str * , channel_event_string , NULL , ast_free ) ;
struct ast_json * cause ;
int is_soft ;
char * manager_event = " HangupRequest " ;
extra = ast_str_create ( 20 ) ;
if ( ! extra ) {
return ;
}
channel_event_string = manager_build_channel_state_string ( obj - > snapshot ) ;
if ( ! channel_event_string ) {
return ;
}
cause = ast_json_object_get ( obj - > blob , " cause " ) ;
if ( cause ) {
ast_str_append ( & extra , 0 ,
" Cause: %jd \r \n " ,
ast_json_integer_get ( cause ) ) ;
}
is_soft = ast_json_is_true ( ast_json_object_get ( obj - > blob , " soft " ) ) ;
if ( is_soft ) {
manager_event = " SoftHangupRequest " ;
}
manager_event ( EVENT_FLAG_CALL , manager_event ,
" %s%s " ,
ast_str_buffer ( channel_event_string ) ,
ast_str_buffer ( extra ) ) ;
}
/*!
* \ brief Callback processing messages on the channel topic .
*/
@ -360,6 +597,8 @@ static void channel_blob_cb(void *data, struct stasis_subscription *sub,
channel_varset ( obj ) ;
} else if ( strcmp ( " userevent " , ast_channel_blob_type ( obj ) ) = = 0 ) {
channel_userevent ( obj ) ;
} else if ( strcmp ( " hangup_request " , ast_channel_blob_type ( obj ) ) = = 0 ) {
channel_hangup_request ( obj ) ;
}
}