@ -25121,6 +25121,39 @@ static int sip_t38_abort(const void *data)
return 0 ;
}
/*! \brief Checks state of the p->refer->refer_call state: can it be picked up?
*
* It will terminate the call if it cannot be picked up ; e . g . because it
* was gone , or because it wasn ' t in a ringing state .
*
* \ return 1 if terminated , 0 if ok
*/
static int terminate_on_invalid_replaces_state ( struct sip_pvt * p , struct sip_request * req , const char * replace_id )
{
if ( p - > refer - > refer_call = = p ) {
ast_log ( LOG_NOTICE , " INVITE with replaces into its own call id (%s == %s)! \n " , replace_id , p - > callid ) ;
transmit_response_reliable ( p , " 400 Bad request " , req ) ; /* The best way to not not accept the transfer */
} else if ( ! p - > refer - > refer_call - > owner ) {
/* Oops, someting wrong anyway, no owner, no call */
ast_log ( LOG_NOTICE , " Supervised transfer attempted to replace non-existing call id (%s)! \n " , replace_id ) ;
/* Check for better return code */
transmit_response_reliable ( p , " 481 Call Leg Does Not Exist (Replace) " , req ) ;
} else if ( ast_channel_state ( p - > refer - > refer_call - > owner ) ! = AST_STATE_RINGING & &
ast_channel_state ( p - > refer - > refer_call - > owner ) ! = AST_STATE_RING & &
ast_channel_state ( p - > refer - > refer_call - > owner ) ! = AST_STATE_UP ) {
ast_log ( LOG_NOTICE , " Supervised transfer attempted to replace non-ringing or active call id (%s)! \n " , replace_id ) ;
transmit_response_reliable ( p , " 603 Declined (Replaces) " , req ) ;
} else {
/* Ok */
return 0 ;
}
/* Terminated */
return 1 ;
}
/*!
* \ brief bare - bones support for SIP UPDATE
*
@ -25322,6 +25355,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
int gotdest ;
const char * p_replaces ;
char * replace_id = NULL ;
int magic_call_id = 0 ;
int refer_locked = 0 ;
const char * required ;
unsigned int required_profile = 0 ;
@ -25553,40 +25587,27 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
ast_channel_unlock ( subscription - > owner ) ;
}
subscription = dialog_unref ( subscription , " unref dialog subscription " ) ;
magic_call_id = ! ast_strlen_zero ( pickup . exten ) ;
}
}
/* This locks both refer_call pvt and refer_call pvt's owner!!!*/
if ( ! error & & ast_strlen_zero ( pickup . exten ) & & ( p - > refer - > refer_call = get_sip_pvt_byid_locked ( replace_id , totag , fromtag ) ) = = NULL ) {
ast_log ( LOG_NOTICE , " Supervised transfer attempted to replace non-existent call id (%s)! \n " , replace_id ) ;
transmit_response_reliable ( p , " 481 Call Leg Does Not Exist (Replaces) " , req ) ;
error = 1 ;
} else {
refer_locked = 1 ;
}
/* The matched call is the call from the transferer to Asterisk .
We want to bridge the bridged part of the call to the
incoming invite , thus taking over the refered call */
if ( p - > refer - > refer_call = = p ) {
ast_log ( LOG_NOTICE , " INVITE with replaces into it's own call id (%s == %s)! \n " , replace_id , p - > callid ) ;
transmit_response_reliable ( p , " 400 Bad request " , req ) ; /* The best way to not not accept the transfer */
error = 1 ;
}
if ( ! error & & ast_strlen_zero ( pickup . exten ) & & ! p - > refer - > refer_call - > owner ) {
/* Oops, someting wrong anyway, no owner, no call */
ast_log ( LOG_NOTICE , " Supervised transfer attempted to replace non-existing call id (%s)! \n " , replace_id ) ;
/* Check for better return code */
transmit_response_reliable ( p , " 481 Call Leg Does Not Exist (Replace) " , req ) ;
error = 1 ;
}
if ( ! error & & ast_strlen_zero ( pickup . exten ) & & ast_channel_state ( p - > refer - > refer_call - > owner ) ! = AST_STATE_RINGING & & ast_channel_state ( p - > refer - > refer_call - > owner ) ! = AST_STATE_RING & & ast_channel_state ( p - > refer - > refer_call - > owner ) ! = AST_STATE_UP ) {
ast_log ( LOG_NOTICE , " Supervised transfer attempted to replace non-ringing or active call id (%s)! \n " , replace_id ) ;
transmit_response_reliable ( p , " 603 Declined (Replaces) " , req ) ;
error = 1 ;
if ( ! error ) {
if ( magic_call_id ) {
;
/* This locks both refer_call pvt and refer_call pvt's owner!!!*/
} else if ( ( p - > refer - > refer_call = get_sip_pvt_byid_locked ( replace_id , totag , fromtag ) ) ) {
/* The matched call is the call from the transferer to Asterisk .
We want to bridge the bridged part of the call to the
incoming invite , thus taking over the refered call */
refer_locked = 1 ;
if ( terminate_on_invalid_replaces_state ( p , req , replace_id ) ) {
error = 1 ;
}
} else {
ast_log ( LOG_NOTICE , " Supervised transfer attempted to replace non-existent call id (%s)! \n " , replace_id ) ;
transmit_response_reliable ( p , " 481 Call Leg Does Not Exist (Replaces) " , req ) ;
error = 1 ;
}
}
if ( error ) { /* Give up this dialog */
@ -25828,11 +25849,52 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
goto request_invite_cleanup ;
}
/* We cannot call sip_new with any channel locks
* held . Unlock the referred channel if locked . */
if ( refer_locked ) {
sip_pvt_unlock ( p - > refer - > refer_call ) ;
if ( p - > refer - > refer_call - > owner ) {
ast_channel_unlock ( p - > refer - > refer_call - > owner ) ;
}
}
/* First invitation - create the channel. Allocation
* failures are handled below . */
c = sip_new ( p , AST_STATE_DOWN , S_OR ( p - > peername , NULL ) , NULL , p - > logger_callid ) ;
/* Reacquire the lock on the referred call. */
if ( refer_locked ) {
/* Reuse deadlock avoid pattern found in
* get_sip_pvt_byid_locked . */
sip_pvt_lock ( p - > refer - > refer_call ) ;
while ( p - > refer - > refer_call - > owner & & ast_channel_trylock ( p - > refer - > refer_call - > owner ) ) {
sip_pvt_unlock ( p - > refer - > refer_call ) ;
usleep ( 1 ) ;
sip_pvt_lock ( p - > refer - > refer_call ) ;
}
/* And now, check once more that the call is still
* still available . Yuck . Bail out if it isn ' t . */
if ( terminate_on_invalid_replaces_state ( p , req , replace_id ) ) {
/* Free the referred call: it's not ours anymore. */
sip_pvt_unlock ( p - > refer - > refer_call ) ;
if ( p - > refer - > refer_call - > owner ) {
ast_channel_unlock ( p - > refer - > refer_call - > owner ) ;
}
p - > refer - > refer_call = dialog_unref ( p - > refer - > refer_call , " unref dialog p->refer->refer_call " ) ;
refer_locked = 0 ;
/* Kill the channel we just created. */
sip_pvt_unlock ( p ) ;
ast_hangup ( c ) ;
sip_pvt_lock ( p ) ; /* pvt is expected to remain locked on return, so re-lock it */
/* Mark the call as failed. */
sip_scheddestroy ( p , DEFAULT_TRANS_TIMEOUT ) ;
p - > invitestate = INV_COMPLETED ;
res = INV_REQ_ERROR ;
goto request_invite_cleanup ;
}
}
if ( cc_recall_core_id ! = - 1 ) {
ast_setup_cc_recall_datastore ( c , cc_recall_core_id ) ;
ast_cc_agent_set_interfaces_chanvar ( c ) ;
@ -25891,7 +25953,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
p - > lastinvite = seqno ;
if ( c & & replace_id ) { /* Attended transfer or call pickup - we're the target */
if ( ! ast_strlen_zero ( pickup . exten ) ) {
if ( magic_call_id ) {
append_history ( p , " Xfer " , " INVITE/Replace received " ) ;
/* Let the caller know we're giving it a shot */
@ -26062,7 +26124,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, str
request_invite_cleanup :
if ( refer_locked & & p - > refer & & p - > refer - > refer_call ) {
if ( refer_locked ) {
sip_pvt_unlock ( p - > refer - > refer_call ) ;
if ( p - > refer - > refer_call - > owner ) {
ast_channel_unlock ( p - > refer - > refer_call - > owner ) ;