@ -264,6 +264,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
# include "asterisk/cel.h"
# include "asterisk/data.h"
# include "asterisk/aoc.h"
# include "asterisk/custom_control_frame.h"
# include "asterisk/message.h"
# include "sip/include/sip.h"
# include "sip/include/globals.h"
@ -1086,6 +1087,13 @@ static void destroy_escs(void)
}
}
struct state_notify_data {
int state ;
int presence_state ;
const char * presence_subtype ;
const char * presence_message ;
} ;
/*!
* \ details
* Here we implement the container for dialogs which are in the
@ -1369,7 +1377,8 @@ static int attempt_transfer(struct sip_dual *transferer, struct sip_dual *target
static int do_magic_pickup ( struct ast_channel * channel , const char * extension , const char * context ) ;
/*--- Device monitoring and Device/extension state/event handling */
static int cb_extensionstate ( const char * context , const char * exten , enum ast_extension_states state , void * data ) ;
static int extensionstate_update ( char * context , char * exten , struct state_notify_data * data , struct sip_pvt * p ) ;
static int cb_extensionstate ( char * context , char * exten , struct ast_state_cb_info * info , void * data ) ;
static int sip_devicestate ( void * data ) ;
static int sip_poke_noanswer ( const void * data ) ;
static int sip_poke_peer ( struct sip_peer * peer , int force ) ;
@ -1500,7 +1509,7 @@ static int get_rpid(struct sip_pvt *p, struct sip_request *oreq);
static int get_rdnis ( struct sip_pvt * p , struct sip_request * oreq , char * * name , char * * number , int * reason ) ;
static enum sip_get_dest_result get_destination ( struct sip_pvt * p , struct sip_request * oreq , int * cc_recall_core_id ) ;
static int get_msg_text ( char * buf , int len , struct sip_request * req ) ;
static int transmit_state_notify ( struct sip_pvt * p , int state , int full , int timeout ) ;
static int transmit_state_notify ( struct sip_pvt * p , struct state_notify_data * data , int full , int timeout ) ;
static void update_connectedline ( struct sip_pvt * p , const void * data , size_t datalen ) ;
static void update_redirecting ( struct sip_pvt * p , const void * data , size_t datalen ) ;
static int get_domain ( const char * str , char * domain , int len ) ;
@ -3858,7 +3867,10 @@ static int __sip_autodestruct(const void *data)
/* If this is a subscription, tell the phone that we got a timeout */
if ( p - > subscribed & & p - > subscribed ! = MWI_NOTIFICATION & & p - > subscribed ! = CALL_COMPLETION ) {
transmit_state_notify ( p , AST_EXTENSION_DEACTIVATED , 1 , TRUE ) ; /* Send last notification */
struct state_notify_data data = { 0 , } ;
data . state = AST_EXTENSION_DEACTIVATED ;
transmit_state_notify ( p , & data , 1 , TRUE ) ; /* Send last notification */
p - > subscribed = NONE ;
append_history ( p , " Subscribestatus " , " timeout " ) ;
ast_debug ( 3 , " Re-scheduled destruction of SIP subscription %s \n " , p - > callid ? p - > callid : " <unknown> " ) ;
@ -6866,6 +6878,54 @@ static int initialize_udptl(struct sip_pvt *p)
return 0 ;
}
/*!
* \ brief Sends AST_CUSTOM_FRAME of type sip info .
*
* \ note pvt is expected to be locked before entering this function .
*/
static int sip_handle_custom_info ( struct sip_pvt * pvt , struct ast_custom_payload * pl )
{
struct ast_variable * headers = NULL ;
char * content_type = NULL ;
char * content = NULL ;
char * useragent_filter = NULL ;
struct ast_variable * var ;
struct sip_request req ;
int res = - 1 ;
if ( ast_custom_payload_sipinfo_decode ( pl , & headers , & content_type , & content , & useragent_filter ) ) {
goto custom_info_cleanup ;
}
if ( ! ( ast_strlen_zero ( useragent_filter ) ) ) {
int match = ( strstr ( pvt - > useragent , useragent_filter ) ) ? 1 : 0 ;
if ( ! match ) {
goto custom_info_cleanup ;
}
}
reqprep ( & req , pvt , SIP_INFO , 0 , 1 ) ;
for ( var = headers ; var ; var = var - > next ) {
add_header ( & req , var - > name , var - > value ) ;
}
if ( ! ast_strlen_zero ( content ) & & ! ast_strlen_zero ( content_type ) ) {
add_header ( & req , " Content-Type " , content_type ) ;
add_content ( & req , content ) ;
}
res = send_request ( pvt , & req , XMIT_RELIABLE , pvt - > ocseq ) ;
custom_info_cleanup :
ast_free ( content ) ;
ast_free ( content_type ) ;
ast_free ( useragent_filter ) ;
ast_variables_destroy ( headers ) ;
return res ;
}
/*! \brief Play indication to user
* With SIP a lot of indications is sent as messages , letting the device play
the indication - busy signal , congestion etc
@ -6995,6 +7055,11 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
case AST_CONTROL_REDIRECTING :
update_redirecting ( p , data , datalen ) ;
break ;
case AST_CONTROL_CUSTOM :
if ( datalen & & ast_custom_payload_type ( ( struct ast_custom_payload * ) data ) = = AST_CUSTOM_SIP_INFO ) {
sip_handle_custom_info ( p , ( struct ast_custom_payload * ) data ) ;
}
break ;
case AST_CONTROL_AOC :
{
struct ast_aoc_decoded * decoded = ast_aoc_decode ( ( struct ast_aoc_encoded * ) data , datalen , ast ) ;
@ -12875,8 +12940,13 @@ static int find_calling_channel(void *obj, void *arg, void *data, int flags)
return res ? CMP_MATCH | CMP_STOP : 0 ;
}
static int allow_notify_user_presence ( struct sip_pvt * p )
{
return ( strstr ( p - > useragent , " Digium " ) ) ? 1 : 0 ;
}
/*! \brief Builds XML portion of NOTIFY messages for presence or dialog updates */
static void state_notify_build_xml ( int state , int full , const char * exten , const char * context , struct ast_str * * tmp , struct sip_pvt * p , int subscribed , const char * mfrom , const char * mto )
static void state_notify_build_xml ( struct state_notify_data * data , int full , const char * exten , const char * context , struct ast_str * * tmp , struct sip_pvt * p , int subscribed , const char * mfrom , const char * mto )
{
enum state { NOTIFY_OPEN , NOTIFY_INUSE , NOTIFY_CLOSED } local_state = NOTIFY_OPEN ;
const char * statestring = " terminated " ;
@ -12884,7 +12954,7 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
const char * pidfnote = " Ready " ;
char hint [ AST_MAX_EXTENSION ] ;
switch ( state) {
switch ( data- > state) {
case ( AST_EXTENSION_RINGING | AST_EXTENSION_INUSE ) :
statestring = ( sip_cfg . notifyringing ) ? " early " : " confirmed " ;
local_state = NOTIFY_INUSE ;
@ -12929,9 +12999,16 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
/* Check which device/devices we are watching and if they are registered */
if ( ast_get_hint ( hint , sizeof ( hint ) , NULL , 0 , NULL , context , exten ) ) {
char * hint2 = hint , * individual_hint = NULL ;
char * hint2 ;
char * individual_hint = NULL ;
int hint_count = 0 , unavailable_count = 0 ;
/* strip off any possible PRESENCE providers from hint */
if ( ( hint2 = strrchr ( hint , ' , ' ) ) ) {
* hint2 = ' \0 ' ;
}
hint2 = hint ;
while ( ( individual_hint = strsep ( & hint2 , " & " ) ) ) {
hint_count + + ;
@ -12979,12 +13056,24 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
ast_str_append ( tmp , 0 , " <status><basic>open</basic></status> \n " ) ;
else
ast_str_append ( tmp , 0 , " <status><basic>%s</basic></status> \n " , ( local_state ! = NOTIFY_CLOSED ) ? " open " : " closed " ) ;
if ( allow_notify_user_presence ( p ) & & ( data - > presence_state > 0 ) ) {
ast_str_append ( tmp , 0 , " </tuple> \n " ) ;
ast_str_append ( tmp , 0 , " <tuple id= \" digium-presence \" > \n " ) ;
ast_str_append ( tmp , 0 , " <status> \n " ) ;
ast_str_append ( tmp , 0 , " <digium_presence type= \" %s \" subtype= \" %s \" >%s</digium_presence> \n " ,
ast_presence_state2str ( data - > presence_state ) ,
S_OR ( data - > presence_subtype , " " ) ,
S_OR ( data - > presence_message , " " ) ) ;
ast_str_append ( tmp , 0 , " </status> \n " ) ;
}
ast_str_append ( tmp , 0 , " </tuple> \n </presence> \n " ) ;
break ;
case DIALOG_INFO_XML : /* SNOM subscribes in this format */
ast_str_append ( tmp , 0 , " <?xml version= \" 1.0 \" ?> \n " ) ;
ast_str_append ( tmp , 0 , " <dialog-info xmlns= \" urn:ietf:params:xml:ns:dialog-info \" version= \" %u \" state= \" %s \" entity= \" %s \" > \n " , p - > dialogver , full ? " full " : " partial " , mto ) ;
if ( ( state & AST_EXTENSION_RINGING ) & & sip_cfg . notifyringing ) {
if ( ( data- > state & AST_EXTENSION_RINGING ) & & sip_cfg . notifyringing ) {
const char * local_display = exten ;
char * local_target = ast_strdupa ( mto ) ;
const char * remote_display = exten ;
@ -13053,7 +13142,7 @@ static void state_notify_build_xml(int state, int full, const char *exten, const
ast_str_append ( tmp , 0 , " <dialog id= \" %s \" > \n " , exten ) ;
}
ast_str_append ( tmp , 0 , " <state>%s</state> \n " , statestring ) ;
if ( state = = AST_EXTENSION_ONHOLD ) {
if ( data- > state = = AST_EXTENSION_ONHOLD ) {
ast_str_append ( tmp , 0 , " <local> \n <target uri= \" %s \" > \n "
" <param pname= \" +sip.rendering \" pvalue= \" no \" /> \n "
" </target> \n </local> \n " , mto ) ;
@ -13097,7 +13186,7 @@ static int transmit_cc_notify(struct ast_cc_agent *agent, struct sip_pvt *subscr
}
/*! \brief Used in the SUBSCRIBE notification subsystem (RFC3265) */
static int transmit_state_notify ( struct sip_pvt * p , int state , int full , int timeout )
static int transmit_state_notify ( struct sip_pvt * p , struct state_notify_data * data , int full , int timeout )
{
struct ast_str * tmp = ast_str_alloca ( 4000 ) ;
char from [ 256 ] , to [ 256 ] ;
@ -13129,7 +13218,7 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim
reqprep ( & req , p , SIP_NOTIFY , 0 , 1 ) ;
switch ( state ) {
switch ( data - > state ) {
case AST_EXTENSION_DEACTIVATED :
if ( timeout )
add_header ( & req , " Subscription-State " , " terminated;reason=timeout " ) ;
@ -13152,19 +13241,19 @@ static int transmit_state_notify(struct sip_pvt *p, int state, int full, int tim
case XPIDF_XML :
case CPIM_PIDF_XML :
add_header ( & req , " Event " , subscriptiontype - > event ) ;
state_notify_build_xml ( state , full , p - > exten , p - > context , & tmp , p , p - > subscribed , mfrom , mto ) ;
state_notify_build_xml ( data , full , p - > exten , p - > context , & tmp , p , p - > subscribed , mfrom , mto ) ;
add_header ( & req , " Content-Type " , subscriptiontype - > mediatype ) ;
p - > dialogver + + ;
break ;
case PIDF_XML : /* Eyebeam supports this format */
add_header ( & req , " Event " , subscriptiontype - > event ) ;
state_notify_build_xml ( state , full , p - > exten , p - > context , & tmp , p , p - > subscribed , mfrom , mto ) ;
state_notify_build_xml ( data , full , p - > exten , p - > context , & tmp , p , p - > subscribed , mfrom , mto ) ;
add_header ( & req , " Content-Type " , subscriptiontype - > mediatype ) ;
p - > dialogver + + ;
break ;
case DIALOG_INFO_XML : /* SNOM subscribes in this format */
add_header ( & req , " Event " , subscriptiontype - > event ) ;
state_notify_build_xml ( state , full , p - > exten , p - > context , & tmp , p , p - > subscribed , mfrom , mto ) ;
state_notify_build_xml ( data , full , p - > exten , p - > context , & tmp , p , p - > subscribed , mfrom , mto ) ;
add_header ( & req , " Content-Type " , subscriptiontype - > mediatype ) ;
p - > dialogver + + ;
break ;
@ -14946,37 +15035,35 @@ static void cb_extensionstate_destroy(int id, void *data)
dialog_unref ( p , " the extensionstate containing this dialog ptr was destroyed " ) ;
}
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
\ note If you add an " hint " priority to the extension in the dial plan ,
you will get notifications on device state changes */
static int cb_extensionstate ( const char * context , const char * exten , enum ast_extension_states state , void * data )
static int extensionstate_update ( char * context , char * exten , struct state_notify_data * data , struct sip_pvt * p )
{
struct sip_pvt * p = data ;
sip_pvt_lock ( p ) ;
switch ( state ) {
switch ( data - > state ) {
case AST_EXTENSION_DEACTIVATED : /* Retry after a while */
case AST_EXTENSION_REMOVED : /* Extension is gone */
sip_scheddestroy ( p , DEFAULT_TRANS_TIMEOUT ) ; /* Delete subscription in 32 secs */
ast_verb ( 2 , " Extension state: Watcher for hint %s %s. Notify User %s \n " , exten , state = = AST_EXTENSION_DEACTIVATED ? " deactivated " : " removed " , p - > username ) ;
ast_verb ( 2 , " Extension state: Watcher for hint %s %s. Notify User %s \n " , exten , data- > state = = AST_EXTENSION_DEACTIVATED ? " deactivated " : " removed " , p - > username ) ;
p - > subscribed = NONE ;
append_history ( p , " Subscribestatus " , " %s " , state = = AST_EXTENSION_REMOVED ? " HintRemoved " : " Deactivated " ) ;
append_history ( p , " Subscribestatus " , " %s " , data- > state = = AST_EXTENSION_REMOVED ? " HintRemoved " : " Deactivated " ) ;
break ;
default : /* Tell user */
p - > laststate = state ;
p - > laststate = data - > state ;
p - > last_presence_state = data - > presence_state ;
ast_string_field_set ( p , last_presence_subtype , S_OR ( data - > presence_subtype , " " ) ) ;
ast_string_field_set ( p , last_presence_message , S_OR ( data - > presence_message , " " ) ) ;
break ;
}
if ( p - > subscribed ! = NONE ) { /* Only send state NOTIFY if we know the format */
if ( ! p - > pendinginvite ) {
transmit_state_notify ( p , state , 1 , FALSE ) ;
transmit_state_notify ( p , data , 1 , FALSE ) ;
} else {
/* We already have a NOTIFY sent that is not answered. Queue the state up.
if many state changes happen meanwhile , we will only send a notification of the last one */
ast_set_flag ( & p - > flags [ 1 ] , SIP_PAGE2_STATECHANGEQUEUE ) ;
}
}
ast_verb ( 2 , " Extension Changed %s[%s] new state %s for Notify User %s %s \n " , exten , context , ast_extension_state2str ( state) , p - > username ,
ast_verb ( 2 , " Extension Changed %s[%s] new state %s for Notify User %s %s \n " , exten , context , ast_extension_state2str ( data- > state) , p - > username ,
ast_test_flag ( & p - > flags [ 1 ] , SIP_PAGE2_STATECHANGEQUEUE ) ? " (queued) " : " " ) ;
sip_pvt_unlock ( p ) ;
@ -14984,6 +15071,27 @@ static int cb_extensionstate(const char *context, const char *exten, enum ast_ex
return 0 ;
}
/*! \brief Callback for the devicestate notification (SUBSCRIBE) support subsystem
\ note If you add an " hint " priority to the extension in the dial plan ,
you will get notifications on device state changes */
static int cb_extensionstate ( char * context , char * exten , struct ast_state_cb_info * info , void * data )
{
struct sip_pvt * p = data ;
struct state_notify_data notify_data = {
. state = info - > exten_state ,
. presence_state = info - > presence_state ,
. presence_subtype = info - > presence_subtype ,
. presence_message = info - > presence_message ,
} ;
if ( ( info - > reason = = AST_HINT_UPDATE_PRESENCE ) & & ! ( allow_notify_user_presence ( p ) ) ) {
/* ignore a presence triggered update if we know the useragent doesn't care */
return 0 ;
}
return extensionstate_update ( context , exten , & notify_data , p ) ;
}
/*! \brief Send a fake 401 Unauthorized response when the administrator
wants to hide the names of local devices from fishers
*/
@ -21022,9 +21130,15 @@ static void handle_response_notify(struct sip_pvt *p, int resp, const char *rest
pvt_set_needdestroy ( p , " received 200 response " ) ;
}
if ( ast_test_flag ( & p - > flags [ 1 ] , SIP_PAGE2_STATECHANGEQUEUE ) ) {
struct state_notify_data data = {
. state = p - > laststate ,
. presence_state = p - > last_presence_state ,
. presence_subtype = p - > last_presence_subtype ,
. presence_message = p - > last_presence_message ,
} ;
/* Ready to send the next state we have on queue */
ast_clear_flag ( & p - > flags [ 1 ] , SIP_PAGE2_STATECHANGEQUEUE ) ;
cb_extensionstate ( ( char * ) p - > context , ( char * ) p - > exten , p - > laststate , ( void * ) p ) ;
extensionstate_upd ate( ( char * ) p - > context , ( char * ) p - > exten , & data , ( void * ) p ) ;
}
}
break ;
@ -25714,8 +25828,10 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
sip_unref_peer ( peer , " release a peer ref now that MWI is sent " ) ;
}
} else if ( p - > subscribed ! = CALL_COMPLETION ) {
if ( ( firststate = ast_extension_state ( NULL , p - > context , p - > exten ) ) < 0 ) {
struct state_notify_data data = { 0 , } ;
char * subtype = NULL ;
char * message = NULL ;
if ( ( data . state = ast_extension_state ( NULL , p - > context , p - > exten ) ) < 0 ) {
ast_log ( LOG_NOTICE , " Got SUBSCRIBE for extension %s@%s from %s, but there is no hint for that extension. \n " , p - > exten , p - > context , ast_sockaddr_stringify ( & p - > sa ) ) ;
transmit_response ( p , " 404 Not found " , req ) ;
@ -25725,14 +25841,21 @@ static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req,
}
return 0 ;
}
if ( allow_notify_user_presence ( p ) ) {
data . presence_state = ast_hint_presence_state ( NULL , p - > context , p - > exten , & subtype , & message ) ;
data . presence_subtype = subtype ;
data . presence_message = message ;
}
ast_set_flag ( & p - > flags [ 1 ] , SIP_PAGE2_DIALOG_ESTABLISHED ) ;
transmit_response ( p , " 200 OK " , req ) ;
transmit_state_notify ( p , firststate , 1 , FALSE ) ; /* Send first notification */
append_history ( p , " Subscribestatus " , " %s " , ast_extension_state2str ( firststate ) ) ;
transmit_state_notify ( p , & data , 1 , FALSE ) ; /* Send first notification */
append_history ( p , " Subscribestatus " , " %s " , ast_extension_state2str ( data. state) ) ;
/* hide the 'complete' exten/context in the refer_to field for later display */
ast_string_field_build ( p , subscribeuri , " %s@%s " , p - > exten , p - > context ) ;
/* Deleted the slow iteration of all sip dialogs to find old subscribes from this peer for exten@context */
ast_free ( subtype ) ;
ast_free ( message ) ;
}
if ( ! p - > expiry ) {
pvt_set_needdestroy ( p , " forcing expiration " ) ;