@ -4,6 +4,7 @@
* Copyright ( C ) 2013 , Fairview 5 Engineering , LLC
* Copyright ( C ) 2013 , Fairview 5 Engineering , LLC
*
*
* George Joseph < george . joseph @ fairview5 . com >
* George Joseph < george . joseph @ fairview5 . com >
* Jos é Lopes < jose . lopes @ nfon . com >
*
*
* See http : //www.asterisk.org for more information about
* See http : //www.asterisk.org for more information about
* the Asterisk project . Please do not directly contact
* the Asterisk project . Please do not directly contact
@ -166,6 +167,89 @@
< ref type = " function " > PJSIP_HEADER < / ref >
< ref type = " function " > PJSIP_HEADER < / ref >
< / see - also >
< / see - also >
< / function >
< / function >
< function name = " PJSIP_RESPONSE_HEADER " language = " en_US " >
< synopsis >
Gets headers of 200 response from an outbound PJSIP channel .
< / synopsis >
< syntax >
< parameter name = " action " required = " true " >
< enumlist >
< enum name = " read " >
< para > Returns instance < replaceable > number < / replaceable >
of response header < replaceable > name < / replaceable > . < / para >
< / enum >
< / enumlist >
< / parameter >
< parameter name = " name " required = " true " >
< para > The < replaceable > name < / replaceable > of the response header .
A < literal > * < / literal > can be appended to the < replaceable > name < / replaceable >
to iterate over all response headers < emphasis > beginning with < / emphasis >
< replaceable > name < / replaceable > . < / para >
< / parameter >
< parameter name = " number " required = " false " >
< para > If there ' s more than 1 header with the same name , this specifies which header
to read . If not specified , defaults to < literal > 1 < / literal > meaning
the first matching header .
< / para >
< / parameter >
< / syntax >
< description >
< para > PJSIP_RESPONSE_HEADER allows you to read specific SIP headers of 200 response
from the outbound PJSIP channel . < / para >
< para > Examples : < / para >
< example title = " Set 'somevar' to the value of the 'From' header " >
exten = > 1 , 1 , Set ( somevar = $ { PJSIP_RESPONSE_HEADER ( read , From ) } )
< / example >
< example title = " Set 'via2' to the value of the 2nd 'Via' header " >
exten = > 1 , 1 , Set ( via2 = $ { PJSIP_RESPONSE_HEADER ( read , Via , 2 ) } )
< / example >
< example title = " Set 'xhdr' to the value of the 1sx X-header " >
exten = > 1 , 1 , Set ( xhdr = $ { PJSIP_RESPONSE_HEADER ( read , X - * , 1 ) } )
< / example >
< note > < para > If you call PJSIP_RESPONSE_HEADER in a normal dialplan context you ' ll be
operating on the < emphasis > caller ' s ( incoming ) < / emphasis > channel which
may not be what you want . To operate on the < emphasis > callee ' s ( outgoing ) < / emphasis >
channel call PJSIP_RESPONSE_HEADER in a pre - connect handler . < / para >
< / note >
< example title = " Usage on pre-connect handler " >
[ handler ]
exten = > readheader , 1 , NoOp ( PJSIP_RESPONSE_HEADER ( read , X - MyHeader ) )
[ somecontext ]
exten = > 1 , 1 , Dial ( PJSIP / $ { EXTEN } , , U ( handler ^ readheader ^ 1 ) )
< / example >
< / description >
< see - also >
< ref type = " function " > PJSIP_RESPONSE_HEADERS < / ref >
< ref type = " function " > PJSIP_HEADER < / ref >
< / see - also >
< / function >
< function name = " PJSIP_RESPONSE_HEADERS " language = " en_US " >
< synopsis >
Gets the list of SIP header names from the 200 response of INVITE message .
< / synopsis >
< syntax >
< parameter name = " prefix " >
< para > If specified , only the headers matching the given prefix are returned . < / para >
< / parameter >
< / syntax >
< description >
< para > Returns a comma - separated list of header names ( without values ) from the 200
response of INVITE message . Multiple headers with the same name are included in the
list only once . < / para >
< para > For example , < literal > $ { PJSIP_RESPONSE_HEADERS ( Co ) } < / literal > might return
< literal > Contact , Content - Length , Content - Type < / literal > . As a practical example ,
you may use < literal > $ { PJSIP_RESPONSE_HEADERS ( X - ) } < / literal > to enumerate optional
extended headers . < / para >
< / description >
< see - also >
< ref type = " function " > PJSIP_RESPONSE_HEADER < / ref >
< ref type = " function " > PJSIP_HEADERS < / ref >
< / see - also >
< / function >
* * */
* * */
@ -180,6 +264,10 @@ AST_LIST_HEAD_NOLOCK(hdr_list, hdr_list_entry);
static const struct ast_datastore_info header_datastore = {
static const struct ast_datastore_info header_datastore = {
. type = " header_datastore " ,
. type = " header_datastore " ,
} ;
} ;
/*! \brief Datastore for saving response headers */
static const struct ast_datastore_info response_header_datastore = {
. type = " response_header_datastore " ,
} ;
/*! \brief Data structure used for ast_sip_push_task_wait_serializer */
/*! \brief Data structure used for ast_sip_push_task_wait_serializer */
struct header_data {
struct header_data {
@ -189,6 +277,7 @@ struct header_data {
char * buf ;
char * buf ;
int header_number ;
int header_number ;
size_t len ;
size_t len ;
const struct ast_datastore_info * header_datastore ;
} ;
} ;
/*!
/*!
@ -243,6 +332,42 @@ static int incoming_request(struct ast_sip_session *session, pjsip_rx_data * rda
return 0 ;
return 0 ;
}
}
/*!
* \ internal
* \ brief Session supplement callback on an incoming INVITE response
*
* Retrieve the response_header_datastore from the session or create one if it doesn ' t exist .
* Create and initialize the list if needed .
* Insert the headers .
*/
static void incoming_response ( struct ast_sip_session * session , pjsip_rx_data * rdata )
{
pj_pool_t * pool = session - > inv_session - > dlg - > pool ;
RAII_VAR ( struct ast_datastore * , datastore ,
ast_sip_session_get_datastore ( session , response_header_datastore . type ) , ao2_cleanup ) ;
pjsip_status_line status = rdata - > msg_info . msg - > line . status ;
/* Skip responses different of 200 OK, when 2xx is received. */
if ( session - > inv_session - > state ! = PJSIP_INV_STATE_CONNECTING | | status . code ! = 200 ) {
return ;
}
if ( ! datastore ) {
if ( ! ( datastore =
ast_sip_session_alloc_datastore ( & response_header_datastore , response_header_datastore . type ) )
| |
! ( datastore - > data = pj_pool_alloc ( pool , sizeof ( struct hdr_list ) ) ) | |
ast_sip_session_add_datastore ( session , datastore ) ) {
ast_log ( AST_LOG_ERROR , " Unable to create datastore for header functions. \n " ) ;
return ;
}
AST_LIST_HEAD_INIT_NOLOCK ( ( struct hdr_list * ) datastore - > data ) ;
}
insert_headers ( pool , ( struct hdr_list * ) datastore - > data , rdata - > msg_info . msg ) ;
return ;
}
/*!
/*!
* \ internal
* \ internal
* \ brief Search list for nth occurrence of specific header .
* \ brief Search list for nth occurrence of specific header .
@ -270,7 +395,7 @@ static pjsip_hdr *find_header(struct hdr_list *list, const char *header_name,
/*!
/*!
* \ internal
* \ internal
* \ brief Implements PJSIP_HEADERS by searching for the requested header prefix .
* \ brief Implements PJSIP_HEADERS / PJSIP_RESPONSE_HEADERS by searching for the requested header prefix .
*
*
* Retrieve the header_datastore .
* Retrieve the header_datastore .
* Search for the all matching headers .
* Search for the all matching headers .
@ -292,7 +417,7 @@ static int read_headers(void *obj)
struct hdr_list * list ;
struct hdr_list * list ;
RAII_VAR ( struct ast_datastore * , datastore ,
RAII_VAR ( struct ast_datastore * , datastore ,
ast_sip_session_get_datastore ( data - > channel - > session , header_datastore. type ) ,
ast_sip_session_get_datastore ( data - > channel - > session , data- > header_datastore - > type ) ,
ao2_cleanup ) ;
ao2_cleanup ) ;
if ( ! datastore | | ! datastore - > data ) {
if ( ! datastore | | ! datastore - > data ) {
@ -354,10 +479,9 @@ static int read_headers(void *obj)
return 0 ;
return 0 ;
}
}
/*!
/*!
* \ internal
* \ internal
* \ brief Implements PJSIP_HEADER ' read ' by searching the for the requested header .
* \ brief Implements PJSIP_HEADER / PJSIP_RESPONSE_HEADER ' read ' by searching the for the requested header .
*
*
* Retrieve the header_datastore .
* Retrieve the header_datastore .
* Search for the nth matching header .
* Search for the nth matching header .
@ -378,7 +502,7 @@ static int read_header(void *obj)
struct hdr_list * list ;
struct hdr_list * list ;
int i = 1 ;
int i = 1 ;
RAII_VAR ( struct ast_datastore * , datastore ,
RAII_VAR ( struct ast_datastore * , datastore ,
ast_sip_session_get_datastore ( data - > channel - > session , header_datastore. type ) ,
ast_sip_session_get_datastore ( data - > channel - > session , data- > header_datastore - > type ) ,
ao2_cleanup ) ;
ao2_cleanup ) ;
if ( ! datastore | | ! datastore - > data ) {
if ( ! datastore | | ! datastore - > data ) {
@ -459,11 +583,11 @@ static int add_header(void *obj)
struct hdr_list * list ;
struct hdr_list * list ;
RAII_VAR ( struct ast_datastore * , datastore ,
RAII_VAR ( struct ast_datastore * , datastore ,
ast_sip_session_get_datastore ( session , header_datastore. type ) , ao2_cleanup ) ;
ast_sip_session_get_datastore ( session , data- > header_datastore - > type ) , ao2_cleanup ) ;
if ( ! datastore ) {
if ( ! datastore ) {
if ( ! ( datastore = ast_sip_session_alloc_datastore ( & header_datastore ,
if ( ! ( datastore = ast_sip_session_alloc_datastore ( data - > header_datastore ,
header_datastore . type ) )
data - > header_datastore - > type ) )
| | ! ( datastore - > data = pj_pool_alloc ( pool , sizeof ( struct hdr_list ) ) )
| | ! ( datastore - > data = pj_pool_alloc ( pool , sizeof ( struct hdr_list ) ) )
| | ast_sip_session_add_datastore ( session , datastore ) ) {
| | ast_sip_session_add_datastore ( session , datastore ) ) {
ast_log ( AST_LOG_ERROR , " Unable to create datastore for header functions. \n " ) ;
ast_log ( AST_LOG_ERROR , " Unable to create datastore for header functions. \n " ) ;
@ -502,7 +626,7 @@ static int update_header(void *obj)
struct header_data * data = obj ;
struct header_data * data = obj ;
pjsip_hdr * hdr = NULL ;
pjsip_hdr * hdr = NULL ;
RAII_VAR ( struct ast_datastore * , datastore ,
RAII_VAR ( struct ast_datastore * , datastore ,
ast_sip_session_get_datastore ( data - > channel - > session , header_datastore. type ) ,
ast_sip_session_get_datastore ( data - > channel - > session , data- > header_datastore - > type ) ,
ao2_cleanup ) ;
ao2_cleanup ) ;
if ( ! datastore | | ! datastore - > data ) {
if ( ! datastore | | ! datastore - > data ) {
@ -539,7 +663,7 @@ static int remove_header(void *obj)
struct hdr_list_entry * le ;
struct hdr_list_entry * le ;
int removed_count = 0 ;
int removed_count = 0 ;
RAII_VAR ( struct ast_datastore * , datastore ,
RAII_VAR ( struct ast_datastore * , datastore ,
ast_sip_session_get_datastore ( data - > channel - > session , header_datastore. type ) ,
ast_sip_session_get_datastore ( data - > channel - > session , data- > header_datastore - > type ) ,
ao2_cleanup ) ;
ao2_cleanup ) ;
if ( ! datastore | | ! datastore - > data ) {
if ( ! datastore | | ! datastore - > data ) {
@ -597,13 +721,47 @@ static int func_read_headers(struct ast_channel *chan, const char *function, cha
header_data . header_value = NULL ;
header_data . header_value = NULL ;
header_data . buf = buf ;
header_data . buf = buf ;
header_data . len = len ;
header_data . len = len ;
header_data . header_datastore = & header_datastore ;
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer , read_headers , & header_data ) ;
}
/*!
* \ brief Read list of unique SIP response headers
*/
static int func_response_read_headers ( struct ast_channel * chan , const char * function , char * data , char * buf , size_t len )
{
struct ast_sip_channel_pvt * channel = chan ? ast_channel_tech_pvt ( chan ) : NULL ;
struct header_data header_data ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( header_pattern ) ;
) ;
AST_STANDARD_APP_ARGS ( args , data ) ;
if ( ! chan | | strncmp ( ast_channel_name ( chan ) , " PJSIP/ " , 6 ) ) {
ast_log ( LOG_ERROR , " This function requires a PJSIP channel. \n " ) ;
return - 1 ;
}
if ( ast_strlen_zero ( args . header_pattern ) ) {
ast_log ( AST_LOG_ERROR , " This function requires a pattern. \n " ) ;
return - 1 ;
}
header_data . channel = channel ;
header_data . header_name = args . header_pattern ;
header_data . header_value = NULL ;
header_data . buf = buf ;
header_data . len = len ;
header_data . header_datastore = & response_header_datastore ;
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer , read_headers , & header_data ) ;
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer , read_headers , & header_data ) ;
}
}
/*!
/*!
* \ brief Implements function ' read ' callback .
* \ brief Implements PJSIP_HEADER function ' read ' callback .
*
*
* Valid actions are ' read ' and ' remove ' .
* Valid actions are ' read ' and ' remove ' .
*/
*/
@ -645,6 +803,7 @@ static int func_read_header(struct ast_channel *chan, const char *function, char
header_data . header_value = NULL ;
header_data . header_value = NULL ;
header_data . buf = buf ;
header_data . buf = buf ;
header_data . len = len ;
header_data . len = len ;
header_data . header_datastore = & header_datastore ;
if ( ! strcasecmp ( args . action , " read " ) ) {
if ( ! strcasecmp ( args . action , " read " ) ) {
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer , read_header , & header_data ) ;
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer , read_header , & header_data ) ;
@ -660,7 +819,62 @@ static int func_read_header(struct ast_channel *chan, const char *function, char
}
}
/*!
/*!
* \ brief Implements function ' write ' callback .
* \ brief Implements PJSIP_RESPONSE_HEADER function ' read ' callback .
*
* Valid actions are ' read '
*/
static int func_response_read_header ( struct ast_channel * chan , const char * function , char * data , char * buf , size_t len )
{
struct ast_sip_channel_pvt * channel = chan ? ast_channel_tech_pvt ( chan ) : NULL ;
struct header_data header_data ;
int number ;
AST_DECLARE_APP_ARGS ( args ,
AST_APP_ARG ( action ) ;
AST_APP_ARG ( header_name ) ; AST_APP_ARG ( header_number ) ; ) ;
AST_STANDARD_APP_ARGS ( args , data ) ;
if ( ! channel | | strncmp ( ast_channel_name ( chan ) , " PJSIP/ " , 6 ) ) {
ast_log ( LOG_ERROR , " This function requires a PJSIP channel. \n " ) ;
return - 1 ;
}
if ( ast_strlen_zero ( args . action ) ) {
ast_log ( AST_LOG_ERROR , " This function requires an action. \n " ) ;
return - 1 ;
}
if ( ast_strlen_zero ( args . header_name ) ) {
ast_log ( AST_LOG_ERROR , " This function requires a header name. \n " ) ;
return - 1 ;
}
if ( ! args . header_number ) {
number = 1 ;
} else {
sscanf ( args . header_number , " %30d " , & number ) ;
if ( number < 1 ) {
number = 1 ;
}
}
header_data . channel = channel ;
header_data . header_name = args . header_name ;
header_data . header_number = number ;
header_data . header_value = NULL ;
header_data . buf = buf ;
header_data . len = len ;
header_data . header_datastore = & response_header_datastore ;
if ( ! strcasecmp ( args . action , " read " ) ) {
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer , read_header , & header_data ) ;
} else {
ast_log ( AST_LOG_ERROR ,
" Unknown action '%s' is not valid, must be 'read'. \n " ,
args . action ) ;
return - 1 ;
}
}
/*!
* \ brief Implements PJSIP_HEADER function ' write ' callback .
*
*
* Valid actions are ' add ' , ' update ' and ' remove ' .
* Valid actions are ' add ' , ' update ' and ' remove ' .
*/
*/
@ -703,6 +917,7 @@ static int func_write_header(struct ast_channel *chan, const char *cmd, char *da
header_data . header_value = value ;
header_data . header_value = value ;
header_data . buf = NULL ;
header_data . buf = NULL ;
header_data . len = 0 ;
header_data . len = 0 ;
header_data . header_datastore = & header_datastore ;
if ( ! strcasecmp ( args . action , " add " ) ) {
if ( ! strcasecmp ( args . action , " add " ) ) {
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer ,
return ast_sip_push_task_wait_serializer ( channel - > session - > serializer ,
@ -732,6 +947,16 @@ static struct ast_custom_function pjsip_headers_function = {
. read = func_read_headers
. read = func_read_headers
} ;
} ;
static struct ast_custom_function pjsip_response_header_function = {
. name = " PJSIP_RESPONSE_HEADER " ,
. read = func_response_read_header
} ;
static struct ast_custom_function pjsip_response_headers_function = {
. name = " PJSIP_RESPONSE_HEADERS " ,
. read = func_response_read_headers
} ;
/*!
/*!
* \ internal
* \ internal
* \ brief Session supplement callback for outgoing INVITE requests
* \ brief Session supplement callback for outgoing INVITE requests
@ -768,6 +993,7 @@ static struct ast_sip_session_supplement header_funcs_supplement = {
. priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 1000 ,
. priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL - 1000 ,
. incoming_request = incoming_request ,
. incoming_request = incoming_request ,
. outgoing_request = outgoing_request ,
. outgoing_request = outgoing_request ,
. incoming_response = incoming_response ,
} ;
} ;
static int load_module ( void )
static int load_module ( void )
@ -775,6 +1001,8 @@ static int load_module(void)
ast_sip_session_register_supplement ( & header_funcs_supplement ) ;
ast_sip_session_register_supplement ( & header_funcs_supplement ) ;
ast_custom_function_register ( & pjsip_header_function ) ;
ast_custom_function_register ( & pjsip_header_function ) ;
ast_custom_function_register ( & pjsip_headers_function ) ;
ast_custom_function_register ( & pjsip_headers_function ) ;
ast_custom_function_register ( & pjsip_response_header_function ) ;
ast_custom_function_register ( & pjsip_response_headers_function ) ;
return AST_MODULE_LOAD_SUCCESS ;
return AST_MODULE_LOAD_SUCCESS ;
}
}
@ -783,6 +1011,8 @@ static int unload_module(void)
{
{
ast_custom_function_unregister ( & pjsip_header_function ) ;
ast_custom_function_unregister ( & pjsip_header_function ) ;
ast_custom_function_unregister ( & pjsip_headers_function ) ;
ast_custom_function_unregister ( & pjsip_headers_function ) ;
ast_custom_function_unregister ( & pjsip_response_header_function ) ;
ast_custom_function_unregister ( & pjsip_response_headers_function ) ;
ast_sip_session_unregister_supplement ( & header_funcs_supplement ) ;
ast_sip_session_unregister_supplement ( & header_funcs_supplement ) ;
return 0 ;
return 0 ;
}
}