@ -27,6 +27,7 @@
# include <pjsip.h>
# include <pjsip.h>
# include <pjsip_ua.h>
# include <pjsip_ua.h>
# include "asterisk/app.h"
# include "asterisk/cli.h"
# include "asterisk/cli.h"
# include "asterisk/config.h"
# include "asterisk/config.h"
# include "asterisk/manager.h"
# include "asterisk/manager.h"
@ -317,6 +318,14 @@ static void notify_cli_uri_data_destroy(void *obj)
ao2_cleanup ( data - > info ) ;
ao2_cleanup ( data - > info ) ;
}
}
static void notify_cli_channel_data_destroy ( void * obj )
{
struct notify_channel_data * data = obj ;
ao2_cleanup ( data - > info ) ;
ao2_cleanup ( data - > session ) ;
}
/*!
/*!
* \ internal
* \ internal
* \ brief Destroy the notify CLI data releasing any resources ( URI variant )
* \ brief Destroy the notify CLI data releasing any resources ( URI variant )
@ -375,6 +384,29 @@ static struct notify_uri_data* notify_cli_uri_data_create(
return data ;
return data ;
}
}
/*!
* \ internal
* \ brief Construct a notify URI data object for CLI .
*/
static struct notify_channel_data * notify_cli_channel_data_create (
struct ast_sip_session * session , void * info )
{
struct notify_channel_data * data = ao2_alloc_options ( sizeof ( * data ) ,
notify_cli_channel_data_destroy , AO2_ALLOC_OPT_LOCK_NOLOCK ) ;
if ( ! data ) {
return NULL ;
}
data - > session = session ;
data - > info = info ;
ao2_ref ( data - > info , + 1 ) ;
data - > build_notify = build_cli_notify ;
return data ;
}
/*!
/*!
* \ internal
* \ internal
* \ brief Destroy the notify AMI data releasing any resources .
* \ brief Destroy the notify AMI data releasing any resources .
@ -945,14 +977,13 @@ static char *cli_complete_endpoint(const char *word)
* \ internal
* \ internal
* \ brief Do completion on the notify CLI command .
* \ brief Do completion on the notify CLI command .
*/
*/
static char * cli_complete_notify ( const char * line , const char * word ,
static char * cli_complete_notify ( struct ast_cli_args * a )
int pos , int state , int using_uri )
{
{
char * c = NULL ;
char * c = NULL ;
if ( pos = = 3 ) {
if ( a- > pos = = 3 ) {
int which = 0 ;
int which = 0 ;
int wordlen = strlen ( word) ;
int wordlen = strlen ( a- > word) ;
RAII_VAR ( struct notify_cfg * , cfg ,
RAII_VAR ( struct notify_cfg * , cfg ,
ao2_global_obj_ref ( globals ) , ao2_cleanup ) ;
ao2_global_obj_ref ( globals ) , ao2_cleanup ) ;
@ -961,7 +992,7 @@ static char *cli_complete_notify(const char *line, const char *word,
/* do completion for notify type */
/* do completion for notify type */
struct ao2_iterator i = ao2_iterator_init ( cfg - > notify_options , 0 ) ;
struct ao2_iterator i = ao2_iterator_init ( cfg - > notify_options , 0 ) ;
while ( ( option = ao2_iterator_next ( & i ) ) ) {
while ( ( option = ao2_iterator_next ( & i ) ) ) {
if ( ! strncasecmp ( word, option - > name , wordlen ) & & + + which > state ) {
if ( ! strncasecmp ( a- > word, option - > name , wordlen ) & & + + which > a- > n ) {
c = ast_strdup ( option - > name ) ;
c = ast_strdup ( option - > name ) ;
}
}
@ -974,27 +1005,38 @@ static char *cli_complete_notify(const char *line, const char *word,
return c ;
return c ;
}
}
if ( pos = = 4 ) {
if ( a- > pos = = 4 ) {
int wordlen = strlen ( word) ;
int wordlen = strlen ( a- > word) ;
if ( ast_strlen_zero ( word) ) {
if ( ast_strlen_zero ( a- > word) ) {
if ( state = = 0 ) {
if ( a- > n = = 0 ) {
c = ast_strdup ( " endpoint " ) ;
c = ast_strdup ( " endpoint " ) ;
} else if ( state = = 1 ) {
} else if ( a- > n = = 1 ) {
c = ast_strdup ( " uri " ) ;
c = ast_strdup ( " uri " ) ;
} else if ( a - > n = = 2 ) {
c = ast_strdup ( " channel " ) ;
}
}
} else if ( state = = 0 ) {
} else if ( a- > n = = 0 ) {
if ( ! strncasecmp ( word, " endpoint " , wordlen ) ) {
if ( ! strncasecmp ( a- > word, " endpoint " , wordlen ) ) {
c = ast_strdup ( " endpoint " ) ;
c = ast_strdup ( " endpoint " ) ;
} else if ( ! strncasecmp ( word, " uri " , wordlen ) ) {
} else if ( ! strncasecmp ( a- > word, " uri " , wordlen ) ) {
c = ast_strdup ( " uri " ) ;
c = ast_strdup ( " uri " ) ;
} else if ( ! strncasecmp ( a - > word , " channel " , wordlen ) ) {
c = ast_strdup ( " channel " ) ;
}
}
}
}
return c ;
return c ;
}
}
return pos > 4 & & ! using_uri ? cli_complete_endpoint ( word ) : NULL ;
if ( a - > pos > 4 ) {
if ( ! strcasecmp ( a - > argv [ 4 ] , " endpoint " ) ) {
return cli_complete_endpoint ( a - > word ) ;
} else if ( ! strcasecmp ( a - > argv [ 4 ] , " channel " ) ) {
return ast_complete_channels ( a - > line , a - > word , a - > pos , a - > n , 5 ) ;
}
}
return NULL ;
}
}
/*!
/*!
@ -1012,21 +1054,18 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
int i ;
int i ;
int using_uri = 0 ;
int using_uri = 0 ;
int using_channel = 0 ;
switch ( cmd ) {
switch ( cmd ) {
case CLI_INIT :
case CLI_INIT :
e - > command = " pjsip send notify " ;
e - > command = " pjsip send notify " ;
e - > usage =
e - > usage =
" Usage: pjsip send notify <type> {endpoint|uri } <peer> [<peer>...]\n "
" Usage: pjsip send notify <type> {endpoint|uri |channel } <peer> [<peer>...]\n "
" Send a NOTIFY request to an endpoint \n "
" Send a NOTIFY request to an endpoint \n "
" Message types are defined in pjsip_notify.conf \n " ;
" Message types are defined in pjsip_notify.conf \n " ;
return NULL ;
return NULL ;
case CLI_GENERATE :
case CLI_GENERATE :
if ( a - > argc > 4 & & ( ! strcasecmp ( a - > argv [ 4 ] , " uri " ) ) ) {
return cli_complete_notify ( a ) ;
using_uri = 1 ;
}
return cli_complete_notify ( a - > line , a - > word , a - > pos , a - > n , using_uri ) ;
}
}
if ( a - > argc < 6 ) {
if ( a - > argc < 6 ) {
@ -1035,6 +1074,8 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
if ( ! strcasecmp ( a - > argv [ 4 ] , " uri " ) ) {
if ( ! strcasecmp ( a - > argv [ 4 ] , " uri " ) ) {
using_uri = 1 ;
using_uri = 1 ;
} else if ( ! strcasecmp ( a - > argv [ 4 ] , " channel " ) ) {
using_channel = 1 ;
} else if ( strcasecmp ( a - > argv [ 4 ] , " endpoint " ) ) {
} else if ( strcasecmp ( a - > argv [ 4 ] , " endpoint " ) ) {
return CLI_SHOWUSAGE ;
return CLI_SHOWUSAGE ;
}
}
@ -1049,14 +1090,23 @@ static char *cli_notify(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a
}
}
for ( i = 5 ; i < a - > argc ; + + i ) {
for ( i = 5 ; i < a - > argc ; + + i ) {
enum notify_result result ;
ast_cli ( a - > fd , " Sending NOTIFY of type '%s' to '%s' \n " ,
ast_cli ( a - > fd , " Sending NOTIFY of type '%s' to '%s' \n " ,
a - > argv [ 3 ] , a - > argv [ i ] ) ;
a - > argv [ 3 ] , a - > argv [ i ] ) ;
switch ( using_uri ? push_notify_uri ( a - > argv [ i ] , option , notify_cli_uri_data_create ) :
if ( using_uri ) {
push_notify ( a - > argv [ i ] , option , notify_cli_data_create ) ) {
result = push_notify_uri ( a - > argv [ i ] , option , notify_cli_uri_data_create ) ;
} else if ( using_channel ) {
result = push_notify_channel ( a - > argv [ i ] , option , notify_cli_channel_data_create ) ;
} else {
result = push_notify ( a - > argv [ i ] , option , notify_cli_data_create ) ;
}
switch ( result ) {
case INVALID_ENDPOINT :
case INVALID_ENDPOINT :
ast_cli ( a - > fd , " Unable to retrieve endpoint %s \n " ,
ast_cli ( a - > fd , " Unable to retrieve endpoint %s \n " , a - > argv [ i ] ) ;
a - > argv [ i ] ) ;
break ;
case INVALID_CHANNEL :
ast_cli ( a - > fd , " Unable to find channel %s \n " , a - > argv [ i ] ) ;
break ;
break ;
case ALLOC_ERROR :
case ALLOC_ERROR :
ast_cli ( a - > fd , " Unable to allocate NOTIFY task data \n " ) ;
ast_cli ( a - > fd , " Unable to allocate NOTIFY task data \n " ) ;
@ -1246,6 +1296,97 @@ static int manager_notify(struct mansession *s, const struct message *m)
return 0 ;
return 0 ;
}
}
/*! \brief Convert headers string such as "Event=hold&Event=answer&..." into ast variable list*/
/* Caller has to call ast_variables_destroy() to free the list*/
static struct ast_variable * headers_to_variables ( const char * headers )
{
struct ast_variable * varlist = NULL ;
struct ast_variable * var ;
char * cur ;
char * header ;
cur = ( char * ) headers ;
while ( ( header = strsep ( & cur , " & " ) ) ) {
char * name ;
char * value ;
name = value = header ;
strsep ( & value , " = " ) ;
if ( ! value | | ast_strlen_zero ( name ) ) {
continue ;
}
var = ast_variable_new ( name , value , " " ) ;
var - > next = varlist ;
varlist = var ;
}
return varlist ;
}
/*! \brief Application entry point to send a SIP notify to an endpoint. */
static int app_notify ( struct ast_channel * chan , const char * data )
{
RAII_VAR ( struct notify_cfg * , cfg , NULL , ao2_cleanup ) ;
RAII_VAR ( struct notify_option * , option , NULL , ao2_cleanup ) ;
struct ast_variable * varlist = NULL ;
char * tmp ;
int res ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( to ) ;
AST_APP_ARG ( headers ) ;
) ;
if ( ast_strlen_zero ( data ) ) {
ast_log ( LOG_WARNING , " PJSIPNotify requires arguments (to, &header=...) \n " ) ;
return - 1 ;
}
tmp = ast_strdupa ( data ) ;
AST_STANDARD_APP_ARGS ( args , tmp ) ;
cfg = ao2_global_obj_ref ( globals ) ;
if ( ! ( option = notify_option_find ( cfg - > notify_options , args . headers ) ) ) {
/* If the app is passed a list of headers, use the notify_ami_*_data_create
functions as the option data is handled the same way as the ami command . */
varlist = headers_to_variables ( args . headers ) ;
if ( ast_strlen_zero ( args . to ) ) {
res = push_notify_channel ( ast_channel_name ( chan ) , varlist , notify_ami_channel_data_create ) ;
} else {
res = push_notify_uri ( args . to , varlist , notify_ami_uri_data_create ) ;
}
} else {
/* If the app is passed a configured notify option, use the notify_cli_*_data_create
functions as the option data is handled the same way as the cli command . */
if ( ast_strlen_zero ( args . to ) ) {
res = push_notify_channel ( ast_channel_name ( chan ) , option , notify_cli_channel_data_create ) ;
} else {
res = push_notify_uri ( args . to , option , notify_cli_uri_data_create ) ;
}
}
switch ( res ) {
case INVALID_CHANNEL :
case INVALID_ENDPOINT :
case ALLOC_ERROR :
res = - 1 ;
ast_variables_destroy ( varlist ) ;
break ;
case TASK_PUSH_ERROR :
/* Don't need to destroy vars since it is handled by cleanup in push_notify_channel */
res = - 1 ;
break ;
case SUCCESS :
res = 0 ;
break ;
}
return res ;
}
static int load_module ( void )
static int load_module ( void )
{
{
if ( aco_info_init ( & notify_cfg ) ) {
if ( aco_info_init ( & notify_cfg ) ) {
@ -1262,6 +1403,7 @@ static int load_module(void)
ast_cli_register_multiple ( cli_options , ARRAY_LEN ( cli_options ) ) ;
ast_cli_register_multiple ( cli_options , ARRAY_LEN ( cli_options ) ) ;
ast_manager_register_xml ( " PJSIPNotify " , EVENT_FLAG_SYSTEM , manager_notify ) ;
ast_manager_register_xml ( " PJSIPNotify " , EVENT_FLAG_SYSTEM , manager_notify ) ;
ast_register_application_xml ( " PJSIPNotify " , app_notify ) ;
return AST_MODULE_LOAD_SUCCESS ;
return AST_MODULE_LOAD_SUCCESS ;
}
}
@ -1278,6 +1420,7 @@ static int reload_module(void)
static int unload_module ( void )
static int unload_module ( void )
{
{
ast_manager_unregister ( " PJSIPNotify " ) ;
ast_manager_unregister ( " PJSIPNotify " ) ;
ast_unregister_application ( " PJSIPNotify " ) ;
ast_cli_unregister_multiple ( cli_options , ARRAY_LEN ( cli_options ) ) ;
ast_cli_unregister_multiple ( cli_options , ARRAY_LEN ( cli_options ) ) ;
aco_info_destroy ( & notify_cfg ) ;
aco_info_destroy ( & notify_cfg ) ;
ao2_global_obj_release ( globals ) ;
ao2_global_obj_release ( globals ) ;